blob: 596c10da59c372b14fc8d392f709473b9d5decfa [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
17load(
18 "@bazel_tools//tools/build_defs/cc:action_names.bzl",
19 "CPP_LINK_EXECUTABLE_ACTION_NAME",
20)
21load("//rust/private:common.bzl", "rust_common")
22load("//rust/private:providers.bzl", _BuildInfo = "BuildInfo")
23load("//rust/private:stamp.bzl", "is_stamping_enabled")
24load(
25 "//rust/private:utils.bzl",
26 "abs",
27 "expand_dict_value_locations",
28 "expand_list_element_locations",
29 "find_cc_toolchain",
30 "get_lib_name",
31 "get_preferred_artifact",
32 "is_exec_configuration",
33 "make_static_lib_symlink",
34 "relativize",
35)
36
37BuildInfo = _BuildInfo
38
39AliasableDepInfo = provider(
40 doc = "A provider mapping an alias name to a Crate's information.",
41 fields = {
42 "dep": "CrateInfo",
43 "name": "str",
44 },
45)
46
47_error_format_values = ["human", "json", "short"]
48
49ErrorFormatInfo = provider(
50 doc = "Set the --error-format flag for all rustc invocations",
51 fields = {"error_format": "(string) [" + ", ".join(_error_format_values) + "]"},
52)
53
54ExtraRustcFlagsInfo = provider(
55 doc = "Pass each value as an additional flag to non-exec rustc invocations",
56 fields = {"extra_rustc_flags": "List[string] Extra flags to pass to rustc in non-exec configuration"},
57)
58
59ExtraExecRustcFlagsInfo = provider(
60 doc = "Pass each value as an additional flag to exec rustc invocations",
61 fields = {"extra_exec_rustc_flags": "List[string] Extra flags to pass to rustc in exec configuration"},
62)
63
64def _get_rustc_env(attr, toolchain, crate_name):
65 """Gathers rustc environment variables
66
67 Args:
68 attr (struct): The current target's attributes
69 toolchain (rust_toolchain): The current target's rust toolchain context
70 crate_name (str): The name of the crate to be compiled
71
72 Returns:
73 dict: Rustc environment variables
74 """
75 version = attr.version if hasattr(attr, "version") else "0.0.0"
76 major, minor, patch = version.split(".", 2)
77 if "-" in patch:
78 patch, pre = patch.split("-", 1)
79 else:
80 pre = ""
81 return {
82 "CARGO_CFG_TARGET_ARCH": toolchain.target_arch,
83 "CARGO_CFG_TARGET_OS": toolchain.os,
84 "CARGO_CRATE_NAME": crate_name,
85 "CARGO_PKG_AUTHORS": "",
86 "CARGO_PKG_DESCRIPTION": "",
87 "CARGO_PKG_HOMEPAGE": "",
88 "CARGO_PKG_NAME": attr.name,
89 "CARGO_PKG_VERSION": version,
90 "CARGO_PKG_VERSION_MAJOR": major,
91 "CARGO_PKG_VERSION_MINOR": minor,
92 "CARGO_PKG_VERSION_PATCH": patch,
93 "CARGO_PKG_VERSION_PRE": pre,
94 }
95
96def get_compilation_mode_opts(ctx, toolchain):
97 """Gathers rustc flags for the current compilation mode (opt/debug)
98
99 Args:
100 ctx (ctx): The current rule's context object
101 toolchain (rust_toolchain): The current rule's `rust_toolchain`
102
103 Returns:
104 struct: See `_rust_toolchain_impl` for more details
105 """
106 comp_mode = ctx.var["COMPILATION_MODE"]
107 if not comp_mode in toolchain.compilation_mode_opts:
108 fail("Unrecognized compilation mode {} for toolchain.".format(comp_mode))
109
110 return toolchain.compilation_mode_opts[comp_mode]
111
112def _are_linkstamps_supported(feature_configuration, has_grep_includes):
113 # Are linkstamps supported by the C++ toolchain?
114 return (cc_common.is_enabled(feature_configuration = feature_configuration, feature_name = "linkstamps") and
115 # Is Bazel recent enough to support Starlark linkstamps?
116 hasattr(cc_common, "register_linkstamp_compile_action") and
117 # The current rule doesn't define _grep_includes attribute; this
118 # attribute is required for compiling linkstamps.
119 has_grep_includes)
120
121def _should_use_pic(cc_toolchain, feature_configuration, crate_type):
122 if crate_type in ("cdylib" or "dylib"):
123 return cc_toolchain.needs_pic_for_dynamic_libraries(feature_configuration = feature_configuration)
124 return False
125
126def collect_deps(
127 deps,
128 proc_macro_deps,
129 aliases,
130 are_linkstamps_supported = False):
131 """Walks through dependencies and collects the transitive dependencies.
132
133 Args:
134 deps (list): The deps from ctx.attr.deps.
135 proc_macro_deps (list): The proc_macro deps from ctx.attr.proc_macro_deps.
136 aliases (dict): A dict mapping aliased targets to their actual Crate information.
137 are_linkstamps_supported (bool): Whether the current rule and the toolchain support building linkstamps..
138
139 Returns:
140 tuple: Returns a tuple of:
141 DepInfo,
142 BuildInfo,
143 linkstamps (depset[CcLinkstamp]): A depset of CcLinkstamps that need to be compiled and linked into all linked binaries.
144
145 """
146 direct_crates = []
147 transitive_crates = []
148 transitive_noncrates = []
149 transitive_build_infos = []
150 transitive_link_search_paths = []
151 build_info = None
152 linkstamps = []
153 transitive_crate_outputs = []
154
155 aliases = {k.label: v for k, v in aliases.items()}
156 for dep in depset(transitive = [deps, proc_macro_deps]).to_list():
157 (crate_info, dep_info) = _get_crate_and_dep_info(dep)
158 cc_info = _get_cc_info(dep)
159 dep_build_info = _get_build_info(dep)
160
161 if cc_info and are_linkstamps_supported:
162 linkstamps.append(cc_info.linking_context.linkstamps())
163
164 if crate_info:
165 # This dependency is a rust_library
166
167 # When crate_info.owner is set, we use it. When the dep type is Target we get the
168 # label from dep.label
169 owner = getattr(crate_info, "owner", dep.label if type(dep) == "Target" else None)
170
171 direct_crates.append(AliasableDepInfo(
172 name = aliases.get(owner, crate_info.name),
173 dep = crate_info,
174 ))
175
176 transitive_crates.append(depset([crate_info], transitive = [dep_info.transitive_crates]))
177 transitive_crate_outputs.append(
178 depset(
179 [crate_info.output],
180 transitive = [dep_info.transitive_crate_outputs],
181 ),
182 )
183 transitive_noncrates.append(dep_info.transitive_noncrates)
184 transitive_build_infos.append(dep_info.transitive_build_infos)
185 transitive_link_search_paths.append(dep_info.link_search_path_files)
186
187 elif cc_info:
188 # This dependency is a cc_library
189 transitive_noncrates.append(cc_info.linking_context.linker_inputs)
190 elif dep_build_info:
191 if build_info:
192 fail("Several deps are providing build information, " +
193 "only one is allowed in the dependencies")
194 build_info = dep_build_info
195 transitive_build_infos.append(depset([build_info]))
196 transitive_link_search_paths.append(depset([build_info.link_search_paths]))
197 else:
198 fail("rust targets can only depend on rust_library, rust_*_library or cc_library " +
199 "targets.")
200
201 transitive_crates_depset = depset(transitive = transitive_crates)
202
203 return (
204 rust_common.dep_info(
205 direct_crates = depset(direct_crates),
206 transitive_crates = transitive_crates_depset,
207 transitive_noncrates = depset(
208 transitive = transitive_noncrates,
209 order = "topological", # dylib link flag ordering matters.
210 ),
211 transitive_crate_outputs = depset(transitive = transitive_crate_outputs),
212 transitive_build_infos = depset(transitive = transitive_build_infos),
213 link_search_path_files = depset(transitive = transitive_link_search_paths),
214 dep_env = build_info.dep_env if build_info else None,
215 ),
216 build_info,
217 depset(transitive = linkstamps),
218 )
219
220def _collect_libs_from_linker_inputs(linker_inputs, use_pic):
221 # TODO: We could let the user choose how to link, instead of always preferring to link static libraries.
222 return [
223 get_preferred_artifact(lib, use_pic)
224 for li in linker_inputs
225 for lib in li.libraries
226 ]
227
228def _get_crate_and_dep_info(dep):
229 if type(dep) == "Target" and rust_common.crate_info in dep:
230 return (dep[rust_common.crate_info], dep[rust_common.dep_info])
231 elif type(dep) == "struct" and hasattr(dep, "crate_info"):
232 return (dep.crate_info, dep.dep_info)
233 return (None, None)
234
235def _get_cc_info(dep):
236 if type(dep) == "Target" and CcInfo in dep:
237 return dep[CcInfo]
238 elif type(dep) == "struct" and hasattr(dep, "cc_info"):
239 return dep.cc_info
240 return None
241
242def _get_build_info(dep):
243 if type(dep) == "Target" and BuildInfo in dep:
244 return dep[BuildInfo]
245 elif type(dep) == "struct" and hasattr(dep, "build_info"):
246 return dep.build_info
247 return None
248
249def get_cc_user_link_flags(ctx):
250 """Get the current target's linkopt flags
251
252 Args:
253 ctx (ctx): The current rule's context object
254
255 Returns:
256 depset: The flags passed to Bazel by --linkopt option.
257 """
258 return ctx.fragments.cpp.linkopts
259
260def get_linker_and_args(ctx, attr, cc_toolchain, feature_configuration, rpaths):
261 """Gathers cc_common linker information
262
263 Args:
264 ctx (ctx): The current target's context object
265 attr (struct): Attributes to use in gathering linker args
266 cc_toolchain (CcToolchain): cc_toolchain for which we are creating build variables.
267 feature_configuration (FeatureConfiguration): Feature configuration to be queried.
268 rpaths (depset): Depset of directories where loader will look for libraries at runtime.
269
270
271 Returns:
272 tuple: A tuple of the following items:
273 - (str): The tool path for given action.
274 - (sequence): A flattened command line flags for given action.
275 - (dict): Environment variables to be set for given action.
276 """
277 user_link_flags = get_cc_user_link_flags(ctx)
278
279 # Add linkopt's from dependencies. This includes linkopts from transitive
280 # dependencies since they get merged up.
281 for dep in getattr(attr, "deps", []):
282 if CcInfo in dep and dep[CcInfo].linking_context:
283 for linker_input in dep[CcInfo].linking_context.linker_inputs.to_list():
284 for flag in linker_input.user_link_flags:
285 user_link_flags.append(flag)
286 link_variables = cc_common.create_link_variables(
287 feature_configuration = feature_configuration,
288 cc_toolchain = cc_toolchain,
289 is_linking_dynamic_library = False,
290 runtime_library_search_directories = rpaths,
291 user_link_flags = user_link_flags,
292 )
293 link_args = cc_common.get_memory_inefficient_command_line(
294 feature_configuration = feature_configuration,
295 action_name = CPP_LINK_EXECUTABLE_ACTION_NAME,
296 variables = link_variables,
297 )
298 link_env = cc_common.get_environment_variables(
299 feature_configuration = feature_configuration,
300 action_name = CPP_LINK_EXECUTABLE_ACTION_NAME,
301 variables = link_variables,
302 )
303 ld = cc_common.get_tool_for_action(
304 feature_configuration = feature_configuration,
305 action_name = CPP_LINK_EXECUTABLE_ACTION_NAME,
306 )
307
308 return ld, link_args, link_env
309
310def _process_build_scripts(
311 build_info,
312 dep_info,
313 compile_inputs):
314 """Gathers the outputs from a target's `cargo_build_script` action.
315
316 Args:
317 build_info (BuildInfo): The target Build's dependency info.
318 dep_info (DepInfo): The Depinfo provider form the target Crate's set of inputs.
319 compile_inputs (depset): A set of all files that will participate in the build.
320
321 Returns:
322 tuple: A tuple: A tuple of the following items:
323 - (depset[File]): A list of all build info `OUT_DIR` File objects
324 - (str): The `OUT_DIR` of the current build info
325 - (File): An optional path to a generated environment file from a `cargo_build_script` target
326 - (depset[File]): All direct and transitive build flags from the current build info.
327 """
328 extra_inputs, out_dir, build_env_file, build_flags_files = _create_extra_input_args(build_info, dep_info)
329 compile_inputs = depset(transitive = [extra_inputs, compile_inputs])
330 return compile_inputs, out_dir, build_env_file, build_flags_files
331
332def _symlink_for_ambiguous_lib(actions, toolchain, crate_info, lib):
333 """Constructs a disambiguating symlink for a library dependency.
334
335 Args:
336 actions (Actions): The rule's context actions object.
337 toolchain: The Rust toolchain object.
338 crate_info (CrateInfo): The target crate's info.
339 lib (File): The library to symlink to.
340
341 Returns:
342 (File): The disambiguating symlink for the library.
343 """
344 # FIXME: Once the relative order part of the native-link-modifiers rustc
345 # feature is stable, we should be able to eliminate the need to construct
346 # symlinks by passing the full paths to the libraries.
347 # https://github.com/rust-lang/rust/issues/81490.
348
349 # Take the absolute value of hash() since it could be negative.
350 path_hash = abs(hash(lib.path))
351 lib_name = get_lib_name(lib)
352
353 prefix = "lib"
354 extension = ".a"
355 if toolchain.os.startswith("windows"):
356 prefix = ""
357 extension = ".lib"
358
359 # Ensure the symlink follows the lib<name>.a pattern on Unix-like platforms
360 # or <name>.lib on Windows.
361 # Add a hash of the original library path to disambiguate libraries with the same basename.
362 symlink_name = "{}{}-{}{}".format(prefix, lib_name, path_hash, extension)
363
364 # Add the symlink to a target crate-specific _ambiguous_libs/ subfolder,
365 # to avoid possible collisions with sibling crates that may depend on the
366 # same ambiguous libraries.
367 symlink = actions.declare_file("_ambiguous_libs/" + crate_info.output.basename + "/" + symlink_name)
368 actions.symlink(
369 output = symlink,
370 target_file = lib,
371 progress_message = "Creating symlink to ambiguous lib: {}".format(lib.path),
372 )
373 return symlink
374
375def _disambiguate_libs(actions, toolchain, crate_info, dep_info, use_pic):
376 """Constructs disambiguating symlinks for ambiguous library dependencies.
377
378 The symlinks are all created in a _ambiguous_libs/ subfolder specific to
379 the target crate to avoid possible collisions with sibling crates that may
380 depend on the same ambiguous libraries.
381
382 Args:
383 actions (Actions): The rule's context actions object.
384 toolchain: The Rust toolchain object.
385 crate_info (CrateInfo): The target crate's info.
386 dep_info: (DepInfo): The target crate's dependency info.
387 use_pic: (boolean): Whether the build should use PIC.
388
389 Returns:
390 dict[String, File]: A mapping from ambiguous library paths to their
391 disambiguating symlink.
392 """
393 # FIXME: Once the relative order part of the native-link-modifiers rustc
394 # feature is stable, we should be able to eliminate the need to construct
395 # symlinks by passing the full paths to the libraries.
396 # https://github.com/rust-lang/rust/issues/81490.
397
398 # A dictionary from file paths of ambiguous libraries to the corresponding
399 # symlink.
400 ambiguous_libs = {}
401
402 # A dictionary maintaining a mapping from preferred library name to the
403 # last visited artifact with that name.
404 visited_libs = {}
405 for link_input in dep_info.transitive_noncrates.to_list():
406 for lib in link_input.libraries:
407 # FIXME: Dynamic libs are not disambiguated right now, there are
408 # cases where those have a non-standard name with version (e.g.,
409 # //test/unit/versioned_libs). We hope that the link modifiers
410 # stabilization will come before we need to make this work.
411 if _is_dylib(lib):
412 continue
413 artifact = get_preferred_artifact(lib, use_pic)
414 name = get_lib_name(artifact)
415
416 # On Linux-like platforms, normally library base names start with
417 # `lib`, following the pattern `lib[name].(a|lo)` and we pass
418 # -lstatic=name.
419 # On Windows, the base name looks like `name.lib` and we pass
420 # -lstatic=name.
421 # FIXME: Under the native-link-modifiers unstable rustc feature,
422 # we could use -lstatic:+verbatim instead.
423 needs_symlink_to_standardize_name = (
424 (toolchain.os.startswith("linux") or toolchain.os.startswith("mac") or toolchain.os.startswith("darwin")) and
425 artifact.basename.endswith(".a") and not artifact.basename.startswith("lib")
426 ) or (
427 toolchain.os.startswith("windows") and not artifact.basename.endswith(".lib")
428 )
429
430 # Detect cases where we need to disambiguate library dependencies
431 # by constructing symlinks.
432 if (
433 needs_symlink_to_standardize_name or
434 # We have multiple libraries with the same name.
435 (name in visited_libs and visited_libs[name].path != artifact.path)
436 ):
437 # Disambiguate the previously visited library (if we just detected
438 # that it is ambiguous) and the current library.
439 if name in visited_libs:
440 old_path = visited_libs[name].path
441 if old_path not in ambiguous_libs:
442 ambiguous_libs[old_path] = _symlink_for_ambiguous_lib(actions, toolchain, crate_info, visited_libs[name])
443 ambiguous_libs[artifact.path] = _symlink_for_ambiguous_lib(actions, toolchain, crate_info, artifact)
444
445 visited_libs[name] = artifact
446 return ambiguous_libs
447
448def collect_inputs(
449 ctx,
450 file,
451 files,
452 linkstamps,
453 toolchain,
454 cc_toolchain,
455 feature_configuration,
456 crate_info,
457 dep_info,
458 build_info,
459 stamp = False):
460 """Gather's the inputs and required input information for a rustc action
461
462 Args:
463 ctx (ctx): The rule's context object.
464 file (struct): A struct containing files defined in label type attributes marked as `allow_single_file`.
465 files (list): A list of all inputs (`ctx.files`).
466 linkstamps (depset): A depset of CcLinkstamps that need to be compiled and linked into all linked binaries.
467 toolchain (rust_toolchain): The current `rust_toolchain`.
468 cc_toolchain (CcToolchainInfo): The current `cc_toolchain`.
469 feature_configuration (FeatureConfiguration): Feature configuration to be queried.
470 crate_info (CrateInfo): The Crate information of the crate to process build scripts for.
471 dep_info (DepInfo): The target Crate's dependency information.
472 build_info (BuildInfo): The target Crate's build settings.
473 stamp (bool, optional): Whether or not workspace status stamping is enabled. For more details see
474 https://docs.bazel.build/versions/main/user-manual.html#flag--stamp
475
476 Returns:
477 tuple: A tuple: A tuple of the following items:
478 - (list): A list of all build info `OUT_DIR` File objects
479 - (str): The `OUT_DIR` of the current build info
480 - (File): An optional path to a generated environment file from a `cargo_build_script` target
481 - (depset[File]): All direct and transitive build flag files from the current build info
482 - (list[File]): Linkstamp outputs
483 - (dict[String, File]): Ambiguous libs, see `_disambiguate_libs`.
484 """
485 linker_script = getattr(file, "linker_script") if hasattr(file, "linker_script") else None
486
487 linker_depset = cc_toolchain.all_files
488
489 use_pic = _should_use_pic(cc_toolchain, feature_configuration, crate_info.type)
490
491 # Pass linker inputs only for linking-like actions, not for example where
492 # the output is rlib. This avoids quadratic behavior where transitive noncrates are
493 # flattened on each transitive rust_library dependency.
494 additional_transitive_inputs = []
495 ambiguous_libs = {}
496 if crate_info.type in ("staticlib", "proc-macro"):
497 additional_transitive_inputs = _collect_libs_from_linker_inputs(
498 dep_info.transitive_noncrates.to_list(),
499 use_pic,
500 )
501 elif crate_info.type in ("bin", "dylib", "cdylib"):
502 linker_inputs = dep_info.transitive_noncrates.to_list()
503 ambiguous_libs = _disambiguate_libs(ctx.actions, toolchain, crate_info, dep_info, use_pic)
504 additional_transitive_inputs = _collect_libs_from_linker_inputs(linker_inputs, use_pic) + [
505 additional_input
506 for linker_input in linker_inputs
507 for additional_input in linker_input.additional_inputs
508 ] + ambiguous_libs.values()
509
510 # Compute linkstamps. Use the inputs of the binary as inputs to the
511 # linkstamp action to ensure linkstamps are rebuilt whenever binary inputs
512 # change.
513 linkstamp_outs = []
514
515 nolinkstamp_compile_inputs = depset(
516 getattr(files, "data", []) +
517 ([build_info.rustc_env, build_info.flags] if build_info else []) +
518 ([toolchain.target_json] if toolchain.target_json else []) +
519 ([] if linker_script == None else [linker_script]),
520 transitive = [
521 linker_depset,
522 crate_info.srcs,
523 dep_info.transitive_crate_outputs,
524 depset(additional_transitive_inputs),
525 crate_info.compile_data,
526 toolchain.all_files,
527 ],
528 )
529
530 if crate_info.type in ("bin", "cdylib"):
531 # There is no other way to register an action for each member of a depset than
532 # flattening the depset as of 2021-10-12. Luckily, usually there is only one linkstamp
533 # in a build, and we only flatten the list on binary targets that perform transitive linking,
534 # so it's extremely unlikely that this call to `to_list()` will ever be a performance
535 # problem.
536 for linkstamp in linkstamps.to_list():
537 # The linkstamp output path is based on the binary crate
538 # name and the input linkstamp path. This is to disambiguate
539 # the linkstamp outputs produced by multiple binary crates
540 # that depend on the same linkstamp. We use the same pattern
541 # for the output name as the one used by native cc rules.
542 out_name = "_objs/" + crate_info.output.basename + "/" + linkstamp.file().path[:-len(linkstamp.file().extension)] + "o"
543 linkstamp_out = ctx.actions.declare_file(out_name)
544 linkstamp_outs.append(linkstamp_out)
545 cc_common.register_linkstamp_compile_action(
546 actions = ctx.actions,
547 cc_toolchain = cc_toolchain,
548 feature_configuration = feature_configuration,
549 grep_includes = ctx.file._grep_includes,
550 source_file = linkstamp.file(),
551 output_file = linkstamp_out,
552 compilation_inputs = linkstamp.hdrs(),
553 inputs_for_validation = nolinkstamp_compile_inputs,
554 label_replacement = str(ctx.label),
555 output_replacement = crate_info.output.path,
556 )
557
558 # If stamping is enabled include the volatile status info file
559 stamp_info = [ctx.version_file] if stamp else []
560
561 compile_inputs = depset(
562 linkstamp_outs + stamp_info,
563 transitive = [
564 nolinkstamp_compile_inputs,
565 ],
566 )
567
568 build_env_files = getattr(files, "rustc_env_files", [])
569 compile_inputs, out_dir, build_env_file, build_flags_files = _process_build_scripts(build_info, dep_info, compile_inputs)
570 if build_env_file:
571 build_env_files = [f for f in build_env_files] + [build_env_file]
572 compile_inputs = depset(build_env_files, transitive = [compile_inputs])
573
574 return compile_inputs, out_dir, build_env_files, build_flags_files, linkstamp_outs, ambiguous_libs
575
576def construct_arguments(
577 ctx,
578 attr,
579 file,
580 toolchain,
581 tool_path,
582 cc_toolchain,
583 feature_configuration,
584 crate_info,
585 dep_info,
586 linkstamp_outs,
587 ambiguous_libs,
588 output_hash,
589 rust_flags,
590 out_dir,
591 build_env_files,
592 build_flags_files,
593 emit = ["dep-info", "link"],
594 force_all_deps_direct = False,
595 force_link = False,
596 stamp = False,
597 remap_path_prefix = "."):
598 """Builds an Args object containing common rustc flags
599
600 Args:
601 ctx (ctx): The rule's context object
602 attr (struct): The attributes for the target. These may be different from ctx.attr in an aspect context.
603 file (struct): A struct containing files defined in label type attributes marked as `allow_single_file`.
604 toolchain (rust_toolchain): The current target's `rust_toolchain`
605 tool_path (str): Path to rustc
606 cc_toolchain (CcToolchain): The CcToolchain for the current target.
607 feature_configuration (FeatureConfiguration): Class used to construct command lines from CROSSTOOL features.
608 crate_info (CrateInfo): The CrateInfo provider of the target crate
609 dep_info (DepInfo): The DepInfo provider of the target crate
610 linkstamp_outs (list): Linkstamp outputs of native dependencies
611 ambiguous_libs (dict): Ambiguous libs, see `_disambiguate_libs`
612 output_hash (str): The hashed path of the crate root
613 rust_flags (list): Additional flags to pass to rustc
614 out_dir (str): The path to the output directory for the target Crate.
615 build_env_files (list): Files containing rustc environment variables, for instance from `cargo_build_script` actions.
616 build_flags_files (depset): The output files of a `cargo_build_script` actions containing rustc build flags
617 emit (list): Values for the --emit flag to rustc.
618 force_all_deps_direct (bool, optional): Whether to pass the transitive rlibs with --extern
619 to the commandline as opposed to -L.
620 force_link (bool, optional): Whether to add link flags to the command regardless of `emit`.
621 stamp (bool, optional): Whether or not workspace status stamping is enabled. For more details see
622 https://docs.bazel.build/versions/main/user-manual.html#flag--stamp
623 remap_path_prefix (str, optional): A value used to remap `${pwd}` to. If set to a falsey value, no prefix will be set.
624
625 Returns:
626 tuple: A tuple of the following items
627 - (struct): A struct of arguments used to run the `Rustc` action
628 - process_wrapper_flags (Args): Arguments for the process wrapper
629 - rustc_path (Args): Arguments for invoking rustc via the process wrapper
630 - rustc_flags (Args): Rust flags for the Rust compiler
631 - all (list): A list of all `Args` objects in the order listed above.
632 This is to be passed to the `arguments` parameter of actions
633 - (dict): Common rustc environment variables
634 """
635 output_dir = getattr(crate_info.output, "dirname", None)
636 linker_script = getattr(file, "linker_script", None)
637
638 env = _get_rustc_env(attr, toolchain, crate_info.name)
639
640 # Wrapper args first
641 process_wrapper_flags = ctx.actions.args()
642
643 for build_env_file in build_env_files:
644 process_wrapper_flags.add("--env-file", build_env_file)
645
646 process_wrapper_flags.add_all(build_flags_files, before_each = "--arg-file")
647
648 # Certain rust build processes expect to find files from the environment
649 # variable `$CARGO_MANIFEST_DIR`. Examples of this include pest, tera,
650 # asakuma.
651 #
652 # The compiler and by extension proc-macros see the current working
653 # directory as the Bazel exec root. This is what `$CARGO_MANIFEST_DIR`
654 # would default to but is often the wrong value (e.g. if the source is in a
655 # sub-package or if we are building something in an external repository).
656 # Hence, we need to set `CARGO_MANIFEST_DIR` explicitly.
657 #
658 # Since we cannot get the `exec_root` from starlark, we cheat a little and
659 # use `${pwd}` which resolves the `exec_root` at action execution time.
660 process_wrapper_flags.add("--subst", "pwd=${pwd}")
661
662 # If stamping is enabled, enable the functionality in the process wrapper
663 if stamp:
664 process_wrapper_flags.add("--volatile-status-file", ctx.version_file)
665
666 # Both ctx.label.workspace_root and ctx.label.package are relative paths
667 # and either can be empty strings. Avoid trailing/double slashes in the path.
668 components = "${{pwd}}/{}/{}".format(ctx.label.workspace_root, ctx.label.package).split("/")
669 env["CARGO_MANIFEST_DIR"] = "/".join([c for c in components if c])
670
671 if out_dir != None:
672 env["OUT_DIR"] = "${pwd}/" + out_dir
673
674 # Handle that the binary name and crate name may be different.
675 #
676 # If a target name contains a - then cargo (and rules_rust) will generate a
677 # crate name with _ instead. Accordingly, rustc will generate a output
678 # file (executable, or rlib, or whatever) with _ not -. But when cargo
679 # puts a binary in the target/${config} directory, and sets environment
680 # variables like `CARGO_BIN_EXE_${binary_name}` it will use the - version
681 # not the _ version. So we rename the rustc-generated file (with _s) to
682 # have -s if needed.
683 emit_with_paths = emit
684 if crate_info.type == "bin" and crate_info.output != None:
685 generated_file = crate_info.name + toolchain.binary_ext
686 src = "/".join([crate_info.output.dirname, generated_file])
687 dst = crate_info.output.path
688 if src != dst:
689 emit_with_paths = [("link=" + dst if val == "link" else val) for val in emit]
690
691 # Arguments for launching rustc from the process wrapper
692 rustc_path = ctx.actions.args()
693 rustc_path.add("--")
694 rustc_path.add(tool_path)
695
696 # Rustc arguments
697 rustc_flags = ctx.actions.args()
698 rustc_flags.set_param_file_format("multiline")
699 rustc_flags.use_param_file("@%s", use_always = False)
700 rustc_flags.add(crate_info.root)
701 rustc_flags.add("--crate-name=" + crate_info.name)
702 rustc_flags.add("--crate-type=" + crate_info.type)
703 if hasattr(attr, "_error_format"):
704 rustc_flags.add("--error-format=" + attr._error_format[ErrorFormatInfo].error_format)
705
706 # Mangle symbols to disambiguate crates with the same name
707 extra_filename = "-" + output_hash if output_hash else ""
708 rustc_flags.add("--codegen=metadata=" + extra_filename)
709 if output_dir:
710 rustc_flags.add("--out-dir=" + output_dir)
711 rustc_flags.add("--codegen=extra-filename=" + extra_filename)
712
713 compilation_mode = get_compilation_mode_opts(ctx, toolchain)
714 rustc_flags.add("--codegen=opt-level=" + compilation_mode.opt_level)
715 rustc_flags.add("--codegen=debuginfo=" + compilation_mode.debug_info)
716
717 # For determinism to help with build distribution and such
718 if remap_path_prefix:
719 rustc_flags.add("--remap-path-prefix=${{pwd}}={}".format(remap_path_prefix))
720
721 if emit:
722 rustc_flags.add("--emit=" + ",".join(emit_with_paths))
723 rustc_flags.add("--color=always")
724 rustc_flags.add("--target=" + toolchain.target_flag_value)
725 if hasattr(attr, "crate_features"):
726 rustc_flags.add_all(getattr(attr, "crate_features"), before_each = "--cfg", format_each = 'feature="%s"')
727 if linker_script:
728 rustc_flags.add(linker_script.path, format = "--codegen=link-arg=-T%s")
729
730 # Gets the paths to the folders containing the standard library (or libcore)
731 rust_std_paths = toolchain.rust_std_paths.to_list()
732
733 # Tell Rustc where to find the standard library
734 rustc_flags.add_all(rust_std_paths, before_each = "-L", format_each = "%s")
735 rustc_flags.add_all(rust_flags)
736
737 # Deduplicate data paths due to https://github.com/bazelbuild/bazel/issues/14681
738 data_paths = depset(direct = getattr(attr, "data", []) + getattr(attr, "compile_data", [])).to_list()
739
740 rustc_flags.add_all(
741 expand_list_element_locations(
742 ctx,
743 getattr(attr, "rustc_flags", []),
744 data_paths,
745 ),
746 )
747 add_edition_flags(rustc_flags, crate_info)
748
749 # Link!
750 if ("link" in emit and crate_info.type not in ["rlib", "lib"]) or force_link:
751 # Rust's built-in linker can handle linking wasm files. We don't want to attempt to use the cc
752 # linker since it won't understand.
753 if toolchain.target_arch != "wasm32":
754 if output_dir:
755 use_pic = _should_use_pic(cc_toolchain, feature_configuration, crate_info.type)
756 rpaths = _compute_rpaths(toolchain, output_dir, dep_info, use_pic)
757 else:
758 rpaths = depset([])
759 ld, link_args, link_env = get_linker_and_args(ctx, attr, cc_toolchain, feature_configuration, rpaths)
760 env.update(link_env)
761 rustc_flags.add("--codegen=linker=" + ld)
762 rustc_flags.add_joined("--codegen", link_args, join_with = " ", format_joined = "link-args=%s")
763
764 _add_native_link_flags(rustc_flags, dep_info, linkstamp_outs, ambiguous_libs, crate_info.type, toolchain, cc_toolchain, feature_configuration)
765
766 # These always need to be added, even if not linking this crate.
767 add_crate_link_flags(rustc_flags, dep_info, force_all_deps_direct)
768
769 needs_extern_proc_macro_flag = "proc-macro" in [crate_info.type, crate_info.wrapped_crate_type] and \
770 crate_info.edition != "2015"
771 if needs_extern_proc_macro_flag:
772 rustc_flags.add("--extern")
773 rustc_flags.add("proc_macro")
774
775 # Make bin crate data deps available to tests.
776 for data in getattr(attr, "data", []):
777 if rust_common.crate_info in data:
778 dep_crate_info = data[rust_common.crate_info]
779 if dep_crate_info.type == "bin":
780 # Trying to make CARGO_BIN_EXE_{} canonical across platform by strip out extension if exists
781 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
782 env["CARGO_BIN_EXE_" + env_basename] = dep_crate_info.output.short_path
783
784 # Update environment with user provided variables.
785 env.update(expand_dict_value_locations(
786 ctx,
787 crate_info.rustc_env,
788 data_paths,
789 ))
790
791 # Ensure the sysroot is set for the target platform
792 env["SYSROOT"] = toolchain.sysroot
793
794 if toolchain._rename_first_party_crates:
795 env["RULES_RUST_THIRD_PARTY_DIR"] = toolchain._third_party_dir
796
797 # extra_rustc_flags apply to the target configuration, not the exec configuration.
798 if hasattr(ctx.attr, "_extra_rustc_flags") and not is_exec_configuration(ctx):
799 rustc_flags.add_all(ctx.attr._extra_rustc_flags[ExtraRustcFlagsInfo].extra_rustc_flags)
800
801 if hasattr(ctx.attr, "_extra_exec_rustc_flags") and is_exec_configuration(ctx):
802 rustc_flags.add_all(ctx.attr._extra_exec_rustc_flags[ExtraExecRustcFlagsInfo].extra_exec_rustc_flags)
803
804 # Create a struct which keeps the arguments separate so each may be tuned or
805 # replaced where necessary
806 args = struct(
807 process_wrapper_flags = process_wrapper_flags,
808 rustc_path = rustc_path,
809 rustc_flags = rustc_flags,
810 all = [process_wrapper_flags, rustc_path, rustc_flags],
811 )
812
813 return args, env
814
815def rustc_compile_action(
816 ctx,
817 attr,
818 toolchain,
819 crate_info,
820 output_hash = None,
821 rust_flags = [],
822 force_all_deps_direct = False):
823 """Create and run a rustc compile action based on the current rule's attributes
824
825 Args:
826 ctx (ctx): The rule's context object
827 attr (struct): Attributes to use for the rust compile action
828 toolchain (rust_toolchain): The current `rust_toolchain`
829 crate_info (CrateInfo): The CrateInfo provider for the current target.
830 output_hash (str, optional): The hashed path of the crate root. Defaults to None.
831 rust_flags (list, optional): Additional flags to pass to rustc. Defaults to [].
832 force_all_deps_direct (bool, optional): Whether to pass the transitive rlibs with --extern
833 to the commandline as opposed to -L.
834
835 Returns:
836 list: A list of the following providers:
837 - (CrateInfo): info for the crate we just built; same as `crate_info` parameter.
838 - (DepInfo): The transitive dependencies of this crate.
839 - (DefaultInfo): The output file for this crate, and its runfiles.
840 """
841 cc_toolchain, feature_configuration = find_cc_toolchain(ctx)
842
843 dep_info, build_info, linkstamps = collect_deps(
844 deps = crate_info.deps,
845 proc_macro_deps = crate_info.proc_macro_deps,
846 aliases = crate_info.aliases,
847 are_linkstamps_supported = _are_linkstamps_supported(
848 feature_configuration = feature_configuration,
849 has_grep_includes = hasattr(ctx.attr, "_grep_includes"),
850 ),
851 )
852
853 # Determine if the build is currently running with --stamp
854 stamp = is_stamping_enabled(attr)
855
856 compile_inputs, out_dir, build_env_files, build_flags_files, linkstamp_outs, ambiguous_libs = collect_inputs(
857 ctx = ctx,
858 file = ctx.file,
859 files = ctx.files,
860 linkstamps = linkstamps,
861 toolchain = toolchain,
862 cc_toolchain = cc_toolchain,
863 feature_configuration = feature_configuration,
864 crate_info = crate_info,
865 dep_info = dep_info,
866 build_info = build_info,
867 stamp = stamp,
868 )
869
870 args, env_from_args = construct_arguments(
871 ctx = ctx,
872 attr = attr,
873 file = ctx.file,
874 toolchain = toolchain,
875 tool_path = toolchain.rustc.path,
876 cc_toolchain = cc_toolchain,
877 feature_configuration = feature_configuration,
878 crate_info = crate_info,
879 dep_info = dep_info,
880 linkstamp_outs = linkstamp_outs,
881 ambiguous_libs = ambiguous_libs,
882 output_hash = output_hash,
883 rust_flags = rust_flags,
884 out_dir = out_dir,
885 build_env_files = build_env_files,
886 build_flags_files = build_flags_files,
887 force_all_deps_direct = force_all_deps_direct,
888 stamp = stamp,
889 )
890
891 env = dict(ctx.configuration.default_shell_env)
892 env.update(env_from_args)
893
894 if hasattr(attr, "version") and attr.version != "0.0.0":
895 formatted_version = " v{}".format(attr.version)
896 else:
897 formatted_version = ""
898
899 outputs = [crate_info.output]
900
901 # For a cdylib that might be added as a dependency to a cc_* target on Windows, it is important to include the
902 # interface library that rustc generates in the output files.
903 interface_library = None
904 if toolchain.os == "windows" and crate_info.type == "cdylib":
905 # Rustc generates the import library with a `.dll.lib` extension rather than the usual `.lib` one that msvc
906 # expects (see https://github.com/rust-lang/rust/pull/29520 for more context).
907 interface_library = ctx.actions.declare_file(crate_info.output.basename + ".lib")
908 outputs.append(interface_library)
909
910 # The action might generate extra output that we don't want to include in the `DefaultInfo` files.
911 action_outputs = list(outputs)
912
913 # Rustc generates a pdb file (on Windows) or a dsym folder (on macos) so provide it in an output group for crate
914 # types that benefit from having debug information in a separate file.
915 pdb_file = None
916 dsym_folder = None
917 if crate_info.type in ("cdylib", "bin") and not crate_info.is_test:
918 if toolchain.os == "windows":
919 pdb_file = ctx.actions.declare_file(crate_info.output.basename[:-len(crate_info.output.extension)] + "pdb")
920 action_outputs.append(pdb_file)
921 elif toolchain.os == "darwin":
922 dsym_folder = ctx.actions.declare_directory(crate_info.output.basename + ".dSYM")
923 action_outputs.append(dsym_folder)
924
925 # This uses startswith as on windows the basename will be process_wrapper_fake.exe.
926 if not ctx.executable._process_wrapper.basename.startswith("process_wrapper_fake"):
927 # Run as normal
928 ctx.actions.run(
929 executable = ctx.executable._process_wrapper,
930 inputs = compile_inputs,
931 outputs = action_outputs,
932 env = env,
933 arguments = args.all,
934 mnemonic = "Rustc",
935 progress_message = "Compiling Rust {} {}{} ({} files)".format(
936 crate_info.type,
937 ctx.label.name,
938 formatted_version,
939 len(crate_info.srcs.to_list()),
940 ),
941 )
942 else:
943 # Run without process_wrapper
944 if build_env_files or build_flags_files or stamp:
945 fail("build_env_files, build_flags_files, stamp are not supported if use_process_wrapper is False")
946 ctx.actions.run(
947 executable = toolchain.rustc,
948 inputs = compile_inputs,
949 outputs = action_outputs,
950 env = env,
951 arguments = [args.rustc_flags],
952 mnemonic = "Rustc",
953 progress_message = "Compiling Rust (without process_wrapper) {} {}{} ({} files)".format(
954 crate_info.type,
955 ctx.label.name,
956 formatted_version,
957 len(crate_info.srcs.to_list()),
958 ),
959 )
960
961 runfiles = ctx.runfiles(
962 files = getattr(ctx.files, "data", []),
963 collect_data = True,
964 )
965
966 # TODO: Remove after some resolution to
967 # https://github.com/bazelbuild/rules_rust/issues/771
968 out_binary = getattr(attr, "out_binary", False)
969
970 providers = [
971 crate_info,
972 dep_info,
973 DefaultInfo(
974 # nb. This field is required for cc_library to depend on our output.
975 files = depset(outputs),
976 runfiles = runfiles,
977 executable = crate_info.output if crate_info.type == "bin" or crate_info.is_test or out_binary else None,
978 ),
979 ]
980 if toolchain.target_arch != "wasm32":
981 providers += establish_cc_info(ctx, attr, crate_info, toolchain, cc_toolchain, feature_configuration, interface_library)
982 if pdb_file:
983 providers.append(OutputGroupInfo(pdb_file = depset([pdb_file])))
984 if dsym_folder:
985 providers.append(OutputGroupInfo(dsym_folder = depset([dsym_folder])))
986
987 return providers
988
989def _is_dylib(dep):
990 return not bool(dep.static_library or dep.pic_static_library)
991
992def establish_cc_info(ctx, attr, crate_info, toolchain, cc_toolchain, feature_configuration, interface_library):
993 """If the produced crate is suitable yield a CcInfo to allow for interop with cc rules
994
995 Args:
996 ctx (ctx): The rule's context object
997 attr (struct): Attributes to use in gathering CcInfo
998 crate_info (CrateInfo): The CrateInfo provider of the target crate
999 toolchain (rust_toolchain): The current `rust_toolchain`
1000 cc_toolchain (CcToolchainInfo): The current `CcToolchainInfo`
1001 feature_configuration (FeatureConfiguration): Feature configuration to be queried.
1002 interface_library (File): Optional interface library for cdylib crates on Windows.
1003
1004 Returns:
1005 list: A list containing the CcInfo provider
1006 """
1007
1008 # A test will not need to produce CcInfo as nothing can depend on test targets
1009 if crate_info.is_test:
1010 return []
1011
1012 # Only generate CcInfo for particular crate types
1013 if crate_info.type not in ("staticlib", "cdylib", "rlib", "lib"):
1014 return []
1015
1016 # TODO: Remove after some resolution to
1017 # https://github.com/bazelbuild/rules_rust/issues/771
1018 if getattr(attr, "out_binary", False):
1019 return []
1020
1021 if crate_info.type == "staticlib":
1022 library_to_link = cc_common.create_library_to_link(
1023 actions = ctx.actions,
1024 feature_configuration = feature_configuration,
1025 cc_toolchain = cc_toolchain,
1026 static_library = crate_info.output,
1027 # TODO(hlopko): handle PIC/NOPIC correctly
1028 pic_static_library = crate_info.output,
1029 )
1030 elif crate_info.type in ("rlib", "lib"):
1031 # bazel hard-codes a check for endswith((".a", ".pic.a",
1032 # ".lib")) in create_library_to_link, so we work around that
1033 # by creating a symlink to the .rlib with a .a extension.
1034 dot_a = make_static_lib_symlink(ctx.actions, crate_info.output)
1035
1036 # TODO(hlopko): handle PIC/NOPIC correctly
1037 library_to_link = cc_common.create_library_to_link(
1038 actions = ctx.actions,
1039 feature_configuration = feature_configuration,
1040 cc_toolchain = cc_toolchain,
1041 static_library = dot_a,
1042 # TODO(hlopko): handle PIC/NOPIC correctly
1043 pic_static_library = dot_a,
1044 )
1045 elif crate_info.type == "cdylib":
1046 library_to_link = cc_common.create_library_to_link(
1047 actions = ctx.actions,
1048 feature_configuration = feature_configuration,
1049 cc_toolchain = cc_toolchain,
1050 dynamic_library = crate_info.output,
1051 interface_library = interface_library,
1052 )
1053 else:
1054 fail("Unexpected case")
1055
1056 link_input = cc_common.create_linker_input(
1057 owner = ctx.label,
1058 libraries = depset([library_to_link]),
1059 )
1060
1061 linking_context = cc_common.create_linking_context(
1062 # TODO - What to do for no_std?
1063 linker_inputs = depset([link_input]),
1064 )
1065
1066 cc_infos = [
1067 CcInfo(linking_context = linking_context),
1068 toolchain.stdlib_linkflags,
1069 ]
1070 for dep in getattr(attr, "deps", []):
1071 if CcInfo in dep:
1072 cc_infos.append(dep[CcInfo])
1073
1074 if crate_info.type in ("rlib", "lib") and toolchain.libstd_and_allocator_ccinfo:
1075 # TODO: if we already have an rlib in our deps, we could skip this
1076 cc_infos.append(toolchain.libstd_and_allocator_ccinfo)
1077
1078 return [cc_common.merge_cc_infos(cc_infos = cc_infos)]
1079
1080def add_edition_flags(args, crate):
1081 """Adds the Rust edition flag to an arguments object reference
1082
1083 Args:
1084 args (Args): A reference to an Args object
1085 crate (CrateInfo): A CrateInfo provider
1086 """
1087 if crate.edition != "2015":
1088 args.add("--edition={}".format(crate.edition))
1089
1090def _create_extra_input_args(build_info, dep_info):
1091 """Gather additional input arguments from transitive dependencies
1092
1093 Args:
1094 build_info (BuildInfo): The BuildInfo provider from the target Crate's set of inputs.
1095 dep_info (DepInfo): The Depinfo provider form the target Crate's set of inputs.
1096
1097 Returns:
1098 tuple: A tuple of the following items:
1099 - (depset[File]): A list of all build info `OUT_DIR` File objects
1100 - (str): The `OUT_DIR` of the current build info
1101 - (File): An optional generated environment file from a `cargo_build_script` target
1102 - (depset[File]): All direct and transitive build flag files from the current build info.
1103 """
1104 input_files = []
1105
1106 # Arguments to the commandline line wrapper that are going to be used
1107 # to create the final command line
1108 out_dir = None
1109 build_env_file = None
1110 build_flags_files = []
1111
1112 if build_info:
1113 out_dir = build_info.out_dir.path
1114 build_env_file = build_info.rustc_env
1115 build_flags_files.append(build_info.flags)
1116 build_flags_files.append(build_info.link_flags)
1117 input_files.append(build_info.out_dir)
1118 input_files.append(build_info.link_flags)
1119
1120 return (
1121 depset(input_files, transitive = [dep_info.link_search_path_files]),
1122 out_dir,
1123 build_env_file,
1124 depset(build_flags_files, transitive = [dep_info.link_search_path_files]),
1125 )
1126
1127def _compute_rpaths(toolchain, output_dir, dep_info, use_pic):
1128 """Determine the artifact's rpaths relative to the bazel root for runtime linking of shared libraries.
1129
1130 Args:
1131 toolchain (rust_toolchain): The current `rust_toolchain`
1132 output_dir (str): The output directory of the current target
1133 dep_info (DepInfo): The current target's dependency info
1134 use_pic: If set, prefers pic_static_library over static_library.
1135
1136 Returns:
1137 depset: A set of relative paths from the output directory to each dependency
1138 """
1139
1140 # Windows has no rpath equivalent, so always return an empty depset.
1141 if toolchain.os == "windows":
1142 return depset([])
1143
1144 dylibs = [
1145 get_preferred_artifact(lib, use_pic)
1146 for linker_input in dep_info.transitive_noncrates.to_list()
1147 for lib in linker_input.libraries
1148 if _is_dylib(lib)
1149 ]
1150 if not dylibs:
1151 return depset([])
1152
1153 # For darwin, dylibs compiled by Bazel will fail to be resolved at runtime
1154 # without a version of Bazel that includes
1155 # https://github.com/bazelbuild/bazel/pull/13427. This is known to not be
1156 # included in Bazel 4.1 and below.
1157 if toolchain.os != "linux" and toolchain.os != "darwin":
1158 fail("Runtime linking is not supported on {}, but found {}".format(
1159 toolchain.os,
1160 dep_info.transitive_noncrates,
1161 ))
1162
1163 # Multiple dylibs can be present in the same directory, so deduplicate them.
1164 return depset([
1165 relativize(lib_dir, output_dir)
1166 for lib_dir in _get_dir_names(dylibs)
1167 ])
1168
1169def _get_dir_names(files):
1170 """Returns a list of directory names from the given list of File objects
1171
1172 Args:
1173 files (list): A list of File objects
1174
1175 Returns:
1176 list: A list of directory names for all files
1177 """
1178 dirs = {}
1179 for f in files:
1180 dirs[f.dirname] = None
1181 return dirs.keys()
1182
1183def add_crate_link_flags(args, dep_info, force_all_deps_direct = False):
1184 """Adds link flags to an Args object reference
1185
1186 Args:
1187 args (Args): An arguments object reference
1188 dep_info (DepInfo): The current target's dependency info
1189 force_all_deps_direct (bool, optional): Whether to pass the transitive rlibs with --extern
1190 to the commandline as opposed to -L.
1191 """
1192
1193 if force_all_deps_direct:
1194 args.add_all(
1195 depset(
1196 transitive = [
1197 dep_info.direct_crates,
1198 dep_info.transitive_crates,
1199 ],
1200 ),
1201 uniquify = True,
1202 map_each = _crate_to_link_flag,
1203 )
1204 else:
1205 # nb. Direct crates are linked via --extern regardless of their crate_type
1206 args.add_all(dep_info.direct_crates, map_each = _crate_to_link_flag)
1207 args.add_all(
1208 dep_info.transitive_crates,
1209 map_each = _get_crate_dirname,
1210 uniquify = True,
1211 format_each = "-Ldependency=%s",
1212 )
1213
1214def _crate_to_link_flag(crate):
1215 """A helper macro used by `add_crate_link_flags` for adding crate link flags to a Arg object
1216
1217 Args:
1218 crate (CrateInfo|AliasableDepInfo): A CrateInfo or an AliasableDepInfo provider
1219
1220 Returns:
1221 list: Link flags for the given provider
1222 """
1223
1224 # This is AliasableDepInfo, we should use the alias as a crate name
1225 if hasattr(crate, "dep"):
1226 name = crate.name
1227 crate_info = crate.dep
1228 else:
1229 name = crate.name
1230 crate_info = crate
1231 return ["--extern={}={}".format(name, crate_info.output.path)]
1232
1233def _get_crate_dirname(crate):
1234 """A helper macro used by `add_crate_link_flags` for getting the directory name of the current crate's output path
1235
1236 Args:
1237 crate (CrateInfo): A CrateInfo provider from the current rule
1238
1239 Returns:
1240 str: The directory name of the the output File that will be produced.
1241 """
1242 return crate.output.dirname
1243
1244def _portable_link_flags(lib, use_pic, ambiguous_libs):
1245 artifact = get_preferred_artifact(lib, use_pic)
1246 if ambiguous_libs and artifact.path in ambiguous_libs:
1247 artifact = ambiguous_libs[artifact.path]
1248 if lib.static_library or lib.pic_static_library:
1249 return [
1250 "-lstatic=%s" % get_lib_name(artifact),
1251 ]
1252 elif _is_dylib(lib):
1253 return [
1254 "-ldylib=%s" % get_lib_name(artifact),
1255 ]
1256
1257 return []
1258
1259def _make_link_flags_windows(linker_input_and_use_pic_and_ambiguous_libs):
1260 linker_input, use_pic, ambiguous_libs = linker_input_and_use_pic_and_ambiguous_libs
1261 ret = []
1262 for lib in linker_input.libraries:
1263 if lib.alwayslink:
1264 ret.extend(["-C", "link-arg=/WHOLEARCHIVE:%s" % get_preferred_artifact(lib, use_pic).path])
1265 else:
1266 ret.extend(_portable_link_flags(lib, use_pic, ambiguous_libs))
1267 return ret
1268
1269def _make_link_flags_darwin(linker_input_and_use_pic_and_ambiguous_libs):
1270 linker_input, use_pic, ambiguous_libs = linker_input_and_use_pic_and_ambiguous_libs
1271 ret = []
1272 for lib in linker_input.libraries:
1273 if lib.alwayslink:
1274 ret.extend([
1275 "-C",
1276 ("link-arg=-Wl,-force_load,%s" % get_preferred_artifact(lib, use_pic).path),
1277 ])
1278 else:
1279 ret.extend(_portable_link_flags(lib, use_pic, ambiguous_libs))
1280 return ret
1281
1282def _make_link_flags_default(linker_input_and_use_pic_and_ambiguous_libs):
1283 linker_input, use_pic, ambiguous_libs = linker_input_and_use_pic_and_ambiguous_libs
1284 ret = []
1285 for lib in linker_input.libraries:
1286 if lib.alwayslink:
1287 ret.extend([
1288 "-C",
1289 "link-arg=-Wl,--whole-archive",
1290 "-C",
1291 ("link-arg=%s" % get_preferred_artifact(lib, use_pic).path),
1292 "-C",
1293 "link-arg=-Wl,--no-whole-archive",
1294 ])
1295 else:
1296 ret.extend(_portable_link_flags(lib, use_pic, ambiguous_libs))
1297 return ret
1298
1299def _libraries_dirnames(linker_input_and_use_pic_and_ambiguous_libs):
1300 link_input, use_pic, _ = linker_input_and_use_pic_and_ambiguous_libs
1301
1302 # De-duplicate names.
1303 return depset([get_preferred_artifact(lib, use_pic).dirname for lib in link_input.libraries]).to_list()
1304
1305def _add_native_link_flags(args, dep_info, linkstamp_outs, ambiguous_libs, crate_type, toolchain, cc_toolchain, feature_configuration):
1306 """Adds linker flags for all dependencies of the current target.
1307
1308 Args:
1309 args (Args): The Args struct for a ctx.action
1310 dep_info (DepInfo): Dependency Info provider
1311 linkstamp_outs (list): Linkstamp outputs of native dependencies
1312 ambiguous_libs (dict): Ambiguous libs, see `_disambiguate_libs`
1313 crate_type: Crate type of the current target
1314 toolchain (rust_toolchain): The current `rust_toolchain`
1315 cc_toolchain (CcToolchainInfo): The current `cc_toolchain`
1316 feature_configuration (FeatureConfiguration): feature configuration to use with cc_toolchain
1317
1318 """
1319 if crate_type in ["lib", "rlib"]:
1320 return
1321
1322 use_pic = _should_use_pic(cc_toolchain, feature_configuration, crate_type)
1323
1324 if toolchain.os == "windows":
1325 make_link_flags = _make_link_flags_windows
1326 elif toolchain.os.startswith("mac") or toolchain.os.startswith("darwin"):
1327 make_link_flags = _make_link_flags_darwin
1328 else:
1329 make_link_flags = _make_link_flags_default
1330
1331 # TODO(hlopko): Remove depset flattening by using lambdas once we are on >=Bazel 5.0
1332 args_and_pic_and_ambiguous_libs = [(arg, use_pic, ambiguous_libs) for arg in dep_info.transitive_noncrates.to_list()]
1333 args.add_all(args_and_pic_and_ambiguous_libs, map_each = _libraries_dirnames, uniquify = True, format_each = "-Lnative=%s")
1334 if ambiguous_libs:
1335 # If there are ambiguous libs, the disambiguation symlinks to them are
1336 # all created in the same directory. Add it to the library search path.
1337 ambiguous_libs_dirname = ambiguous_libs.values()[0].dirname
1338 args.add("-Lnative={}".format(ambiguous_libs_dirname))
1339
1340 args.add_all(args_and_pic_and_ambiguous_libs, map_each = make_link_flags)
1341
1342 for linkstamp_out in linkstamp_outs:
1343 args.add_all(["-C", "link-arg=%s" % linkstamp_out.path])
1344
1345 if crate_type in ["dylib", "cdylib"]:
1346 # For shared libraries we want to link C++ runtime library dynamically
1347 # (for example libstdc++.so or libc++.so).
1348 args.add_all(
1349 cc_toolchain.dynamic_runtime_lib(feature_configuration = feature_configuration),
1350 map_each = _get_dirname,
1351 format_each = "-Lnative=%s",
1352 )
1353 args.add_all(
1354 cc_toolchain.dynamic_runtime_lib(feature_configuration = feature_configuration),
1355 map_each = get_lib_name,
1356 format_each = "-ldylib=%s",
1357 )
1358 else:
1359 # For all other crate types we want to link C++ runtime library statically
1360 # (for example libstdc++.a or libc++.a).
1361 args.add_all(
1362 cc_toolchain.static_runtime_lib(feature_configuration = feature_configuration),
1363 map_each = _get_dirname,
1364 format_each = "-Lnative=%s",
1365 )
1366 args.add_all(
1367 cc_toolchain.static_runtime_lib(feature_configuration = feature_configuration),
1368 map_each = get_lib_name,
1369 format_each = "-lstatic=%s",
1370 )
1371
1372def _get_dirname(file):
1373 """A helper function for `_add_native_link_flags`.
1374
1375 Args:
1376 file (File): The target file
1377
1378 Returns:
1379 str: Directory name of `file`
1380 """
1381 return file.dirname
1382
1383def _error_format_impl(ctx):
1384 """Implementation of the `error_format` rule
1385
1386 Args:
1387 ctx (ctx): The rule's context object
1388
1389 Returns:
1390 list: A list containing the ErrorFormatInfo provider
1391 """
1392 raw = ctx.build_setting_value
1393 if raw not in _error_format_values:
1394 fail("{} expected a value in `{}` but got `{}`".format(
1395 ctx.label,
1396 _error_format_values,
1397 raw,
1398 ))
1399 return [ErrorFormatInfo(error_format = raw)]
1400
1401error_format = rule(
1402 doc = (
1403 "Change the [--error-format](https://doc.rust-lang.org/rustc/command-line-arguments.html#option-error-format) " +
1404 "flag from the command line with `--@rules_rust//:error_format`. See rustc documentation for valid values."
1405 ),
1406 implementation = _error_format_impl,
1407 build_setting = config.string(flag = True),
1408)
1409
1410def _extra_rustc_flags_impl(ctx):
1411 return ExtraRustcFlagsInfo(extra_rustc_flags = ctx.build_setting_value)
1412
1413extra_rustc_flags = rule(
1414 doc = (
1415 "Add additional rustc_flags from the command line with `--@rules_rust//:extra_rustc_flags`. " +
1416 "This flag should only be used for flags that need to be applied across the entire build. For options that " +
1417 "apply to individual crates, use the rustc_flags attribute on the individual crate's rule instead. NOTE: " +
1418 "These flags not applied to the exec configuration (proc-macros, cargo_build_script, etc); " +
1419 "use `--@rules_rust//:extra_exec_rustc_flags` to apply flags to the exec configuration."
1420 ),
1421 implementation = _extra_rustc_flags_impl,
1422 build_setting = config.string_list(flag = True),
1423)
1424
1425def _extra_exec_rustc_flags_impl(ctx):
1426 return ExtraExecRustcFlagsInfo(extra_exec_rustc_flags = ctx.build_setting_value)
1427
1428extra_exec_rustc_flags = rule(
1429 doc = (
1430 "Add additional rustc_flags in the exec configuration from the command line with `--@rules_rust//:extra_exec_rustc_flags`. " +
1431 "This flag should only be used for flags that need to be applied across the entire build. " +
1432 "These flags only apply to the exec configuration (proc-macros, cargo_build_script, etc)."
1433 ),
1434 implementation = _extra_exec_rustc_flags_impl,
1435 build_setting = config.string_list(flag = True),
1436)