blob: 9f3b0c26ac2d06e2de1f3fa7e3017a9dcfe509e4 [file] [log] [blame]
Brian Silvermancc09f182022-03-09 15:40:20 -08001# Copyright 2018 The Bazel Authors. All rights reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15"""Functionality for constructing actions that invoke the Rust compiler"""
16
Adam Snaider1c095c92023-07-08 02:09:58 -040017load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo")
Brian Silvermancc09f182022-03-09 15:40:20 -080018load(
19 "@bazel_tools//tools/build_defs/cc:action_names.bzl",
Adam Snaider1c095c92023-07-08 02:09:58 -040020 "CPP_LINK_DYNAMIC_LIBRARY_ACTION_NAME",
Brian Silvermancc09f182022-03-09 15:40:20 -080021 "CPP_LINK_EXECUTABLE_ACTION_NAME",
Adam Snaider1c095c92023-07-08 02:09:58 -040022 "CPP_LINK_NODEPS_DYNAMIC_LIBRARY_ACTION_NAME",
23 "CPP_LINK_STATIC_LIBRARY_ACTION_NAME",
Brian Silvermancc09f182022-03-09 15:40:20 -080024)
25load("//rust/private:common.bzl", "rust_common")
26load("//rust/private:providers.bzl", _BuildInfo = "BuildInfo")
27load("//rust/private:stamp.bzl", "is_stamping_enabled")
28load(
29 "//rust/private:utils.bzl",
30 "abs",
31 "expand_dict_value_locations",
32 "expand_list_element_locations",
33 "find_cc_toolchain",
Brian Silverman5f6f2762022-08-13 19:30:05 -070034 "get_lib_name_default",
35 "get_lib_name_for_windows",
Brian Silvermancc09f182022-03-09 15:40:20 -080036 "get_preferred_artifact",
37 "is_exec_configuration",
38 "make_static_lib_symlink",
39 "relativize",
40)
41
42BuildInfo = _BuildInfo
43
44AliasableDepInfo = provider(
45 doc = "A provider mapping an alias name to a Crate's information.",
46 fields = {
47 "dep": "CrateInfo",
48 "name": "str",
49 },
50)
51
52_error_format_values = ["human", "json", "short"]
53
54ErrorFormatInfo = provider(
55 doc = "Set the --error-format flag for all rustc invocations",
56 fields = {"error_format": "(string) [" + ", ".join(_error_format_values) + "]"},
57)
58
59ExtraRustcFlagsInfo = provider(
60 doc = "Pass each value as an additional flag to non-exec rustc invocations",
61 fields = {"extra_rustc_flags": "List[string] Extra flags to pass to rustc in non-exec configuration"},
62)
63
64ExtraExecRustcFlagsInfo = provider(
65 doc = "Pass each value as an additional flag to exec rustc invocations",
66 fields = {"extra_exec_rustc_flags": "List[string] Extra flags to pass to rustc in exec configuration"},
67)
68
Adam Snaider1c095c92023-07-08 02:09:58 -040069PerCrateRustcFlagsInfo = provider(
70 doc = "Pass each value as an additional flag to non-exec rustc invocations for crates matching the provided filter",
71 fields = {"per_crate_rustc_flags": "List[string] Extra flags to pass to rustc in non-exec configuration"},
72)
73
Brian Silverman5f6f2762022-08-13 19:30:05 -070074IsProcMacroDepInfo = provider(
75 doc = "Records if this is a transitive dependency of a proc-macro.",
76 fields = {"is_proc_macro_dep": "Boolean"},
77)
78
79def _is_proc_macro_dep_impl(ctx):
80 return IsProcMacroDepInfo(is_proc_macro_dep = ctx.build_setting_value)
81
82is_proc_macro_dep = rule(
83 doc = "Records if this is a transitive dependency of a proc-macro.",
84 implementation = _is_proc_macro_dep_impl,
85 build_setting = config.bool(flag = True),
86)
87
88IsProcMacroDepEnabledInfo = provider(
89 doc = "Enables the feature to record if a library is a transitive dependency of a proc-macro.",
90 fields = {"enabled": "Boolean"},
91)
92
93def _is_proc_macro_dep_enabled_impl(ctx):
94 return IsProcMacroDepEnabledInfo(enabled = ctx.build_setting_value)
95
96is_proc_macro_dep_enabled = rule(
97 doc = "Enables the feature to record if a library is a transitive dependency of a proc-macro.",
98 implementation = _is_proc_macro_dep_enabled_impl,
99 build_setting = config.bool(flag = True),
100)
101
Brian Silvermancc09f182022-03-09 15:40:20 -0800102def _get_rustc_env(attr, toolchain, crate_name):
103 """Gathers rustc environment variables
104
105 Args:
106 attr (struct): The current target's attributes
107 toolchain (rust_toolchain): The current target's rust toolchain context
108 crate_name (str): The name of the crate to be compiled
109
110 Returns:
111 dict: Rustc environment variables
112 """
113 version = attr.version if hasattr(attr, "version") else "0.0.0"
114 major, minor, patch = version.split(".", 2)
115 if "-" in patch:
116 patch, pre = patch.split("-", 1)
117 else:
118 pre = ""
Adam Snaider1c095c92023-07-08 02:09:58 -0400119
Brian Silverman5f6f2762022-08-13 19:30:05 -0700120 result = {
Adam Snaider1c095c92023-07-08 02:09:58 -0400121 "CARGO_CFG_TARGET_ARCH": "" if toolchain.target_arch == None else toolchain.target_arch,
122 "CARGO_CFG_TARGET_OS": "" if toolchain.target_os == None else toolchain.target_os,
Brian Silvermancc09f182022-03-09 15:40:20 -0800123 "CARGO_CRATE_NAME": crate_name,
124 "CARGO_PKG_AUTHORS": "",
125 "CARGO_PKG_DESCRIPTION": "",
126 "CARGO_PKG_HOMEPAGE": "",
127 "CARGO_PKG_NAME": attr.name,
128 "CARGO_PKG_VERSION": version,
129 "CARGO_PKG_VERSION_MAJOR": major,
130 "CARGO_PKG_VERSION_MINOR": minor,
131 "CARGO_PKG_VERSION_PATCH": patch,
132 "CARGO_PKG_VERSION_PRE": pre,
133 }
Brian Silverman5f6f2762022-08-13 19:30:05 -0700134 if hasattr(attr, "_is_proc_macro_dep_enabled") and attr._is_proc_macro_dep_enabled[IsProcMacroDepEnabledInfo].enabled:
135 is_proc_macro_dep = "0"
136 if hasattr(attr, "_is_proc_macro_dep") and attr._is_proc_macro_dep[IsProcMacroDepInfo].is_proc_macro_dep:
137 is_proc_macro_dep = "1"
138 result["BAZEL_RULES_RUST_IS_PROC_MACRO_DEP"] = is_proc_macro_dep
139 return result
Brian Silvermancc09f182022-03-09 15:40:20 -0800140
141def get_compilation_mode_opts(ctx, toolchain):
142 """Gathers rustc flags for the current compilation mode (opt/debug)
143
144 Args:
145 ctx (ctx): The current rule's context object
146 toolchain (rust_toolchain): The current rule's `rust_toolchain`
147
148 Returns:
149 struct: See `_rust_toolchain_impl` for more details
150 """
151 comp_mode = ctx.var["COMPILATION_MODE"]
152 if not comp_mode in toolchain.compilation_mode_opts:
153 fail("Unrecognized compilation mode {} for toolchain.".format(comp_mode))
154
155 return toolchain.compilation_mode_opts[comp_mode]
156
157def _are_linkstamps_supported(feature_configuration, has_grep_includes):
158 # Are linkstamps supported by the C++ toolchain?
159 return (cc_common.is_enabled(feature_configuration = feature_configuration, feature_name = "linkstamps") and
160 # Is Bazel recent enough to support Starlark linkstamps?
161 hasattr(cc_common, "register_linkstamp_compile_action") and
162 # The current rule doesn't define _grep_includes attribute; this
163 # attribute is required for compiling linkstamps.
164 has_grep_includes)
165
Adam Snaider1c095c92023-07-08 02:09:58 -0400166def _should_use_pic(cc_toolchain, feature_configuration, crate_type, compilation_mode):
Brian Silverman5f6f2762022-08-13 19:30:05 -0700167 """Whether or not [PIC][pic] should be enabled
168
169 [pic]: https://en.wikipedia.org/wiki/Position-independent_code
170
171 Args:
172 cc_toolchain (CcToolchainInfo): The current `cc_toolchain`.
173 feature_configuration (FeatureConfiguration): Feature configuration to be queried.
174 crate_type (str): A Rust target's crate type.
Adam Snaider1c095c92023-07-08 02:09:58 -0400175 compilation_mode: The compilation mode.
Brian Silverman5f6f2762022-08-13 19:30:05 -0700176
177 Returns:
178 bool: Whether or not [PIC][pic] should be enabled.
179 """
Adam Snaider1c095c92023-07-08 02:09:58 -0400180
181 # We use the same logic to select between `pic` and `nopic` outputs as the C++ rules:
182 # - For shared libraries - we use `pic`. This covers `dylib`, `cdylib` and `proc-macro` crate types.
183 # - In `fastbuild` and `dbg` mode we use `pic` by default.
184 # - In `opt` mode we use `nopic` outputs to build binaries.
185 if crate_type in ("cdylib", "dylib", "proc-macro"):
Brian Silvermancc09f182022-03-09 15:40:20 -0800186 return cc_toolchain.needs_pic_for_dynamic_libraries(feature_configuration = feature_configuration)
Adam Snaider1c095c92023-07-08 02:09:58 -0400187 elif compilation_mode in ("fastbuild", "dbg"):
188 return True
Brian Silvermancc09f182022-03-09 15:40:20 -0800189 return False
190
Brian Silverman5f6f2762022-08-13 19:30:05 -0700191def _is_proc_macro(crate_info):
192 return "proc-macro" in (crate_info.type, crate_info.wrapped_crate_type)
193
Brian Silvermancc09f182022-03-09 15:40:20 -0800194def collect_deps(
195 deps,
196 proc_macro_deps,
197 aliases,
198 are_linkstamps_supported = False):
199 """Walks through dependencies and collects the transitive dependencies.
200
201 Args:
202 deps (list): The deps from ctx.attr.deps.
203 proc_macro_deps (list): The proc_macro deps from ctx.attr.proc_macro_deps.
204 aliases (dict): A dict mapping aliased targets to their actual Crate information.
205 are_linkstamps_supported (bool): Whether the current rule and the toolchain support building linkstamps..
206
207 Returns:
208 tuple: Returns a tuple of:
209 DepInfo,
210 BuildInfo,
211 linkstamps (depset[CcLinkstamp]): A depset of CcLinkstamps that need to be compiled and linked into all linked binaries.
212
213 """
214 direct_crates = []
215 transitive_crates = []
216 transitive_noncrates = []
217 transitive_build_infos = []
218 transitive_link_search_paths = []
219 build_info = None
220 linkstamps = []
221 transitive_crate_outputs = []
Brian Silverman5f6f2762022-08-13 19:30:05 -0700222 transitive_metadata_outputs = []
Brian Silvermancc09f182022-03-09 15:40:20 -0800223
Adam Snaider1c095c92023-07-08 02:09:58 -0400224 crate_deps = []
Brian Silvermancc09f182022-03-09 15:40:20 -0800225 for dep in depset(transitive = [deps, proc_macro_deps]).to_list():
Adam Snaider1c095c92023-07-08 02:09:58 -0400226 crate_group = None
227
228 if type(dep) == "Target" and rust_common.crate_group_info in dep:
229 crate_group = dep[rust_common.crate_group_info]
230 elif type(dep) == "struct" and hasattr(dep, "crate_group_info") and dep.crate_group_info != None:
231 crate_group = dep.crate_group_info
232 else:
233 crate_deps.append(dep)
234
235 if crate_group:
236 for dep_variant_info in crate_group.dep_variant_infos.to_list():
237 crate_deps.append(struct(
238 crate_info = dep_variant_info.crate_info,
239 dep_info = dep_variant_info.dep_info,
240 ))
241
242 aliases = {k.label: v for k, v in aliases.items()}
243 for dep in crate_deps:
Brian Silvermancc09f182022-03-09 15:40:20 -0800244 (crate_info, dep_info) = _get_crate_and_dep_info(dep)
245 cc_info = _get_cc_info(dep)
246 dep_build_info = _get_build_info(dep)
247
248 if cc_info and are_linkstamps_supported:
249 linkstamps.append(cc_info.linking_context.linkstamps())
250
251 if crate_info:
252 # This dependency is a rust_library
253
254 # When crate_info.owner is set, we use it. When the dep type is Target we get the
255 # label from dep.label
256 owner = getattr(crate_info, "owner", dep.label if type(dep) == "Target" else None)
257
258 direct_crates.append(AliasableDepInfo(
259 name = aliases.get(owner, crate_info.name),
260 dep = crate_info,
261 ))
262
Brian Silverman5f6f2762022-08-13 19:30:05 -0700263 transitive_crates.append(
264 depset(
265 [crate_info],
266 transitive = [] if _is_proc_macro(crate_info) else [dep_info.transitive_crates],
267 ),
268 )
269
270 # If this dependency produces metadata, add it to the metadata outputs.
271 # If it doesn't (for example a custom library that exports crate_info),
272 # we depend on crate_info.output.
273 depend_on = crate_info.metadata
274 if not crate_info.metadata:
275 depend_on = crate_info.output
276
277 # If this dependency is a proc_macro, it still can be used for lib crates
278 # that produce metadata.
279 # In that case, we don't depend on its metadata dependencies.
280 transitive_metadata_outputs.append(
281 depset(
282 [depend_on],
283 transitive = [] if _is_proc_macro(crate_info) else [dep_info.transitive_metadata_outputs],
284 ),
285 )
286
Brian Silvermancc09f182022-03-09 15:40:20 -0800287 transitive_crate_outputs.append(
288 depset(
289 [crate_info.output],
Brian Silverman5f6f2762022-08-13 19:30:05 -0700290 transitive = [] if _is_proc_macro(crate_info) else [dep_info.transitive_crate_outputs],
Brian Silvermancc09f182022-03-09 15:40:20 -0800291 ),
292 )
Brian Silverman5f6f2762022-08-13 19:30:05 -0700293
294 if "proc-macro" not in [crate_info.type, crate_info.wrapped_crate_type]:
295 transitive_noncrates.append(dep_info.transitive_noncrates)
296 transitive_link_search_paths.append(dep_info.link_search_path_files)
297
Brian Silvermancc09f182022-03-09 15:40:20 -0800298 transitive_build_infos.append(dep_info.transitive_build_infos)
Brian Silvermancc09f182022-03-09 15:40:20 -0800299
300 elif cc_info:
301 # This dependency is a cc_library
302 transitive_noncrates.append(cc_info.linking_context.linker_inputs)
303 elif dep_build_info:
304 if build_info:
305 fail("Several deps are providing build information, " +
306 "only one is allowed in the dependencies")
307 build_info = dep_build_info
308 transitive_build_infos.append(depset([build_info]))
309 transitive_link_search_paths.append(depset([build_info.link_search_paths]))
310 else:
311 fail("rust targets can only depend on rust_library, rust_*_library or cc_library " +
312 "targets.")
313
314 transitive_crates_depset = depset(transitive = transitive_crates)
315
316 return (
317 rust_common.dep_info(
318 direct_crates = depset(direct_crates),
319 transitive_crates = transitive_crates_depset,
320 transitive_noncrates = depset(
321 transitive = transitive_noncrates,
322 order = "topological", # dylib link flag ordering matters.
323 ),
324 transitive_crate_outputs = depset(transitive = transitive_crate_outputs),
Brian Silverman5f6f2762022-08-13 19:30:05 -0700325 transitive_metadata_outputs = depset(transitive = transitive_metadata_outputs),
Brian Silvermancc09f182022-03-09 15:40:20 -0800326 transitive_build_infos = depset(transitive = transitive_build_infos),
327 link_search_path_files = depset(transitive = transitive_link_search_paths),
328 dep_env = build_info.dep_env if build_info else None,
329 ),
330 build_info,
331 depset(transitive = linkstamps),
332 )
333
334def _collect_libs_from_linker_inputs(linker_inputs, use_pic):
335 # TODO: We could let the user choose how to link, instead of always preferring to link static libraries.
336 return [
337 get_preferred_artifact(lib, use_pic)
338 for li in linker_inputs
339 for lib in li.libraries
340 ]
341
342def _get_crate_and_dep_info(dep):
343 if type(dep) == "Target" and rust_common.crate_info in dep:
344 return (dep[rust_common.crate_info], dep[rust_common.dep_info])
345 elif type(dep) == "struct" and hasattr(dep, "crate_info"):
346 return (dep.crate_info, dep.dep_info)
347 return (None, None)
348
349def _get_cc_info(dep):
350 if type(dep) == "Target" and CcInfo in dep:
351 return dep[CcInfo]
352 elif type(dep) == "struct" and hasattr(dep, "cc_info"):
353 return dep.cc_info
354 return None
355
356def _get_build_info(dep):
357 if type(dep) == "Target" and BuildInfo in dep:
358 return dep[BuildInfo]
359 elif type(dep) == "struct" and hasattr(dep, "build_info"):
360 return dep.build_info
361 return None
362
363def get_cc_user_link_flags(ctx):
364 """Get the current target's linkopt flags
365
366 Args:
367 ctx (ctx): The current rule's context object
368
369 Returns:
370 depset: The flags passed to Bazel by --linkopt option.
371 """
372 return ctx.fragments.cpp.linkopts
373
Adam Snaider1c095c92023-07-08 02:09:58 -0400374def get_linker_and_args(ctx, attr, crate_type, cc_toolchain, feature_configuration, rpaths, rustdoc = False):
Brian Silvermancc09f182022-03-09 15:40:20 -0800375 """Gathers cc_common linker information
376
377 Args:
378 ctx (ctx): The current target's context object
379 attr (struct): Attributes to use in gathering linker args
Adam Snaider1c095c92023-07-08 02:09:58 -0400380 crate_type (str): The target crate's type (i.e. "bin", "proc-macro", etc.).
Brian Silvermancc09f182022-03-09 15:40:20 -0800381 cc_toolchain (CcToolchain): cc_toolchain for which we are creating build variables.
382 feature_configuration (FeatureConfiguration): Feature configuration to be queried.
383 rpaths (depset): Depset of directories where loader will look for libraries at runtime.
Adam Snaider1c095c92023-07-08 02:09:58 -0400384 rustdoc (bool, optional): Whether to add "bin" link flags to the command regardless of `crate_type`.
Brian Silvermancc09f182022-03-09 15:40:20 -0800385
386
387 Returns:
388 tuple: A tuple of the following items:
389 - (str): The tool path for given action.
390 - (sequence): A flattened command line flags for given action.
391 - (dict): Environment variables to be set for given action.
392 """
393 user_link_flags = get_cc_user_link_flags(ctx)
394
Adam Snaider1c095c92023-07-08 02:09:58 -0400395 if crate_type in ("bin") or rustdoc:
396 is_linking_dynamic_library = False
397 action_name = CPP_LINK_EXECUTABLE_ACTION_NAME
398 elif crate_type in ("dylib"):
399 is_linking_dynamic_library = True
400 action_name = CPP_LINK_NODEPS_DYNAMIC_LIBRARY_ACTION_NAME
401 elif crate_type in ("staticlib"):
402 is_linking_dynamic_library = False
403 action_name = CPP_LINK_STATIC_LIBRARY_ACTION_NAME
404 elif crate_type in ("cdylib", "proc-macro"):
405 # Proc macros get compiled as shared libraries to be loaded by the compiler.
406 is_linking_dynamic_library = True
407 action_name = CPP_LINK_DYNAMIC_LIBRARY_ACTION_NAME
408 elif crate_type in ("lib", "rlib"):
409 fail("Invalid `crate_type` for linking action: {}".format(crate_type))
410 else:
411 fail("Unknown `crate_type`: {}".format(crate_type))
412
Brian Silvermancc09f182022-03-09 15:40:20 -0800413 # Add linkopt's from dependencies. This includes linkopts from transitive
414 # dependencies since they get merged up.
415 for dep in getattr(attr, "deps", []):
416 if CcInfo in dep and dep[CcInfo].linking_context:
417 for linker_input in dep[CcInfo].linking_context.linker_inputs.to_list():
418 for flag in linker_input.user_link_flags:
419 user_link_flags.append(flag)
420 link_variables = cc_common.create_link_variables(
421 feature_configuration = feature_configuration,
422 cc_toolchain = cc_toolchain,
Adam Snaider1c095c92023-07-08 02:09:58 -0400423 is_linking_dynamic_library = is_linking_dynamic_library,
Brian Silvermancc09f182022-03-09 15:40:20 -0800424 runtime_library_search_directories = rpaths,
425 user_link_flags = user_link_flags,
426 )
427 link_args = cc_common.get_memory_inefficient_command_line(
428 feature_configuration = feature_configuration,
Adam Snaider1c095c92023-07-08 02:09:58 -0400429 action_name = action_name,
Brian Silvermancc09f182022-03-09 15:40:20 -0800430 variables = link_variables,
431 )
432 link_env = cc_common.get_environment_variables(
433 feature_configuration = feature_configuration,
Adam Snaider1c095c92023-07-08 02:09:58 -0400434 action_name = action_name,
Brian Silvermancc09f182022-03-09 15:40:20 -0800435 variables = link_variables,
436 )
437 ld = cc_common.get_tool_for_action(
438 feature_configuration = feature_configuration,
Adam Snaider1c095c92023-07-08 02:09:58 -0400439 action_name = action_name,
Brian Silvermancc09f182022-03-09 15:40:20 -0800440 )
441
442 return ld, link_args, link_env
443
444def _process_build_scripts(
445 build_info,
446 dep_info,
447 compile_inputs):
448 """Gathers the outputs from a target's `cargo_build_script` action.
449
450 Args:
451 build_info (BuildInfo): The target Build's dependency info.
452 dep_info (DepInfo): The Depinfo provider form the target Crate's set of inputs.
453 compile_inputs (depset): A set of all files that will participate in the build.
454
455 Returns:
456 tuple: A tuple: A tuple of the following items:
457 - (depset[File]): A list of all build info `OUT_DIR` File objects
458 - (str): The `OUT_DIR` of the current build info
459 - (File): An optional path to a generated environment file from a `cargo_build_script` target
460 - (depset[File]): All direct and transitive build flags from the current build info.
461 """
462 extra_inputs, out_dir, build_env_file, build_flags_files = _create_extra_input_args(build_info, dep_info)
463 compile_inputs = depset(transitive = [extra_inputs, compile_inputs])
464 return compile_inputs, out_dir, build_env_file, build_flags_files
465
466def _symlink_for_ambiguous_lib(actions, toolchain, crate_info, lib):
467 """Constructs a disambiguating symlink for a library dependency.
468
469 Args:
470 actions (Actions): The rule's context actions object.
471 toolchain: The Rust toolchain object.
472 crate_info (CrateInfo): The target crate's info.
473 lib (File): The library to symlink to.
474
475 Returns:
476 (File): The disambiguating symlink for the library.
477 """
478 # FIXME: Once the relative order part of the native-link-modifiers rustc
479 # feature is stable, we should be able to eliminate the need to construct
480 # symlinks by passing the full paths to the libraries.
481 # https://github.com/rust-lang/rust/issues/81490.
482
483 # Take the absolute value of hash() since it could be negative.
484 path_hash = abs(hash(lib.path))
Adam Snaider1c095c92023-07-08 02:09:58 -0400485 lib_name = get_lib_name_for_windows(lib) if toolchain.target_os.startswith("windows") else get_lib_name_default(lib)
Brian Silvermancc09f182022-03-09 15:40:20 -0800486
Adam Snaider1c095c92023-07-08 02:09:58 -0400487 if toolchain.target_os.startswith("windows"):
Brian Silvermancc09f182022-03-09 15:40:20 -0800488 prefix = ""
489 extension = ".lib"
Adam Snaider1c095c92023-07-08 02:09:58 -0400490 elif lib_name.endswith(".pic"):
491 # Strip the .pic suffix
492 lib_name = lib_name[:-4]
493 prefix = "lib"
494 extension = ".pic.a"
495 else:
496 prefix = "lib"
497 extension = ".a"
Brian Silvermancc09f182022-03-09 15:40:20 -0800498
499 # Ensure the symlink follows the lib<name>.a pattern on Unix-like platforms
500 # or <name>.lib on Windows.
501 # Add a hash of the original library path to disambiguate libraries with the same basename.
502 symlink_name = "{}{}-{}{}".format(prefix, lib_name, path_hash, extension)
503
504 # Add the symlink to a target crate-specific _ambiguous_libs/ subfolder,
505 # to avoid possible collisions with sibling crates that may depend on the
506 # same ambiguous libraries.
507 symlink = actions.declare_file("_ambiguous_libs/" + crate_info.output.basename + "/" + symlink_name)
508 actions.symlink(
509 output = symlink,
510 target_file = lib,
511 progress_message = "Creating symlink to ambiguous lib: {}".format(lib.path),
512 )
513 return symlink
514
515def _disambiguate_libs(actions, toolchain, crate_info, dep_info, use_pic):
516 """Constructs disambiguating symlinks for ambiguous library dependencies.
517
518 The symlinks are all created in a _ambiguous_libs/ subfolder specific to
519 the target crate to avoid possible collisions with sibling crates that may
520 depend on the same ambiguous libraries.
521
522 Args:
523 actions (Actions): The rule's context actions object.
524 toolchain: The Rust toolchain object.
525 crate_info (CrateInfo): The target crate's info.
526 dep_info: (DepInfo): The target crate's dependency info.
527 use_pic: (boolean): Whether the build should use PIC.
528
529 Returns:
530 dict[String, File]: A mapping from ambiguous library paths to their
531 disambiguating symlink.
532 """
533 # FIXME: Once the relative order part of the native-link-modifiers rustc
534 # feature is stable, we should be able to eliminate the need to construct
535 # symlinks by passing the full paths to the libraries.
536 # https://github.com/rust-lang/rust/issues/81490.
537
538 # A dictionary from file paths of ambiguous libraries to the corresponding
539 # symlink.
540 ambiguous_libs = {}
541
542 # A dictionary maintaining a mapping from preferred library name to the
543 # last visited artifact with that name.
544 visited_libs = {}
545 for link_input in dep_info.transitive_noncrates.to_list():
546 for lib in link_input.libraries:
547 # FIXME: Dynamic libs are not disambiguated right now, there are
548 # cases where those have a non-standard name with version (e.g.,
549 # //test/unit/versioned_libs). We hope that the link modifiers
550 # stabilization will come before we need to make this work.
551 if _is_dylib(lib):
552 continue
553 artifact = get_preferred_artifact(lib, use_pic)
Adam Snaider1c095c92023-07-08 02:09:58 -0400554 name = get_lib_name_for_windows(artifact) if toolchain.target_os.startswith("windows") else get_lib_name_default(artifact)
Brian Silvermancc09f182022-03-09 15:40:20 -0800555
556 # On Linux-like platforms, normally library base names start with
557 # `lib`, following the pattern `lib[name].(a|lo)` and we pass
558 # -lstatic=name.
559 # On Windows, the base name looks like `name.lib` and we pass
560 # -lstatic=name.
561 # FIXME: Under the native-link-modifiers unstable rustc feature,
562 # we could use -lstatic:+verbatim instead.
563 needs_symlink_to_standardize_name = (
Adam Snaider1c095c92023-07-08 02:09:58 -0400564 toolchain.target_os.startswith(("linux", "mac", "darwin")) and
Brian Silvermancc09f182022-03-09 15:40:20 -0800565 artifact.basename.endswith(".a") and not artifact.basename.startswith("lib")
566 ) or (
Adam Snaider1c095c92023-07-08 02:09:58 -0400567 toolchain.target_os.startswith("windows") and not artifact.basename.endswith(".lib")
Brian Silvermancc09f182022-03-09 15:40:20 -0800568 )
569
570 # Detect cases where we need to disambiguate library dependencies
571 # by constructing symlinks.
572 if (
573 needs_symlink_to_standardize_name or
574 # We have multiple libraries with the same name.
575 (name in visited_libs and visited_libs[name].path != artifact.path)
576 ):
577 # Disambiguate the previously visited library (if we just detected
578 # that it is ambiguous) and the current library.
579 if name in visited_libs:
580 old_path = visited_libs[name].path
581 if old_path not in ambiguous_libs:
582 ambiguous_libs[old_path] = _symlink_for_ambiguous_lib(actions, toolchain, crate_info, visited_libs[name])
583 ambiguous_libs[artifact.path] = _symlink_for_ambiguous_lib(actions, toolchain, crate_info, artifact)
584
585 visited_libs[name] = artifact
586 return ambiguous_libs
587
Brian Silverman5f6f2762022-08-13 19:30:05 -0700588def _depend_on_metadata(crate_info, force_depend_on_objects):
589 """Determines if we can depend on metadata for this crate.
590
591 By default (when pipelining is disabled or when the crate type needs to link against
592 objects) we depend on the set of object files (.rlib).
593 When pipelining is enabled and the crate type supports depending on metadata,
594 we depend on metadata files only (.rmeta).
595 In some rare cases, even if both of those conditions are true, we still want to
596 depend on objects. This is what force_depend_on_objects is.
597
598 Args:
599 crate_info (CrateInfo): The Crate to determine this for.
600 force_depend_on_objects (bool): if set we will not depend on metadata.
601
602 Returns:
603 Whether we can depend on metadata for this crate.
604 """
605 if force_depend_on_objects:
606 return False
607
608 return crate_info.type in ("rlib", "lib")
609
Brian Silvermancc09f182022-03-09 15:40:20 -0800610def collect_inputs(
611 ctx,
612 file,
613 files,
614 linkstamps,
615 toolchain,
616 cc_toolchain,
617 feature_configuration,
618 crate_info,
619 dep_info,
620 build_info,
Brian Silverman5f6f2762022-08-13 19:30:05 -0700621 stamp = False,
622 force_depend_on_objects = False,
623 experimental_use_cc_common_link = False):
Brian Silvermancc09f182022-03-09 15:40:20 -0800624 """Gather's the inputs and required input information for a rustc action
625
626 Args:
627 ctx (ctx): The rule's context object.
628 file (struct): A struct containing files defined in label type attributes marked as `allow_single_file`.
629 files (list): A list of all inputs (`ctx.files`).
630 linkstamps (depset): A depset of CcLinkstamps that need to be compiled and linked into all linked binaries.
631 toolchain (rust_toolchain): The current `rust_toolchain`.
632 cc_toolchain (CcToolchainInfo): The current `cc_toolchain`.
633 feature_configuration (FeatureConfiguration): Feature configuration to be queried.
634 crate_info (CrateInfo): The Crate information of the crate to process build scripts for.
635 dep_info (DepInfo): The target Crate's dependency information.
636 build_info (BuildInfo): The target Crate's build settings.
637 stamp (bool, optional): Whether or not workspace status stamping is enabled. For more details see
638 https://docs.bazel.build/versions/main/user-manual.html#flag--stamp
Brian Silverman5f6f2762022-08-13 19:30:05 -0700639 force_depend_on_objects (bool, optional): Forces dependencies of this rule to be objects rather than
640 metadata, even for libraries. This is used in rustdoc tests.
641 experimental_use_cc_common_link (bool, optional): Whether rules_rust uses cc_common.link to link
642 rust binaries.
Brian Silvermancc09f182022-03-09 15:40:20 -0800643
644 Returns:
645 tuple: A tuple: A tuple of the following items:
646 - (list): A list of all build info `OUT_DIR` File objects
647 - (str): The `OUT_DIR` of the current build info
648 - (File): An optional path to a generated environment file from a `cargo_build_script` target
649 - (depset[File]): All direct and transitive build flag files from the current build info
650 - (list[File]): Linkstamp outputs
651 - (dict[String, File]): Ambiguous libs, see `_disambiguate_libs`.
652 """
653 linker_script = getattr(file, "linker_script") if hasattr(file, "linker_script") else None
654
655 linker_depset = cc_toolchain.all_files
Adam Snaider1c095c92023-07-08 02:09:58 -0400656 compilation_mode = ctx.var["COMPILATION_MODE"]
Brian Silvermancc09f182022-03-09 15:40:20 -0800657
Adam Snaider1c095c92023-07-08 02:09:58 -0400658 use_pic = _should_use_pic(cc_toolchain, feature_configuration, crate_info.type, compilation_mode)
Brian Silvermancc09f182022-03-09 15:40:20 -0800659
660 # Pass linker inputs only for linking-like actions, not for example where
661 # the output is rlib. This avoids quadratic behavior where transitive noncrates are
662 # flattened on each transitive rust_library dependency.
663 additional_transitive_inputs = []
664 ambiguous_libs = {}
Adam Snaider1c095c92023-07-08 02:09:58 -0400665 if crate_info.type not in ("lib", "rlib"):
Brian Silvermancc09f182022-03-09 15:40:20 -0800666 linker_inputs = dep_info.transitive_noncrates.to_list()
667 ambiguous_libs = _disambiguate_libs(ctx.actions, toolchain, crate_info, dep_info, use_pic)
668 additional_transitive_inputs = _collect_libs_from_linker_inputs(linker_inputs, use_pic) + [
669 additional_input
670 for linker_input in linker_inputs
671 for additional_input in linker_input.additional_inputs
672 ] + ambiguous_libs.values()
673
674 # Compute linkstamps. Use the inputs of the binary as inputs to the
675 # linkstamp action to ensure linkstamps are rebuilt whenever binary inputs
676 # change.
677 linkstamp_outs = []
678
Brian Silverman5f6f2762022-08-13 19:30:05 -0700679 transitive_crate_outputs = dep_info.transitive_crate_outputs
680 if _depend_on_metadata(crate_info, force_depend_on_objects):
681 transitive_crate_outputs = dep_info.transitive_metadata_outputs
682
Brian Silvermancc09f182022-03-09 15:40:20 -0800683 nolinkstamp_compile_inputs = depset(
684 getattr(files, "data", []) +
685 ([build_info.rustc_env, build_info.flags] if build_info else []) +
686 ([toolchain.target_json] if toolchain.target_json else []) +
687 ([] if linker_script == None else [linker_script]),
688 transitive = [
689 linker_depset,
690 crate_info.srcs,
Brian Silverman5f6f2762022-08-13 19:30:05 -0700691 transitive_crate_outputs,
Brian Silvermancc09f182022-03-09 15:40:20 -0800692 depset(additional_transitive_inputs),
693 crate_info.compile_data,
694 toolchain.all_files,
695 ],
696 )
697
Brian Silverman5f6f2762022-08-13 19:30:05 -0700698 # Register linkstamps when linking with rustc (when linking with
699 # cc_common.link linkstamps are handled by cc_common.link itself).
700 if not experimental_use_cc_common_link and crate_info.type in ("bin", "cdylib"):
Brian Silvermancc09f182022-03-09 15:40:20 -0800701 # There is no other way to register an action for each member of a depset than
702 # flattening the depset as of 2021-10-12. Luckily, usually there is only one linkstamp
703 # in a build, and we only flatten the list on binary targets that perform transitive linking,
704 # so it's extremely unlikely that this call to `to_list()` will ever be a performance
705 # problem.
706 for linkstamp in linkstamps.to_list():
707 # The linkstamp output path is based on the binary crate
708 # name and the input linkstamp path. This is to disambiguate
709 # the linkstamp outputs produced by multiple binary crates
710 # that depend on the same linkstamp. We use the same pattern
711 # for the output name as the one used by native cc rules.
712 out_name = "_objs/" + crate_info.output.basename + "/" + linkstamp.file().path[:-len(linkstamp.file().extension)] + "o"
713 linkstamp_out = ctx.actions.declare_file(out_name)
714 linkstamp_outs.append(linkstamp_out)
715 cc_common.register_linkstamp_compile_action(
716 actions = ctx.actions,
717 cc_toolchain = cc_toolchain,
718 feature_configuration = feature_configuration,
719 grep_includes = ctx.file._grep_includes,
720 source_file = linkstamp.file(),
721 output_file = linkstamp_out,
722 compilation_inputs = linkstamp.hdrs(),
723 inputs_for_validation = nolinkstamp_compile_inputs,
724 label_replacement = str(ctx.label),
725 output_replacement = crate_info.output.path,
726 )
727
Brian Silverman5f6f2762022-08-13 19:30:05 -0700728 # If stamping is enabled include the volatile and stable status info file
729 stamp_info = [ctx.version_file, ctx.info_file] if stamp else []
Brian Silvermancc09f182022-03-09 15:40:20 -0800730
731 compile_inputs = depset(
732 linkstamp_outs + stamp_info,
733 transitive = [
734 nolinkstamp_compile_inputs,
735 ],
736 )
737
Brian Silverman5f6f2762022-08-13 19:30:05 -0700738 # For backwards compatibility, we also check the value of the `rustc_env_files` attribute when
739 # `crate_info.rustc_env_files` is not populated.
740 build_env_files = crate_info.rustc_env_files if crate_info.rustc_env_files else getattr(files, "rustc_env_files", [])
Brian Silvermancc09f182022-03-09 15:40:20 -0800741 compile_inputs, out_dir, build_env_file, build_flags_files = _process_build_scripts(build_info, dep_info, compile_inputs)
742 if build_env_file:
743 build_env_files = [f for f in build_env_files] + [build_env_file]
744 compile_inputs = depset(build_env_files, transitive = [compile_inputs])
745
746 return compile_inputs, out_dir, build_env_files, build_flags_files, linkstamp_outs, ambiguous_libs
747
748def construct_arguments(
749 ctx,
750 attr,
751 file,
752 toolchain,
753 tool_path,
754 cc_toolchain,
755 feature_configuration,
756 crate_info,
757 dep_info,
758 linkstamp_outs,
759 ambiguous_libs,
760 output_hash,
761 rust_flags,
762 out_dir,
763 build_env_files,
764 build_flags_files,
765 emit = ["dep-info", "link"],
766 force_all_deps_direct = False,
Adam Snaider1c095c92023-07-08 02:09:58 -0400767 rustdoc = False,
Brian Silvermancc09f182022-03-09 15:40:20 -0800768 stamp = False,
Adam Snaider1c095c92023-07-08 02:09:58 -0400769 remap_path_prefix = "",
Brian Silverman5f6f2762022-08-13 19:30:05 -0700770 use_json_output = False,
771 build_metadata = False,
772 force_depend_on_objects = False):
Brian Silvermancc09f182022-03-09 15:40:20 -0800773 """Builds an Args object containing common rustc flags
774
775 Args:
776 ctx (ctx): The rule's context object
777 attr (struct): The attributes for the target. These may be different from ctx.attr in an aspect context.
778 file (struct): A struct containing files defined in label type attributes marked as `allow_single_file`.
779 toolchain (rust_toolchain): The current target's `rust_toolchain`
780 tool_path (str): Path to rustc
781 cc_toolchain (CcToolchain): The CcToolchain for the current target.
782 feature_configuration (FeatureConfiguration): Class used to construct command lines from CROSSTOOL features.
783 crate_info (CrateInfo): The CrateInfo provider of the target crate
784 dep_info (DepInfo): The DepInfo provider of the target crate
785 linkstamp_outs (list): Linkstamp outputs of native dependencies
786 ambiguous_libs (dict): Ambiguous libs, see `_disambiguate_libs`
787 output_hash (str): The hashed path of the crate root
788 rust_flags (list): Additional flags to pass to rustc
789 out_dir (str): The path to the output directory for the target Crate.
790 build_env_files (list): Files containing rustc environment variables, for instance from `cargo_build_script` actions.
791 build_flags_files (depset): The output files of a `cargo_build_script` actions containing rustc build flags
792 emit (list): Values for the --emit flag to rustc.
793 force_all_deps_direct (bool, optional): Whether to pass the transitive rlibs with --extern
794 to the commandline as opposed to -L.
Adam Snaider1c095c92023-07-08 02:09:58 -0400795 rustdoc (bool, optional): Whether to add "bin" link flags to the command regardless of `emit` and `crate_type`.
Brian Silvermancc09f182022-03-09 15:40:20 -0800796 stamp (bool, optional): Whether or not workspace status stamping is enabled. For more details see
797 https://docs.bazel.build/versions/main/user-manual.html#flag--stamp
Adam Snaider1c095c92023-07-08 02:09:58 -0400798 remap_path_prefix (str, optional): A value used to remap `${pwd}` to. If set to None, no prefix will be set.
Brian Silverman5f6f2762022-08-13 19:30:05 -0700799 use_json_output (bool): Have rustc emit json and process_wrapper parse json messages to output rendered output.
800 build_metadata (bool): Generate CLI arguments for building *only* .rmeta files. This requires use_json_output.
801 force_depend_on_objects (bool): Force using `.rlib` object files instead of metadata (`.rmeta`) files even if they are available.
Brian Silvermancc09f182022-03-09 15:40:20 -0800802
803 Returns:
804 tuple: A tuple of the following items
805 - (struct): A struct of arguments used to run the `Rustc` action
806 - process_wrapper_flags (Args): Arguments for the process wrapper
807 - rustc_path (Args): Arguments for invoking rustc via the process wrapper
808 - rustc_flags (Args): Rust flags for the Rust compiler
809 - all (list): A list of all `Args` objects in the order listed above.
810 This is to be passed to the `arguments` parameter of actions
811 - (dict): Common rustc environment variables
812 """
Brian Silverman5f6f2762022-08-13 19:30:05 -0700813 if build_metadata and not use_json_output:
814 fail("build_metadata requires parse_json_output")
815
Brian Silvermancc09f182022-03-09 15:40:20 -0800816 output_dir = getattr(crate_info.output, "dirname", None)
817 linker_script = getattr(file, "linker_script", None)
818
819 env = _get_rustc_env(attr, toolchain, crate_info.name)
820
821 # Wrapper args first
822 process_wrapper_flags = ctx.actions.args()
823
824 for build_env_file in build_env_files:
825 process_wrapper_flags.add("--env-file", build_env_file)
826
827 process_wrapper_flags.add_all(build_flags_files, before_each = "--arg-file")
828
829 # Certain rust build processes expect to find files from the environment
830 # variable `$CARGO_MANIFEST_DIR`. Examples of this include pest, tera,
831 # asakuma.
832 #
833 # The compiler and by extension proc-macros see the current working
834 # directory as the Bazel exec root. This is what `$CARGO_MANIFEST_DIR`
835 # would default to but is often the wrong value (e.g. if the source is in a
836 # sub-package or if we are building something in an external repository).
837 # Hence, we need to set `CARGO_MANIFEST_DIR` explicitly.
838 #
839 # Since we cannot get the `exec_root` from starlark, we cheat a little and
840 # use `${pwd}` which resolves the `exec_root` at action execution time.
841 process_wrapper_flags.add("--subst", "pwd=${pwd}")
842
843 # If stamping is enabled, enable the functionality in the process wrapper
844 if stamp:
845 process_wrapper_flags.add("--volatile-status-file", ctx.version_file)
Brian Silverman5f6f2762022-08-13 19:30:05 -0700846 process_wrapper_flags.add("--stable-status-file", ctx.info_file)
Brian Silvermancc09f182022-03-09 15:40:20 -0800847
848 # Both ctx.label.workspace_root and ctx.label.package are relative paths
849 # and either can be empty strings. Avoid trailing/double slashes in the path.
850 components = "${{pwd}}/{}/{}".format(ctx.label.workspace_root, ctx.label.package).split("/")
851 env["CARGO_MANIFEST_DIR"] = "/".join([c for c in components if c])
852
853 if out_dir != None:
854 env["OUT_DIR"] = "${pwd}/" + out_dir
855
856 # Handle that the binary name and crate name may be different.
857 #
858 # If a target name contains a - then cargo (and rules_rust) will generate a
859 # crate name with _ instead. Accordingly, rustc will generate a output
860 # file (executable, or rlib, or whatever) with _ not -. But when cargo
861 # puts a binary in the target/${config} directory, and sets environment
862 # variables like `CARGO_BIN_EXE_${binary_name}` it will use the - version
863 # not the _ version. So we rename the rustc-generated file (with _s) to
864 # have -s if needed.
865 emit_with_paths = emit
866 if crate_info.type == "bin" and crate_info.output != None:
867 generated_file = crate_info.name + toolchain.binary_ext
868 src = "/".join([crate_info.output.dirname, generated_file])
869 dst = crate_info.output.path
870 if src != dst:
871 emit_with_paths = [("link=" + dst if val == "link" else val) for val in emit]
872
873 # Arguments for launching rustc from the process wrapper
874 rustc_path = ctx.actions.args()
875 rustc_path.add("--")
876 rustc_path.add(tool_path)
877
878 # Rustc arguments
879 rustc_flags = ctx.actions.args()
880 rustc_flags.set_param_file_format("multiline")
881 rustc_flags.use_param_file("@%s", use_always = False)
882 rustc_flags.add(crate_info.root)
883 rustc_flags.add("--crate-name=" + crate_info.name)
884 rustc_flags.add("--crate-type=" + crate_info.type)
Brian Silvermancc09f182022-03-09 15:40:20 -0800885
Brian Silverman5f6f2762022-08-13 19:30:05 -0700886 error_format = "human"
887 if hasattr(attr, "_error_format"):
888 error_format = attr._error_format[ErrorFormatInfo].error_format
889
890 if use_json_output:
891 # If --error-format was set to json, we just pass the output through
892 # Otherwise process_wrapper uses the "rendered" field.
893 process_wrapper_flags.add("--rustc-output-format", "json" if error_format == "json" else "rendered")
894
895 # Configure rustc json output by adding artifact notifications.
896 # These will always be filtered out by process_wrapper and will be use to terminate
897 # rustc when appropriate.
898 json = ["artifacts"]
899 if error_format == "short":
900 json.append("diagnostic-short")
Adam Snaider1c095c92023-07-08 02:09:58 -0400901 elif error_format == "human" and toolchain.target_os != "windows":
Brian Silverman5f6f2762022-08-13 19:30:05 -0700902 # If the os is not windows, we can get colorized output.
903 json.append("diagnostic-rendered-ansi")
904
905 rustc_flags.add("--json=" + ",".join(json))
906
907 error_format = "json"
908
909 if build_metadata:
910 # Configure process_wrapper to terminate rustc when metadata are emitted
911 process_wrapper_flags.add("--rustc-quit-on-rmeta", "true")
912
913 rustc_flags.add("--error-format=" + error_format)
914
915 # Mangle symbols to disambiguate crates with the same name. This could
916 # happen only for non-final artifacts where we compute an output_hash,
917 # e.g., rust_library.
918 #
919 # For "final" artifacts and ones intended for distribution outside of
920 # Bazel, such as rust_binary, rust_static_library and rust_shared_library,
921 # where output_hash is None we don't need to add these flags.
922 if output_hash:
923 extra_filename = "-" + output_hash
924 rustc_flags.add("--codegen=metadata=" + extra_filename)
925 rustc_flags.add("--codegen=extra-filename=" + extra_filename)
926
Brian Silvermancc09f182022-03-09 15:40:20 -0800927 if output_dir:
928 rustc_flags.add("--out-dir=" + output_dir)
Brian Silvermancc09f182022-03-09 15:40:20 -0800929
930 compilation_mode = get_compilation_mode_opts(ctx, toolchain)
931 rustc_flags.add("--codegen=opt-level=" + compilation_mode.opt_level)
932 rustc_flags.add("--codegen=debuginfo=" + compilation_mode.debug_info)
933
934 # For determinism to help with build distribution and such
Adam Snaider1c095c92023-07-08 02:09:58 -0400935 if remap_path_prefix != None:
Brian Silvermancc09f182022-03-09 15:40:20 -0800936 rustc_flags.add("--remap-path-prefix=${{pwd}}={}".format(remap_path_prefix))
937
938 if emit:
939 rustc_flags.add("--emit=" + ",".join(emit_with_paths))
Brian Silverman5f6f2762022-08-13 19:30:05 -0700940 if error_format != "json":
941 # Color is not compatible with json output.
942 rustc_flags.add("--color=always")
Brian Silvermancc09f182022-03-09 15:40:20 -0800943 rustc_flags.add("--target=" + toolchain.target_flag_value)
944 if hasattr(attr, "crate_features"):
945 rustc_flags.add_all(getattr(attr, "crate_features"), before_each = "--cfg", format_each = 'feature="%s"')
946 if linker_script:
947 rustc_flags.add(linker_script.path, format = "--codegen=link-arg=-T%s")
948
949 # Gets the paths to the folders containing the standard library (or libcore)
950 rust_std_paths = toolchain.rust_std_paths.to_list()
951
952 # Tell Rustc where to find the standard library
953 rustc_flags.add_all(rust_std_paths, before_each = "-L", format_each = "%s")
954 rustc_flags.add_all(rust_flags)
955
Adam Snaider1c095c92023-07-08 02:09:58 -0400956 # Gather data path from crate_info since it is inherited from real crate for rust_doc and rust_test
Brian Silvermancc09f182022-03-09 15:40:20 -0800957 # Deduplicate data paths due to https://github.com/bazelbuild/bazel/issues/14681
Adam Snaider1c095c92023-07-08 02:09:58 -0400958 data_paths = depset(direct = getattr(attr, "data", []), transitive = [crate_info.compile_data_targets]).to_list()
Brian Silvermancc09f182022-03-09 15:40:20 -0800959
960 rustc_flags.add_all(
961 expand_list_element_locations(
962 ctx,
963 getattr(attr, "rustc_flags", []),
964 data_paths,
965 ),
966 )
967 add_edition_flags(rustc_flags, crate_info)
968
969 # Link!
Adam Snaider1c095c92023-07-08 02:09:58 -0400970 if ("link" in emit and crate_info.type not in ["rlib", "lib"]) or rustdoc:
Brian Silvermancc09f182022-03-09 15:40:20 -0800971 # Rust's built-in linker can handle linking wasm files. We don't want to attempt to use the cc
972 # linker since it won't understand.
Adam Snaider1c095c92023-07-08 02:09:58 -0400973 compilation_mode = ctx.var["COMPILATION_MODE"]
Brian Silvermancc09f182022-03-09 15:40:20 -0800974 if toolchain.target_arch != "wasm32":
975 if output_dir:
Adam Snaider1c095c92023-07-08 02:09:58 -0400976 use_pic = _should_use_pic(cc_toolchain, feature_configuration, crate_info.type, compilation_mode)
Brian Silvermancc09f182022-03-09 15:40:20 -0800977 rpaths = _compute_rpaths(toolchain, output_dir, dep_info, use_pic)
978 else:
979 rpaths = depset([])
Adam Snaider1c095c92023-07-08 02:09:58 -0400980
981 ld, link_args, link_env = get_linker_and_args(ctx, attr, crate_info.type, cc_toolchain, feature_configuration, rpaths, rustdoc)
982
Brian Silvermancc09f182022-03-09 15:40:20 -0800983 env.update(link_env)
984 rustc_flags.add("--codegen=linker=" + ld)
985 rustc_flags.add_joined("--codegen", link_args, join_with = " ", format_joined = "link-args=%s")
986
Adam Snaider1c095c92023-07-08 02:09:58 -0400987 _add_native_link_flags(rustc_flags, dep_info, linkstamp_outs, ambiguous_libs, crate_info.type, toolchain, cc_toolchain, feature_configuration, compilation_mode)
Brian Silvermancc09f182022-03-09 15:40:20 -0800988
Brian Silverman5f6f2762022-08-13 19:30:05 -0700989 use_metadata = _depend_on_metadata(crate_info, force_depend_on_objects)
Brian Silvermancc09f182022-03-09 15:40:20 -0800990
Brian Silverman5f6f2762022-08-13 19:30:05 -0700991 # These always need to be added, even if not linking this crate.
992 add_crate_link_flags(rustc_flags, dep_info, force_all_deps_direct, use_metadata)
993
994 needs_extern_proc_macro_flag = _is_proc_macro(crate_info) and crate_info.edition != "2015"
Brian Silvermancc09f182022-03-09 15:40:20 -0800995 if needs_extern_proc_macro_flag:
996 rustc_flags.add("--extern")
997 rustc_flags.add("proc_macro")
998
Brian Silverman5f6f2762022-08-13 19:30:05 -0700999 if toolchain.llvm_cov and ctx.configuration.coverage_enabled:
1000 rustc_flags.add("--codegen=instrument-coverage")
1001
Brian Silvermancc09f182022-03-09 15:40:20 -08001002 # Make bin crate data deps available to tests.
1003 for data in getattr(attr, "data", []):
1004 if rust_common.crate_info in data:
1005 dep_crate_info = data[rust_common.crate_info]
1006 if dep_crate_info.type == "bin":
1007 # Trying to make CARGO_BIN_EXE_{} canonical across platform by strip out extension if exists
1008 env_basename = dep_crate_info.output.basename[:-(1 + len(dep_crate_info.output.extension))] if len(dep_crate_info.output.extension) > 0 else dep_crate_info.output.basename
1009 env["CARGO_BIN_EXE_" + env_basename] = dep_crate_info.output.short_path
1010
Brian Silverman5f6f2762022-08-13 19:30:05 -07001011 # Add environment variables from the Rust toolchain.
1012 env.update(toolchain.env)
1013
Brian Silvermancc09f182022-03-09 15:40:20 -08001014 # Update environment with user provided variables.
1015 env.update(expand_dict_value_locations(
1016 ctx,
1017 crate_info.rustc_env,
1018 data_paths,
1019 ))
1020
1021 # Ensure the sysroot is set for the target platform
1022 env["SYSROOT"] = toolchain.sysroot
1023
1024 if toolchain._rename_first_party_crates:
1025 env["RULES_RUST_THIRD_PARTY_DIR"] = toolchain._third_party_dir
1026
Adam Snaider1c095c92023-07-08 02:09:58 -04001027 if is_exec_configuration(ctx):
1028 rustc_flags.add_all(toolchain.extra_exec_rustc_flags)
1029 else:
1030 rustc_flags.add_all(toolchain.extra_rustc_flags)
1031
Brian Silvermancc09f182022-03-09 15:40:20 -08001032 # extra_rustc_flags apply to the target configuration, not the exec configuration.
1033 if hasattr(ctx.attr, "_extra_rustc_flags") and not is_exec_configuration(ctx):
1034 rustc_flags.add_all(ctx.attr._extra_rustc_flags[ExtraRustcFlagsInfo].extra_rustc_flags)
1035
Brian Silverman5f6f2762022-08-13 19:30:05 -07001036 if hasattr(ctx.attr, "_extra_rustc_flag") and not is_exec_configuration(ctx):
1037 rustc_flags.add_all(ctx.attr._extra_rustc_flag[ExtraRustcFlagsInfo].extra_rustc_flags)
1038
Adam Snaider1c095c92023-07-08 02:09:58 -04001039 if hasattr(ctx.attr, "_per_crate_rustc_flag") and not is_exec_configuration(ctx):
1040 per_crate_rustc_flags = ctx.attr._per_crate_rustc_flag[PerCrateRustcFlagsInfo].per_crate_rustc_flags
1041 _add_per_crate_rustc_flags(ctx, rustc_flags, crate_info, per_crate_rustc_flags)
1042
Brian Silvermancc09f182022-03-09 15:40:20 -08001043 if hasattr(ctx.attr, "_extra_exec_rustc_flags") and is_exec_configuration(ctx):
1044 rustc_flags.add_all(ctx.attr._extra_exec_rustc_flags[ExtraExecRustcFlagsInfo].extra_exec_rustc_flags)
1045
Brian Silverman5f6f2762022-08-13 19:30:05 -07001046 if hasattr(ctx.attr, "_extra_exec_rustc_flag") and is_exec_configuration(ctx):
1047 rustc_flags.add_all(ctx.attr._extra_exec_rustc_flag[ExtraExecRustcFlagsInfo].extra_exec_rustc_flags)
1048
Adam Snaider1c095c92023-07-08 02:09:58 -04001049 if _is_no_std(ctx, toolchain, crate_info):
1050 rustc_flags.add_all(['--cfg=feature="no_std"'])
1051
Brian Silvermancc09f182022-03-09 15:40:20 -08001052 # Create a struct which keeps the arguments separate so each may be tuned or
1053 # replaced where necessary
1054 args = struct(
1055 process_wrapper_flags = process_wrapper_flags,
1056 rustc_path = rustc_path,
1057 rustc_flags = rustc_flags,
1058 all = [process_wrapper_flags, rustc_path, rustc_flags],
1059 )
1060
1061 return args, env
1062
1063def rustc_compile_action(
1064 ctx,
1065 attr,
1066 toolchain,
1067 crate_info,
1068 output_hash = None,
1069 rust_flags = [],
1070 force_all_deps_direct = False):
1071 """Create and run a rustc compile action based on the current rule's attributes
1072
1073 Args:
1074 ctx (ctx): The rule's context object
1075 attr (struct): Attributes to use for the rust compile action
1076 toolchain (rust_toolchain): The current `rust_toolchain`
1077 crate_info (CrateInfo): The CrateInfo provider for the current target.
1078 output_hash (str, optional): The hashed path of the crate root. Defaults to None.
1079 rust_flags (list, optional): Additional flags to pass to rustc. Defaults to [].
1080 force_all_deps_direct (bool, optional): Whether to pass the transitive rlibs with --extern
1081 to the commandline as opposed to -L.
1082
1083 Returns:
1084 list: A list of the following providers:
1085 - (CrateInfo): info for the crate we just built; same as `crate_info` parameter.
1086 - (DepInfo): The transitive dependencies of this crate.
1087 - (DefaultInfo): The output file for this crate, and its runfiles.
1088 """
Brian Silverman5f6f2762022-08-13 19:30:05 -07001089 build_metadata = getattr(crate_info, "metadata", None)
1090
Brian Silvermancc09f182022-03-09 15:40:20 -08001091 cc_toolchain, feature_configuration = find_cc_toolchain(ctx)
1092
Brian Silverman5f6f2762022-08-13 19:30:05 -07001093 # Determine whether to use cc_common.link:
1094 # * either if experimental_use_cc_common_link is 1,
1095 # * or if experimental_use_cc_common_link is -1 and
1096 # the toolchain experimental_use_cc_common_link is true.
1097 experimental_use_cc_common_link = False
1098 if hasattr(ctx.attr, "experimental_use_cc_common_link"):
1099 if ctx.attr.experimental_use_cc_common_link == 0:
1100 experimental_use_cc_common_link = False
1101 elif ctx.attr.experimental_use_cc_common_link == 1:
1102 experimental_use_cc_common_link = True
1103 elif ctx.attr.experimental_use_cc_common_link == -1:
1104 experimental_use_cc_common_link = toolchain._experimental_use_cc_common_link
1105
Brian Silvermancc09f182022-03-09 15:40:20 -08001106 dep_info, build_info, linkstamps = collect_deps(
1107 deps = crate_info.deps,
1108 proc_macro_deps = crate_info.proc_macro_deps,
1109 aliases = crate_info.aliases,
1110 are_linkstamps_supported = _are_linkstamps_supported(
1111 feature_configuration = feature_configuration,
1112 has_grep_includes = hasattr(ctx.attr, "_grep_includes"),
1113 ),
1114 )
1115
1116 # Determine if the build is currently running with --stamp
1117 stamp = is_stamping_enabled(attr)
1118
1119 compile_inputs, out_dir, build_env_files, build_flags_files, linkstamp_outs, ambiguous_libs = collect_inputs(
1120 ctx = ctx,
1121 file = ctx.file,
1122 files = ctx.files,
1123 linkstamps = linkstamps,
1124 toolchain = toolchain,
1125 cc_toolchain = cc_toolchain,
1126 feature_configuration = feature_configuration,
1127 crate_info = crate_info,
1128 dep_info = dep_info,
1129 build_info = build_info,
1130 stamp = stamp,
Brian Silverman5f6f2762022-08-13 19:30:05 -07001131 experimental_use_cc_common_link = experimental_use_cc_common_link,
Brian Silvermancc09f182022-03-09 15:40:20 -08001132 )
1133
Brian Silverman5f6f2762022-08-13 19:30:05 -07001134 # The types of rustc outputs to emit.
1135 # If we build metadata, we need to keep the command line of the two invocations
1136 # (rlib and rmeta) as similar as possible, otherwise rustc rejects the rmeta as
1137 # a candidate.
1138 # Because of that we need to add emit=metadata to both the rlib and rmeta invocation.
1139 #
1140 # When cc_common linking is enabled, emit a `.o` file, which is later
1141 # passed to the cc_common.link action.
1142 emit = ["dep-info", "link"]
1143 if build_metadata:
1144 emit.append("metadata")
1145 if experimental_use_cc_common_link:
1146 emit = ["obj"]
1147
Brian Silvermancc09f182022-03-09 15:40:20 -08001148 args, env_from_args = construct_arguments(
1149 ctx = ctx,
1150 attr = attr,
1151 file = ctx.file,
1152 toolchain = toolchain,
1153 tool_path = toolchain.rustc.path,
1154 cc_toolchain = cc_toolchain,
Brian Silverman5f6f2762022-08-13 19:30:05 -07001155 emit = emit,
Brian Silvermancc09f182022-03-09 15:40:20 -08001156 feature_configuration = feature_configuration,
1157 crate_info = crate_info,
1158 dep_info = dep_info,
1159 linkstamp_outs = linkstamp_outs,
1160 ambiguous_libs = ambiguous_libs,
1161 output_hash = output_hash,
1162 rust_flags = rust_flags,
1163 out_dir = out_dir,
1164 build_env_files = build_env_files,
1165 build_flags_files = build_flags_files,
1166 force_all_deps_direct = force_all_deps_direct,
1167 stamp = stamp,
Brian Silverman5f6f2762022-08-13 19:30:05 -07001168 use_json_output = bool(build_metadata),
Brian Silvermancc09f182022-03-09 15:40:20 -08001169 )
1170
Brian Silverman5f6f2762022-08-13 19:30:05 -07001171 args_metadata = None
1172 if build_metadata:
1173 args_metadata, _ = construct_arguments(
1174 ctx = ctx,
1175 attr = attr,
1176 file = ctx.file,
1177 toolchain = toolchain,
1178 tool_path = toolchain.rustc.path,
1179 cc_toolchain = cc_toolchain,
1180 emit = emit,
1181 feature_configuration = feature_configuration,
1182 crate_info = crate_info,
1183 dep_info = dep_info,
1184 linkstamp_outs = linkstamp_outs,
1185 ambiguous_libs = ambiguous_libs,
1186 output_hash = output_hash,
1187 rust_flags = rust_flags,
1188 out_dir = out_dir,
1189 build_env_files = build_env_files,
1190 build_flags_files = build_flags_files,
1191 force_all_deps_direct = force_all_deps_direct,
1192 stamp = stamp,
1193 use_json_output = True,
1194 build_metadata = True,
1195 )
1196
Brian Silvermancc09f182022-03-09 15:40:20 -08001197 env = dict(ctx.configuration.default_shell_env)
1198 env.update(env_from_args)
1199
1200 if hasattr(attr, "version") and attr.version != "0.0.0":
1201 formatted_version = " v{}".format(attr.version)
1202 else:
1203 formatted_version = ""
1204
Brian Silverman5f6f2762022-08-13 19:30:05 -07001205 # Declares the outputs of the rustc compile action.
1206 # By default this is the binary output; if cc_common.link is used, this is
1207 # the main `.o` file (`output_o` below).
Brian Silvermancc09f182022-03-09 15:40:20 -08001208 outputs = [crate_info.output]
1209
Brian Silverman5f6f2762022-08-13 19:30:05 -07001210 # The `.o` output file, only used for linking via cc_common.link.
1211 output_o = None
1212 if experimental_use_cc_common_link:
1213 obj_ext = ".o"
1214 output_o = ctx.actions.declare_file(crate_info.name + obj_ext, sibling = crate_info.output)
1215 outputs = [output_o]
1216
Brian Silvermancc09f182022-03-09 15:40:20 -08001217 # For a cdylib that might be added as a dependency to a cc_* target on Windows, it is important to include the
1218 # interface library that rustc generates in the output files.
1219 interface_library = None
Adam Snaider1c095c92023-07-08 02:09:58 -04001220 if toolchain.target_os == "windows" and crate_info.type == "cdylib":
Brian Silvermancc09f182022-03-09 15:40:20 -08001221 # Rustc generates the import library with a `.dll.lib` extension rather than the usual `.lib` one that msvc
1222 # expects (see https://github.com/rust-lang/rust/pull/29520 for more context).
Brian Silverman5f6f2762022-08-13 19:30:05 -07001223 interface_library = ctx.actions.declare_file(crate_info.output.basename + ".lib", sibling = crate_info.output)
Brian Silvermancc09f182022-03-09 15:40:20 -08001224 outputs.append(interface_library)
1225
1226 # The action might generate extra output that we don't want to include in the `DefaultInfo` files.
1227 action_outputs = list(outputs)
1228
1229 # Rustc generates a pdb file (on Windows) or a dsym folder (on macos) so provide it in an output group for crate
1230 # types that benefit from having debug information in a separate file.
1231 pdb_file = None
1232 dsym_folder = None
Adam Snaider1c095c92023-07-08 02:09:58 -04001233 if crate_info.type in ("cdylib", "bin"):
1234 if toolchain.target_os == "windows":
Brian Silverman5f6f2762022-08-13 19:30:05 -07001235 pdb_file = ctx.actions.declare_file(crate_info.output.basename[:-len(crate_info.output.extension)] + "pdb", sibling = crate_info.output)
Brian Silvermancc09f182022-03-09 15:40:20 -08001236 action_outputs.append(pdb_file)
Adam Snaider1c095c92023-07-08 02:09:58 -04001237 elif toolchain.target_os == "darwin":
Brian Silverman5f6f2762022-08-13 19:30:05 -07001238 dsym_folder = ctx.actions.declare_directory(crate_info.output.basename + ".dSYM", sibling = crate_info.output)
Brian Silvermancc09f182022-03-09 15:40:20 -08001239 action_outputs.append(dsym_folder)
1240
Brian Silverman5f6f2762022-08-13 19:30:05 -07001241 if ctx.executable._process_wrapper:
Brian Silvermancc09f182022-03-09 15:40:20 -08001242 # Run as normal
1243 ctx.actions.run(
1244 executable = ctx.executable._process_wrapper,
1245 inputs = compile_inputs,
1246 outputs = action_outputs,
1247 env = env,
1248 arguments = args.all,
1249 mnemonic = "Rustc",
1250 progress_message = "Compiling Rust {} {}{} ({} files)".format(
1251 crate_info.type,
1252 ctx.label.name,
1253 formatted_version,
1254 len(crate_info.srcs.to_list()),
1255 ),
1256 )
Brian Silverman5f6f2762022-08-13 19:30:05 -07001257 if args_metadata:
1258 ctx.actions.run(
1259 executable = ctx.executable._process_wrapper,
1260 inputs = compile_inputs,
1261 outputs = [build_metadata],
1262 env = env,
1263 arguments = args_metadata.all,
1264 mnemonic = "RustcMetadata",
1265 progress_message = "Compiling Rust metadata {} {}{} ({} files)".format(
1266 crate_info.type,
1267 ctx.label.name,
1268 formatted_version,
1269 len(crate_info.srcs.to_list()),
1270 ),
1271 )
Brian Silvermancc09f182022-03-09 15:40:20 -08001272 else:
1273 # Run without process_wrapper
Brian Silverman5f6f2762022-08-13 19:30:05 -07001274 if build_env_files or build_flags_files or stamp or build_metadata:
1275 fail("build_env_files, build_flags_files, stamp, build_metadata are not supported when building without process_wrapper")
Brian Silvermancc09f182022-03-09 15:40:20 -08001276 ctx.actions.run(
1277 executable = toolchain.rustc,
1278 inputs = compile_inputs,
1279 outputs = action_outputs,
1280 env = env,
1281 arguments = [args.rustc_flags],
1282 mnemonic = "Rustc",
1283 progress_message = "Compiling Rust (without process_wrapper) {} {}{} ({} files)".format(
1284 crate_info.type,
1285 ctx.label.name,
1286 formatted_version,
1287 len(crate_info.srcs.to_list()),
1288 ),
1289 )
1290
Brian Silverman5f6f2762022-08-13 19:30:05 -07001291 if experimental_use_cc_common_link:
1292 # Wrap the main `.o` file into a compilation output suitable for
1293 # cc_common.link. The main `.o` file is useful in both PIC and non-PIC
1294 # modes.
1295 compilation_outputs = cc_common.create_compilation_outputs(
1296 objects = depset([output_o]),
1297 pic_objects = depset([output_o]),
1298 )
1299
Adam Snaider1c095c92023-07-08 02:09:58 -04001300 malloc_library = ctx.attr._custom_malloc or ctx.attr.malloc
1301
Brian Silverman5f6f2762022-08-13 19:30:05 -07001302 # Collect the linking contexts of the standard library and dependencies.
Adam Snaider1c095c92023-07-08 02:09:58 -04001303 linking_contexts = [
1304 malloc_library[CcInfo].linking_context,
1305 _get_std_and_alloc_info(ctx, toolchain, crate_info).linking_context,
1306 toolchain.stdlib_linkflags.linking_context,
1307 ]
Brian Silverman5f6f2762022-08-13 19:30:05 -07001308
1309 for dep in crate_info.deps.to_list():
1310 if dep.cc_info:
1311 linking_contexts.append(dep.cc_info.linking_context)
1312
1313 # In the cc_common.link action we need to pass the name of the final
1314 # binary (output) relative to the package of this target.
1315 # We compute it by stripping the path to the package directory,
1316 # which is a prefix of the path of `crate_info.output`.
1317
1318 # The path to the package dir, including a trailing "/".
1319 package_dir = ctx.bin_dir.path + "/"
1320 if ctx.label.workspace_root:
1321 package_dir = package_dir + ctx.label.workspace_root + "/"
1322 if ctx.label.package:
1323 package_dir = package_dir + ctx.label.package + "/"
1324
1325 if not crate_info.output.path.startswith(package_dir):
1326 fail("The package dir path {} should be a prefix of the crate_info.output.path {}", package_dir, crate_info.output.path)
1327
1328 output_relative_to_package = crate_info.output.path[len(package_dir):]
1329
Adam Snaider1c095c92023-07-08 02:09:58 -04001330 # Compile actions that produce shared libraries create output of the form "libfoo.so" for linux and macos;
1331 # cc_common.link expects us to pass "foo" to the name parameter. We cannot simply use crate_info.name because
1332 # the name of the crate does not always match the name of output file, e.g a crate named foo-bar will produce
1333 # a (lib)foo_bar output file.
1334 if crate_info.type == "cdylib":
1335 output_lib = crate_info.output.basename
1336 if toolchain.target_os != "windows":
1337 # Strip the leading "lib" prefix
1338 output_lib = output_lib[3:]
1339
1340 # Strip the file extension
1341 output_lib = output_lib[:-(1 + len(crate_info.output.extension))]
1342
1343 # Remove the basename (which contains the undesired 'lib' prefix and the file extension)
1344 output_relative_to_package = output_relative_to_package[:-len(crate_info.output.basename)]
1345
1346 # Append the name of the library
1347 output_relative_to_package = output_relative_to_package + output_lib
1348
Brian Silverman5f6f2762022-08-13 19:30:05 -07001349 cc_common.link(
1350 actions = ctx.actions,
1351 feature_configuration = feature_configuration,
1352 cc_toolchain = cc_toolchain,
1353 linking_contexts = linking_contexts,
1354 compilation_outputs = compilation_outputs,
1355 name = output_relative_to_package,
1356 grep_includes = ctx.file._grep_includes,
1357 stamp = ctx.attr.stamp,
Adam Snaider1c095c92023-07-08 02:09:58 -04001358 output_type = "executable" if crate_info.type == "bin" else "dynamic_library",
Brian Silverman5f6f2762022-08-13 19:30:05 -07001359 )
1360
1361 outputs = [crate_info.output]
1362
1363 coverage_runfiles = []
1364 if toolchain.llvm_cov and ctx.configuration.coverage_enabled and crate_info.is_test:
1365 coverage_runfiles = [toolchain.llvm_cov, toolchain.llvm_profdata]
1366
Brian Silvermancc09f182022-03-09 15:40:20 -08001367 runfiles = ctx.runfiles(
Brian Silverman5f6f2762022-08-13 19:30:05 -07001368 files = getattr(ctx.files, "data", []) + coverage_runfiles,
Brian Silvermancc09f182022-03-09 15:40:20 -08001369 collect_data = True,
1370 )
Adam Snaider1c095c92023-07-08 02:09:58 -04001371 if getattr(ctx.attr, "crate", None):
1372 runfiles = runfiles.merge(ctx.attr.crate[DefaultInfo].default_runfiles)
1373 runfiles = runfiles.merge(ctx.attr.crate[DefaultInfo].data_runfiles)
Brian Silvermancc09f182022-03-09 15:40:20 -08001374
1375 # TODO: Remove after some resolution to
1376 # https://github.com/bazelbuild/rules_rust/issues/771
1377 out_binary = getattr(attr, "out_binary", False)
1378
1379 providers = [
Brian Silvermancc09f182022-03-09 15:40:20 -08001380 DefaultInfo(
1381 # nb. This field is required for cc_library to depend on our output.
1382 files = depset(outputs),
1383 runfiles = runfiles,
1384 executable = crate_info.output if crate_info.type == "bin" or crate_info.is_test or out_binary else None,
1385 ),
Brian Silverman5f6f2762022-08-13 19:30:05 -07001386 coverage_common.instrumented_files_info(
1387 ctx,
1388 dependency_attributes = ["deps", "crate"],
1389 extensions = ["rs"],
1390 source_attributes = ["srcs"],
1391 ),
Brian Silvermancc09f182022-03-09 15:40:20 -08001392 ]
Brian Silverman5f6f2762022-08-13 19:30:05 -07001393
1394 if crate_info.type in ["staticlib", "cdylib"]:
1395 # These rules are not supposed to be depended on by other rust targets, and
1396 # as such they shouldn't provide a CrateInfo. However, one may still want to
1397 # write a rust_test for them, so we provide the CrateInfo wrapped in a provider
1398 # that rust_test understands.
1399 providers.extend([rust_common.test_crate_info(crate = crate_info), dep_info])
1400 else:
1401 providers.extend([crate_info, dep_info])
1402
Brian Silvermancc09f182022-03-09 15:40:20 -08001403 if toolchain.target_arch != "wasm32":
1404 providers += establish_cc_info(ctx, attr, crate_info, toolchain, cc_toolchain, feature_configuration, interface_library)
1405 if pdb_file:
1406 providers.append(OutputGroupInfo(pdb_file = depset([pdb_file])))
1407 if dsym_folder:
1408 providers.append(OutputGroupInfo(dsym_folder = depset([dsym_folder])))
Adam Snaider1c095c92023-07-08 02:09:58 -04001409 if build_metadata:
1410 providers.append(OutputGroupInfo(build_metadata = depset([build_metadata])))
Brian Silvermancc09f182022-03-09 15:40:20 -08001411
1412 return providers
1413
Adam Snaider1c095c92023-07-08 02:09:58 -04001414def _is_no_std(ctx, toolchain, crate_info):
1415 if is_exec_configuration(ctx) or crate_info.is_test:
1416 return False
1417 if toolchain._no_std == "off":
1418 return False
1419 return True
1420
1421def _get_std_and_alloc_info(ctx, toolchain, crate_info):
1422 if is_exec_configuration(ctx):
1423 return toolchain.libstd_and_allocator_ccinfo
1424 if toolchain._experimental_use_global_allocator:
1425 if _is_no_std(ctx, toolchain, crate_info):
1426 return toolchain.nostd_and_global_allocator_cc_info
1427 else:
1428 return toolchain.libstd_and_global_allocator_ccinfo
1429 else:
1430 return toolchain.libstd_and_allocator_ccinfo
1431
Brian Silvermancc09f182022-03-09 15:40:20 -08001432def _is_dylib(dep):
1433 return not bool(dep.static_library or dep.pic_static_library)
1434
Brian Silverman5f6f2762022-08-13 19:30:05 -07001435def _collect_nonstatic_linker_inputs(cc_info):
1436 shared_linker_inputs = []
1437 for linker_input in cc_info.linking_context.linker_inputs.to_list():
1438 dylibs = [
1439 lib
1440 for lib in linker_input.libraries
1441 if _is_dylib(lib)
1442 ]
1443 if dylibs:
1444 shared_linker_inputs.append(cc_common.create_linker_input(
1445 owner = linker_input.owner,
1446 libraries = depset(dylibs),
1447 ))
1448 return shared_linker_inputs
1449
Brian Silvermancc09f182022-03-09 15:40:20 -08001450def establish_cc_info(ctx, attr, crate_info, toolchain, cc_toolchain, feature_configuration, interface_library):
1451 """If the produced crate is suitable yield a CcInfo to allow for interop with cc rules
1452
1453 Args:
1454 ctx (ctx): The rule's context object
1455 attr (struct): Attributes to use in gathering CcInfo
1456 crate_info (CrateInfo): The CrateInfo provider of the target crate
1457 toolchain (rust_toolchain): The current `rust_toolchain`
1458 cc_toolchain (CcToolchainInfo): The current `CcToolchainInfo`
1459 feature_configuration (FeatureConfiguration): Feature configuration to be queried.
1460 interface_library (File): Optional interface library for cdylib crates on Windows.
1461
1462 Returns:
1463 list: A list containing the CcInfo provider
1464 """
1465
1466 # A test will not need to produce CcInfo as nothing can depend on test targets
1467 if crate_info.is_test:
1468 return []
1469
1470 # Only generate CcInfo for particular crate types
1471 if crate_info.type not in ("staticlib", "cdylib", "rlib", "lib"):
1472 return []
1473
1474 # TODO: Remove after some resolution to
1475 # https://github.com/bazelbuild/rules_rust/issues/771
1476 if getattr(attr, "out_binary", False):
1477 return []
1478
1479 if crate_info.type == "staticlib":
1480 library_to_link = cc_common.create_library_to_link(
1481 actions = ctx.actions,
1482 feature_configuration = feature_configuration,
1483 cc_toolchain = cc_toolchain,
1484 static_library = crate_info.output,
1485 # TODO(hlopko): handle PIC/NOPIC correctly
1486 pic_static_library = crate_info.output,
1487 )
1488 elif crate_info.type in ("rlib", "lib"):
1489 # bazel hard-codes a check for endswith((".a", ".pic.a",
1490 # ".lib")) in create_library_to_link, so we work around that
1491 # by creating a symlink to the .rlib with a .a extension.
1492 dot_a = make_static_lib_symlink(ctx.actions, crate_info.output)
1493
1494 # TODO(hlopko): handle PIC/NOPIC correctly
1495 library_to_link = cc_common.create_library_to_link(
1496 actions = ctx.actions,
1497 feature_configuration = feature_configuration,
1498 cc_toolchain = cc_toolchain,
1499 static_library = dot_a,
1500 # TODO(hlopko): handle PIC/NOPIC correctly
1501 pic_static_library = dot_a,
1502 )
1503 elif crate_info.type == "cdylib":
1504 library_to_link = cc_common.create_library_to_link(
1505 actions = ctx.actions,
1506 feature_configuration = feature_configuration,
1507 cc_toolchain = cc_toolchain,
1508 dynamic_library = crate_info.output,
1509 interface_library = interface_library,
1510 )
1511 else:
1512 fail("Unexpected case")
1513
1514 link_input = cc_common.create_linker_input(
1515 owner = ctx.label,
1516 libraries = depset([library_to_link]),
1517 )
1518
1519 linking_context = cc_common.create_linking_context(
1520 # TODO - What to do for no_std?
1521 linker_inputs = depset([link_input]),
1522 )
1523
1524 cc_infos = [
1525 CcInfo(linking_context = linking_context),
1526 toolchain.stdlib_linkflags,
1527 ]
Brian Silverman5f6f2762022-08-13 19:30:05 -07001528
Adam Snaider1c095c92023-07-08 02:09:58 -04001529 # Flattening is okay since crate_info.deps only records direct deps.
1530 for dep in crate_info.deps.to_list():
1531 if dep.cc_info:
Brian Silverman5f6f2762022-08-13 19:30:05 -07001532 # A Rust staticlib or shared library doesn't need to propagate linker inputs
1533 # of its dependencies, except for shared libraries.
1534 if crate_info.type in ["cdylib", "staticlib"]:
Adam Snaider1c095c92023-07-08 02:09:58 -04001535 shared_linker_inputs = _collect_nonstatic_linker_inputs(dep.cc_info)
Brian Silverman5f6f2762022-08-13 19:30:05 -07001536 if shared_linker_inputs:
1537 linking_context = cc_common.create_linking_context(
1538 linker_inputs = depset(shared_linker_inputs),
1539 )
1540 cc_infos.append(CcInfo(linking_context = linking_context))
1541 else:
Adam Snaider1c095c92023-07-08 02:09:58 -04001542 cc_infos.append(dep.cc_info)
Brian Silvermancc09f182022-03-09 15:40:20 -08001543
Adam Snaider1c095c92023-07-08 02:09:58 -04001544 if crate_info.type in ("rlib", "lib"):
1545 libstd_and_allocator_cc_info = _get_std_and_alloc_info(ctx, toolchain, crate_info)
1546 if libstd_and_allocator_cc_info:
1547 # TODO: if we already have an rlib in our deps, we could skip this
1548 cc_infos.append(libstd_and_allocator_cc_info)
Brian Silvermancc09f182022-03-09 15:40:20 -08001549
1550 return [cc_common.merge_cc_infos(cc_infos = cc_infos)]
1551
1552def add_edition_flags(args, crate):
1553 """Adds the Rust edition flag to an arguments object reference
1554
1555 Args:
1556 args (Args): A reference to an Args object
1557 crate (CrateInfo): A CrateInfo provider
1558 """
1559 if crate.edition != "2015":
1560 args.add("--edition={}".format(crate.edition))
1561
1562def _create_extra_input_args(build_info, dep_info):
1563 """Gather additional input arguments from transitive dependencies
1564
1565 Args:
1566 build_info (BuildInfo): The BuildInfo provider from the target Crate's set of inputs.
1567 dep_info (DepInfo): The Depinfo provider form the target Crate's set of inputs.
1568
1569 Returns:
1570 tuple: A tuple of the following items:
1571 - (depset[File]): A list of all build info `OUT_DIR` File objects
1572 - (str): The `OUT_DIR` of the current build info
1573 - (File): An optional generated environment file from a `cargo_build_script` target
1574 - (depset[File]): All direct and transitive build flag files from the current build info.
1575 """
1576 input_files = []
1577
1578 # Arguments to the commandline line wrapper that are going to be used
1579 # to create the final command line
1580 out_dir = None
1581 build_env_file = None
1582 build_flags_files = []
1583
1584 if build_info:
1585 out_dir = build_info.out_dir.path
1586 build_env_file = build_info.rustc_env
1587 build_flags_files.append(build_info.flags)
1588 build_flags_files.append(build_info.link_flags)
1589 input_files.append(build_info.out_dir)
1590 input_files.append(build_info.link_flags)
1591
1592 return (
1593 depset(input_files, transitive = [dep_info.link_search_path_files]),
1594 out_dir,
1595 build_env_file,
1596 depset(build_flags_files, transitive = [dep_info.link_search_path_files]),
1597 )
1598
1599def _compute_rpaths(toolchain, output_dir, dep_info, use_pic):
1600 """Determine the artifact's rpaths relative to the bazel root for runtime linking of shared libraries.
1601
1602 Args:
1603 toolchain (rust_toolchain): The current `rust_toolchain`
1604 output_dir (str): The output directory of the current target
1605 dep_info (DepInfo): The current target's dependency info
1606 use_pic: If set, prefers pic_static_library over static_library.
1607
1608 Returns:
1609 depset: A set of relative paths from the output directory to each dependency
1610 """
1611
1612 # Windows has no rpath equivalent, so always return an empty depset.
Brian Silverman5f6f2762022-08-13 19:30:05 -07001613 # Fuchsia assembles shared libraries during packaging.
Adam Snaider1c095c92023-07-08 02:09:58 -04001614 if toolchain.target_os == "windows" or toolchain.target_os == "fuchsia":
Brian Silvermancc09f182022-03-09 15:40:20 -08001615 return depset([])
1616
1617 dylibs = [
1618 get_preferred_artifact(lib, use_pic)
1619 for linker_input in dep_info.transitive_noncrates.to_list()
1620 for lib in linker_input.libraries
1621 if _is_dylib(lib)
1622 ]
1623 if not dylibs:
1624 return depset([])
1625
1626 # For darwin, dylibs compiled by Bazel will fail to be resolved at runtime
1627 # without a version of Bazel that includes
1628 # https://github.com/bazelbuild/bazel/pull/13427. This is known to not be
1629 # included in Bazel 4.1 and below.
Adam Snaider1c095c92023-07-08 02:09:58 -04001630 if toolchain.target_os != "linux" and toolchain.target_os != "darwin":
Brian Silvermancc09f182022-03-09 15:40:20 -08001631 fail("Runtime linking is not supported on {}, but found {}".format(
Adam Snaider1c095c92023-07-08 02:09:58 -04001632 toolchain.target_os,
Brian Silvermancc09f182022-03-09 15:40:20 -08001633 dep_info.transitive_noncrates,
1634 ))
1635
1636 # Multiple dylibs can be present in the same directory, so deduplicate them.
1637 return depset([
1638 relativize(lib_dir, output_dir)
1639 for lib_dir in _get_dir_names(dylibs)
1640 ])
1641
1642def _get_dir_names(files):
1643 """Returns a list of directory names from the given list of File objects
1644
1645 Args:
1646 files (list): A list of File objects
1647
1648 Returns:
1649 list: A list of directory names for all files
1650 """
1651 dirs = {}
1652 for f in files:
1653 dirs[f.dirname] = None
1654 return dirs.keys()
1655
Brian Silverman5f6f2762022-08-13 19:30:05 -07001656def add_crate_link_flags(args, dep_info, force_all_deps_direct = False, use_metadata = False):
Brian Silvermancc09f182022-03-09 15:40:20 -08001657 """Adds link flags to an Args object reference
1658
1659 Args:
1660 args (Args): An arguments object reference
1661 dep_info (DepInfo): The current target's dependency info
1662 force_all_deps_direct (bool, optional): Whether to pass the transitive rlibs with --extern
1663 to the commandline as opposed to -L.
Brian Silverman5f6f2762022-08-13 19:30:05 -07001664 use_metadata (bool, optional): Build command line arugments using metadata for crates that provide it.
Brian Silvermancc09f182022-03-09 15:40:20 -08001665 """
1666
Brian Silverman5f6f2762022-08-13 19:30:05 -07001667 direct_crates = depset(
1668 transitive = [
1669 dep_info.direct_crates,
1670 dep_info.transitive_crates,
1671 ],
1672 ) if force_all_deps_direct else dep_info.direct_crates
1673
1674 crate_to_link_flags = _crate_to_link_flag_metadata if use_metadata else _crate_to_link_flag
1675 args.add_all(direct_crates, uniquify = True, map_each = crate_to_link_flags)
1676
Brian Silvermancc09f182022-03-09 15:40:20 -08001677 args.add_all(
1678 dep_info.transitive_crates,
1679 map_each = _get_crate_dirname,
1680 uniquify = True,
1681 format_each = "-Ldependency=%s",
1682 )
1683
Brian Silverman5f6f2762022-08-13 19:30:05 -07001684def _crate_to_link_flag_metadata(crate):
1685 """A helper macro used by `add_crate_link_flags` for adding crate link flags to a Arg object
1686
1687 Args:
1688 crate (CrateInfo|AliasableDepInfo): A CrateInfo or an AliasableDepInfo provider
1689
1690 Returns:
1691 list: Link flags for the given provider
1692 """
1693
1694 # This is AliasableDepInfo, we should use the alias as a crate name
1695 if hasattr(crate, "dep"):
1696 name = crate.name
1697 crate_info = crate.dep
1698 else:
1699 name = crate.name
1700 crate_info = crate
1701
1702 lib_or_meta = crate_info.metadata
1703 if not crate_info.metadata:
1704 lib_or_meta = crate_info.output
1705 return ["--extern={}={}".format(name, lib_or_meta.path)]
1706
Brian Silvermancc09f182022-03-09 15:40:20 -08001707def _crate_to_link_flag(crate):
1708 """A helper macro used by `add_crate_link_flags` for adding crate link flags to a Arg object
1709
1710 Args:
1711 crate (CrateInfo|AliasableDepInfo): A CrateInfo or an AliasableDepInfo provider
1712
1713 Returns:
1714 list: Link flags for the given provider
1715 """
1716
1717 # This is AliasableDepInfo, we should use the alias as a crate name
1718 if hasattr(crate, "dep"):
1719 name = crate.name
1720 crate_info = crate.dep
1721 else:
1722 name = crate.name
1723 crate_info = crate
1724 return ["--extern={}={}".format(name, crate_info.output.path)]
1725
1726def _get_crate_dirname(crate):
1727 """A helper macro used by `add_crate_link_flags` for getting the directory name of the current crate's output path
1728
1729 Args:
1730 crate (CrateInfo): A CrateInfo provider from the current rule
1731
1732 Returns:
1733 str: The directory name of the the output File that will be produced.
1734 """
1735 return crate.output.dirname
1736
Adam Snaider1c095c92023-07-08 02:09:58 -04001737def _portable_link_flags(lib, use_pic, ambiguous_libs, get_lib_name, for_windows = False, for_darwin = False, flavor_msvc = False):
Brian Silvermancc09f182022-03-09 15:40:20 -08001738 artifact = get_preferred_artifact(lib, use_pic)
1739 if ambiguous_libs and artifact.path in ambiguous_libs:
1740 artifact = ambiguous_libs[artifact.path]
1741 if lib.static_library or lib.pic_static_library:
Brian Silverman5f6f2762022-08-13 19:30:05 -07001742 # To ensure appropriate linker library argument order, in the presence
1743 # of both native libraries that depend on rlibs and rlibs that depend
1744 # on native libraries, we use an approach where we "sandwich" the
1745 # rust libraries between two similar sections of all of native
1746 # libraries:
1747 # n1 n2 ... r1 r2 ... n1 n2 ...
1748 # A B C
1749 # This way any dependency from a native library to a rust library
1750 # is resolved from A to B, and any dependency from a rust library to
1751 # a native one is resolved from B to C.
1752 # The question of resolving dependencies from a native library from A
1753 # to any rust library is addressed in a different place, where we
1754 # create symlinks to the rlibs, pretending they are native libraries,
1755 # and adding references to these symlinks in the native section A.
1756 # We rely in the behavior of -Clink-arg to put the linker args
1757 # at the end of the linker invocation constructed by rustc.
1758
1759 # We skip adding `-Clink-arg=-l` for libstd and libtest from the standard library, as
1760 # these two libraries are present both as an `.rlib` and a `.so` format.
1761 # On linux, Rustc adds a -Bdynamic to the linker command line before the libraries specified
1762 # with `-Clink-arg`, which leads to us linking against the `.so`s but not putting the
1763 # corresponding value to the runtime library search paths, which results in a
1764 # "cannot open shared object file: No such file or directory" error at exectuion time.
1765 # We can fix this by adding a `-Clink-arg=-Bstatic` on linux, but we don't have that option for
1766 # macos. The proper solution for this issue would be to remove `libtest-{hash}.so` and `libstd-{hash}.so`
1767 # from the toolchain. However, it is not enough to change the toolchain's `rust_std_{...}` filegroups
1768 # here: https://github.com/bazelbuild/rules_rust/blob/a9d5d894ad801002d007b858efd154e503796b9f/rust/private/repository_utils.bzl#L144
1769 # because rustc manages to escape the sandbox and still finds them at linking time.
1770 # We need to modify the repository rules to erase those files completely.
1771 if "lib/rustlib" in artifact.path and (
1772 artifact.basename.startswith("libtest-") or artifact.basename.startswith("libstd-") or
1773 artifact.basename.startswith("test-") or artifact.basename.startswith("std-")
1774 ):
Adam Snaider1c095c92023-07-08 02:09:58 -04001775 return [] if for_darwin else ["-lstatic=%s" % get_lib_name(artifact)]
1776
1777 if for_windows:
1778 if flavor_msvc:
1779 return [
1780 "-lstatic=%s" % get_lib_name(artifact),
1781 "-Clink-arg={}".format(artifact.basename),
1782 ]
1783 else:
1784 return [
1785 "-lstatic=%s" % get_lib_name(artifact),
1786 "-Clink-arg=-l{}".format(artifact.basename),
1787 ]
1788 else:
1789 return [
1790 "-lstatic=%s" % get_lib_name(artifact),
1791 "-Clink-arg=-l{}".format(get_lib_name(artifact)),
1792 ]
Brian Silvermancc09f182022-03-09 15:40:20 -08001793 elif _is_dylib(lib):
1794 return [
1795 "-ldylib=%s" % get_lib_name(artifact),
1796 ]
1797
1798 return []
1799
Adam Snaider1c095c92023-07-08 02:09:58 -04001800def _make_link_flags_windows(linker_input_and_use_pic_and_ambiguous_libs, flavor_msvc):
Brian Silvermancc09f182022-03-09 15:40:20 -08001801 linker_input, use_pic, ambiguous_libs = linker_input_and_use_pic_and_ambiguous_libs
1802 ret = []
1803 for lib in linker_input.libraries:
1804 if lib.alwayslink:
Adam Snaider1c095c92023-07-08 02:09:58 -04001805 if flavor_msvc:
1806 ret.extend(["-C", "link-arg=/WHOLEARCHIVE:%s" % get_preferred_artifact(lib, use_pic).path])
1807 else:
1808 ret.extend([
1809 "-C",
1810 "link-arg=-Wl,--whole-archive",
1811 "-C",
1812 ("link-arg=%s" % get_preferred_artifact(lib, use_pic).path),
1813 "-C",
1814 "link-arg=-Wl,--no-whole-archive",
1815 ])
Brian Silvermancc09f182022-03-09 15:40:20 -08001816 else:
Adam Snaider1c095c92023-07-08 02:09:58 -04001817 ret.extend(_portable_link_flags(lib, use_pic, ambiguous_libs, get_lib_name_for_windows, for_windows = True, flavor_msvc = flavor_msvc))
Brian Silvermancc09f182022-03-09 15:40:20 -08001818 return ret
1819
Adam Snaider1c095c92023-07-08 02:09:58 -04001820def _make_link_flags_windows_msvc(linker_input_and_use_pic_and_ambiguous_libs):
1821 return _make_link_flags_windows(linker_input_and_use_pic_and_ambiguous_libs, flavor_msvc = True)
1822
1823def _make_link_flags_windows_gnu(linker_input_and_use_pic_and_ambiguous_libs):
1824 return _make_link_flags_windows(linker_input_and_use_pic_and_ambiguous_libs, flavor_msvc = False)
1825
Brian Silvermancc09f182022-03-09 15:40:20 -08001826def _make_link_flags_darwin(linker_input_and_use_pic_and_ambiguous_libs):
1827 linker_input, use_pic, ambiguous_libs = linker_input_and_use_pic_and_ambiguous_libs
1828 ret = []
1829 for lib in linker_input.libraries:
1830 if lib.alwayslink:
1831 ret.extend([
1832 "-C",
1833 ("link-arg=-Wl,-force_load,%s" % get_preferred_artifact(lib, use_pic).path),
1834 ])
1835 else:
Adam Snaider1c095c92023-07-08 02:09:58 -04001836 ret.extend(_portable_link_flags(lib, use_pic, ambiguous_libs, get_lib_name_default, for_darwin = True))
Brian Silvermancc09f182022-03-09 15:40:20 -08001837 return ret
1838
1839def _make_link_flags_default(linker_input_and_use_pic_and_ambiguous_libs):
1840 linker_input, use_pic, ambiguous_libs = linker_input_and_use_pic_and_ambiguous_libs
1841 ret = []
1842 for lib in linker_input.libraries:
1843 if lib.alwayslink:
1844 ret.extend([
1845 "-C",
1846 "link-arg=-Wl,--whole-archive",
1847 "-C",
1848 ("link-arg=%s" % get_preferred_artifact(lib, use_pic).path),
1849 "-C",
1850 "link-arg=-Wl,--no-whole-archive",
1851 ])
1852 else:
Adam Snaider1c095c92023-07-08 02:09:58 -04001853 ret.extend(_portable_link_flags(lib, use_pic, ambiguous_libs, get_lib_name_default))
Brian Silvermancc09f182022-03-09 15:40:20 -08001854 return ret
1855
1856def _libraries_dirnames(linker_input_and_use_pic_and_ambiguous_libs):
1857 link_input, use_pic, _ = linker_input_and_use_pic_and_ambiguous_libs
1858
1859 # De-duplicate names.
1860 return depset([get_preferred_artifact(lib, use_pic).dirname for lib in link_input.libraries]).to_list()
1861
Adam Snaider1c095c92023-07-08 02:09:58 -04001862def _add_native_link_flags(args, dep_info, linkstamp_outs, ambiguous_libs, crate_type, toolchain, cc_toolchain, feature_configuration, compilation_mode):
Brian Silvermancc09f182022-03-09 15:40:20 -08001863 """Adds linker flags for all dependencies of the current target.
1864
1865 Args:
1866 args (Args): The Args struct for a ctx.action
1867 dep_info (DepInfo): Dependency Info provider
1868 linkstamp_outs (list): Linkstamp outputs of native dependencies
1869 ambiguous_libs (dict): Ambiguous libs, see `_disambiguate_libs`
1870 crate_type: Crate type of the current target
1871 toolchain (rust_toolchain): The current `rust_toolchain`
1872 cc_toolchain (CcToolchainInfo): The current `cc_toolchain`
1873 feature_configuration (FeatureConfiguration): feature configuration to use with cc_toolchain
Adam Snaider1c095c92023-07-08 02:09:58 -04001874 compilation_mode (bool): The compilation mode for this build.
Brian Silvermancc09f182022-03-09 15:40:20 -08001875 """
1876 if crate_type in ["lib", "rlib"]:
1877 return
1878
Adam Snaider1c095c92023-07-08 02:09:58 -04001879 use_pic = _should_use_pic(cc_toolchain, feature_configuration, crate_type, compilation_mode)
Brian Silvermancc09f182022-03-09 15:40:20 -08001880
Adam Snaider1c095c92023-07-08 02:09:58 -04001881 if toolchain.target_os == "windows":
1882 make_link_flags = _make_link_flags_windows_msvc if toolchain.target_triple.abi == "msvc" else _make_link_flags_windows_gnu
Brian Silverman5f6f2762022-08-13 19:30:05 -07001883 get_lib_name = get_lib_name_for_windows
Adam Snaider1c095c92023-07-08 02:09:58 -04001884 elif toolchain.target_os.startswith(("mac", "darwin", "ios")):
Brian Silvermancc09f182022-03-09 15:40:20 -08001885 make_link_flags = _make_link_flags_darwin
Brian Silverman5f6f2762022-08-13 19:30:05 -07001886 get_lib_name = get_lib_name_default
Brian Silvermancc09f182022-03-09 15:40:20 -08001887 else:
1888 make_link_flags = _make_link_flags_default
Brian Silverman5f6f2762022-08-13 19:30:05 -07001889 get_lib_name = get_lib_name_default
Brian Silvermancc09f182022-03-09 15:40:20 -08001890
1891 # TODO(hlopko): Remove depset flattening by using lambdas once we are on >=Bazel 5.0
1892 args_and_pic_and_ambiguous_libs = [(arg, use_pic, ambiguous_libs) for arg in dep_info.transitive_noncrates.to_list()]
1893 args.add_all(args_and_pic_and_ambiguous_libs, map_each = _libraries_dirnames, uniquify = True, format_each = "-Lnative=%s")
1894 if ambiguous_libs:
1895 # If there are ambiguous libs, the disambiguation symlinks to them are
1896 # all created in the same directory. Add it to the library search path.
1897 ambiguous_libs_dirname = ambiguous_libs.values()[0].dirname
1898 args.add("-Lnative={}".format(ambiguous_libs_dirname))
1899
1900 args.add_all(args_and_pic_and_ambiguous_libs, map_each = make_link_flags)
1901
1902 for linkstamp_out in linkstamp_outs:
1903 args.add_all(["-C", "link-arg=%s" % linkstamp_out.path])
1904
1905 if crate_type in ["dylib", "cdylib"]:
1906 # For shared libraries we want to link C++ runtime library dynamically
1907 # (for example libstdc++.so or libc++.so).
1908 args.add_all(
1909 cc_toolchain.dynamic_runtime_lib(feature_configuration = feature_configuration),
1910 map_each = _get_dirname,
1911 format_each = "-Lnative=%s",
1912 )
1913 args.add_all(
1914 cc_toolchain.dynamic_runtime_lib(feature_configuration = feature_configuration),
1915 map_each = get_lib_name,
1916 format_each = "-ldylib=%s",
1917 )
1918 else:
1919 # For all other crate types we want to link C++ runtime library statically
1920 # (for example libstdc++.a or libc++.a).
1921 args.add_all(
1922 cc_toolchain.static_runtime_lib(feature_configuration = feature_configuration),
1923 map_each = _get_dirname,
1924 format_each = "-Lnative=%s",
1925 )
1926 args.add_all(
1927 cc_toolchain.static_runtime_lib(feature_configuration = feature_configuration),
1928 map_each = get_lib_name,
1929 format_each = "-lstatic=%s",
1930 )
1931
1932def _get_dirname(file):
1933 """A helper function for `_add_native_link_flags`.
1934
1935 Args:
1936 file (File): The target file
1937
1938 Returns:
1939 str: Directory name of `file`
1940 """
1941 return file.dirname
1942
Adam Snaider1c095c92023-07-08 02:09:58 -04001943def _add_per_crate_rustc_flags(ctx, args, crate_info, per_crate_rustc_flags):
1944 """Adds matching per-crate rustc flags to an arguments object reference
1945
1946 Args:
1947 ctx (ctx): The source rule's context object
1948 args (Args): A reference to an Args object
1949 crate_info (CrateInfo): A CrateInfo provider
1950 per_crate_rustc_flags (list): A list of per_crate_rustc_flag values
1951 """
1952 for per_crate_rustc_flag in per_crate_rustc_flags:
1953 at_index = per_crate_rustc_flag.find("@")
1954 if at_index == -1:
1955 fail("per_crate_rustc_flag '{}' does not follow the expected format: prefix_filter@flag".format(per_crate_rustc_flag))
1956
1957 prefix_filter = per_crate_rustc_flag[:at_index]
1958 flag = per_crate_rustc_flag[at_index + 1:]
1959 if not flag:
1960 fail("per_crate_rustc_flag '{}' does not follow the expected format: prefix_filter@flag".format(per_crate_rustc_flag))
1961
1962 label_string = str(ctx.label)
1963 label = label_string[1:] if label_string.startswith("@//") else label_string
1964 execution_path = crate_info.root.path
1965
1966 if label.startswith(prefix_filter) or execution_path.startswith(prefix_filter):
1967 args.add(flag)
1968
Brian Silvermancc09f182022-03-09 15:40:20 -08001969def _error_format_impl(ctx):
1970 """Implementation of the `error_format` rule
1971
1972 Args:
1973 ctx (ctx): The rule's context object
1974
1975 Returns:
1976 list: A list containing the ErrorFormatInfo provider
1977 """
1978 raw = ctx.build_setting_value
1979 if raw not in _error_format_values:
1980 fail("{} expected a value in `{}` but got `{}`".format(
1981 ctx.label,
1982 _error_format_values,
1983 raw,
1984 ))
1985 return [ErrorFormatInfo(error_format = raw)]
1986
1987error_format = rule(
1988 doc = (
1989 "Change the [--error-format](https://doc.rust-lang.org/rustc/command-line-arguments.html#option-error-format) " +
1990 "flag from the command line with `--@rules_rust//:error_format`. See rustc documentation for valid values."
1991 ),
1992 implementation = _error_format_impl,
1993 build_setting = config.string(flag = True),
1994)
1995
1996def _extra_rustc_flags_impl(ctx):
1997 return ExtraRustcFlagsInfo(extra_rustc_flags = ctx.build_setting_value)
1998
1999extra_rustc_flags = rule(
2000 doc = (
2001 "Add additional rustc_flags from the command line with `--@rules_rust//:extra_rustc_flags`. " +
2002 "This flag should only be used for flags that need to be applied across the entire build. For options that " +
2003 "apply to individual crates, use the rustc_flags attribute on the individual crate's rule instead. NOTE: " +
2004 "These flags not applied to the exec configuration (proc-macros, cargo_build_script, etc); " +
2005 "use `--@rules_rust//:extra_exec_rustc_flags` to apply flags to the exec configuration."
2006 ),
2007 implementation = _extra_rustc_flags_impl,
2008 build_setting = config.string_list(flag = True),
2009)
2010
Brian Silverman5f6f2762022-08-13 19:30:05 -07002011def _extra_rustc_flag_impl(ctx):
2012 return ExtraRustcFlagsInfo(extra_rustc_flags = [f for f in ctx.build_setting_value if f != ""])
2013
2014extra_rustc_flag = rule(
2015 doc = (
2016 "Add additional rustc_flag from the command line with `--@rules_rust//:extra_rustc_flag`. " +
2017 "Multiple uses are accumulated and appended after the extra_rustc_flags."
2018 ),
2019 implementation = _extra_rustc_flag_impl,
2020 build_setting = config.string(flag = True, allow_multiple = True),
2021)
2022
Brian Silvermancc09f182022-03-09 15:40:20 -08002023def _extra_exec_rustc_flags_impl(ctx):
2024 return ExtraExecRustcFlagsInfo(extra_exec_rustc_flags = ctx.build_setting_value)
2025
2026extra_exec_rustc_flags = rule(
2027 doc = (
2028 "Add additional rustc_flags in the exec configuration from the command line with `--@rules_rust//:extra_exec_rustc_flags`. " +
2029 "This flag should only be used for flags that need to be applied across the entire build. " +
2030 "These flags only apply to the exec configuration (proc-macros, cargo_build_script, etc)."
2031 ),
2032 implementation = _extra_exec_rustc_flags_impl,
2033 build_setting = config.string_list(flag = True),
2034)
Brian Silverman5f6f2762022-08-13 19:30:05 -07002035
2036def _extra_exec_rustc_flag_impl(ctx):
2037 return ExtraExecRustcFlagsInfo(extra_exec_rustc_flags = [f for f in ctx.build_setting_value if f != ""])
2038
2039extra_exec_rustc_flag = rule(
2040 doc = (
2041 "Add additional rustc_flags in the exec configuration from the command line with `--@rules_rust//:extra_exec_rustc_flag`. " +
2042 "Multiple uses are accumulated and appended after the extra_exec_rustc_flags."
2043 ),
2044 implementation = _extra_exec_rustc_flag_impl,
2045 build_setting = config.string(flag = True, allow_multiple = True),
2046)
Adam Snaider1c095c92023-07-08 02:09:58 -04002047
2048def _per_crate_rustc_flag_impl(ctx):
2049 return PerCrateRustcFlagsInfo(per_crate_rustc_flags = [f for f in ctx.build_setting_value if f != ""])
2050
2051per_crate_rustc_flag = rule(
2052 doc = (
2053 "Add additional rustc_flag to matching crates from the command line with `--@rules_rust//:experimental_per_crate_rustc_flag`. " +
2054 "The expected flag format is prefix_filter@flag, where any crate with a label or execution path starting with the prefix filter will be built with the given flag." +
2055 "The label matching uses the canonical form of the label (i.e //package:label_name)." +
2056 "The execution path is the relative path to your workspace directory including the base name (including extension) of the crate root." +
2057 "This flag is only applied to the exec configuration (proc-macros, cargo_build_script, etc)." +
2058 "Multiple uses are accumulated."
2059 ),
2060 implementation = _per_crate_rustc_flag_impl,
2061 build_setting = config.string(flag = True, allow_multiple = True),
2062)
2063
2064def _no_std_impl(ctx):
2065 value = str(ctx.attr._no_std[BuildSettingInfo].value)
2066 if is_exec_configuration(ctx):
2067 return [config_common.FeatureFlagInfo(value = "off")]
2068 return [config_common.FeatureFlagInfo(value = value)]
2069
2070no_std = rule(
2071 doc = (
2072 "No std; we need this so that we can distinguish between host and exec"
2073 ),
2074 attrs = {
2075 "_no_std": attr.label(default = "//:no_std"),
2076 },
2077 implementation = _no_std_impl,
2078)