xref: /aosp_15_r20/external/bazelbuild-rules_rust/rust/private/rustc.bzl (revision d4726bddaa87cc4778e7472feed243fa4b6c267f)
1# 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("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo")
18load(
19    "@bazel_tools//tools/build_defs/cc:action_names.bzl",
20    "CPP_LINK_DYNAMIC_LIBRARY_ACTION_NAME",
21    "CPP_LINK_EXECUTABLE_ACTION_NAME",
22    "CPP_LINK_NODEPS_DYNAMIC_LIBRARY_ACTION_NAME",
23    "CPP_LINK_STATIC_LIBRARY_ACTION_NAME",
24)
25load("//rust/private:common.bzl", "rust_common")
26load("//rust/private:providers.bzl", "RustcOutputDiagnosticsInfo", _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",
34    "get_lib_name_default",
35    "get_lib_name_for_windows",
36    "get_preferred_artifact",
37    "is_exec_configuration",
38    "make_static_lib_symlink",
39    "relativize",
40)
41load(":utils.bzl", "is_std_dylib")
42
43# This feature is disabled unless one of the dependencies is a cc_library.
44# Authors of C++ toolchains can place linker flags that should only be applied
45# when linking with C objects in a feature with this name, or require this
46# feature from other features which needs to be disabled together.
47RUST_LINK_CC_FEATURE = "rules_rust_link_cc"
48
49BuildInfo = _BuildInfo
50
51AliasableDepInfo = provider(
52    doc = "A provider mapping an alias name to a Crate's information.",
53    fields = {
54        "dep": "CrateInfo",
55        "name": "str",
56    },
57)
58
59_error_format_values = ["human", "json", "short"]
60
61ErrorFormatInfo = provider(
62    doc = "Set the --error-format flag for all rustc invocations",
63    fields = {"error_format": "(string) [" + ", ".join(_error_format_values) + "]"},
64)
65
66ExtraRustcFlagsInfo = provider(
67    doc = "Pass each value as an additional flag to non-exec rustc invocations",
68    fields = {"extra_rustc_flags": "List[string] Extra flags to pass to rustc in non-exec configuration"},
69)
70
71ExtraExecRustcFlagsInfo = provider(
72    doc = "Pass each value as an additional flag to exec rustc invocations",
73    fields = {"extra_exec_rustc_flags": "List[string] Extra flags to pass to rustc in exec configuration"},
74)
75
76PerCrateRustcFlagsInfo = provider(
77    doc = "Pass each value as an additional flag to non-exec rustc invocations for crates matching the provided filter",
78    fields = {"per_crate_rustc_flags": "List[string] Extra flags to pass to rustc in non-exec configuration"},
79)
80
81IsProcMacroDepInfo = provider(
82    doc = "Records if this is a transitive dependency of a proc-macro.",
83    fields = {"is_proc_macro_dep": "Boolean"},
84)
85
86def _is_proc_macro_dep_impl(ctx):
87    return IsProcMacroDepInfo(is_proc_macro_dep = ctx.build_setting_value)
88
89is_proc_macro_dep = rule(
90    doc = "Records if this is a transitive dependency of a proc-macro.",
91    implementation = _is_proc_macro_dep_impl,
92    build_setting = config.bool(flag = True),
93)
94
95IsProcMacroDepEnabledInfo = provider(
96    doc = "Enables the feature to record if a library is a transitive dependency of a proc-macro.",
97    fields = {"enabled": "Boolean"},
98)
99
100def _is_proc_macro_dep_enabled_impl(ctx):
101    return IsProcMacroDepEnabledInfo(enabled = ctx.build_setting_value)
102
103is_proc_macro_dep_enabled = rule(
104    doc = "Enables the feature to record if a library is a transitive dependency of a proc-macro.",
105    implementation = _is_proc_macro_dep_enabled_impl,
106    build_setting = config.bool(flag = True),
107)
108
109def _get_rustc_env(attr, toolchain, crate_name):
110    """Gathers rustc environment variables
111
112    Args:
113        attr (struct): The current target's attributes
114        toolchain (rust_toolchain): The current target's rust toolchain context
115        crate_name (str): The name of the crate to be compiled
116
117    Returns:
118        dict: Rustc environment variables
119    """
120    version = attr.version if hasattr(attr, "version") else "0.0.0"
121    major, minor, patch = version.split(".", 2)
122    if "-" in patch:
123        patch, pre = patch.split("-", 1)
124    else:
125        pre = ""
126
127    result = {
128        "CARGO_CFG_TARGET_ARCH": "" if toolchain.target_arch == None else toolchain.target_arch,
129        "CARGO_CFG_TARGET_OS": "" if toolchain.target_os == None else toolchain.target_os,
130        "CARGO_CRATE_NAME": crate_name,
131        "CARGO_PKG_AUTHORS": "",
132        "CARGO_PKG_DESCRIPTION": "",
133        "CARGO_PKG_HOMEPAGE": "",
134        "CARGO_PKG_NAME": attr.name,
135        "CARGO_PKG_VERSION": version,
136        "CARGO_PKG_VERSION_MAJOR": major,
137        "CARGO_PKG_VERSION_MINOR": minor,
138        "CARGO_PKG_VERSION_PATCH": patch,
139        "CARGO_PKG_VERSION_PRE": pre,
140    }
141    if hasattr(attr, "_is_proc_macro_dep_enabled") and attr._is_proc_macro_dep_enabled[IsProcMacroDepEnabledInfo].enabled:
142        is_proc_macro_dep = "0"
143        if hasattr(attr, "_is_proc_macro_dep") and attr._is_proc_macro_dep[IsProcMacroDepInfo].is_proc_macro_dep:
144            is_proc_macro_dep = "1"
145        result["BAZEL_RULES_RUST_IS_PROC_MACRO_DEP"] = is_proc_macro_dep
146    return result
147
148def get_compilation_mode_opts(ctx, toolchain):
149    """Gathers rustc flags for the current compilation mode (opt/debug)
150
151    Args:
152        ctx (ctx): The current rule's context object
153        toolchain (rust_toolchain): The current rule's `rust_toolchain`
154
155    Returns:
156        struct: See `_rust_toolchain_impl` for more details
157    """
158    comp_mode = ctx.var["COMPILATION_MODE"]
159    if not comp_mode in toolchain.compilation_mode_opts:
160        fail("Unrecognized compilation mode {} for toolchain.".format(comp_mode))
161
162    return toolchain.compilation_mode_opts[comp_mode]
163
164def _are_linkstamps_supported(feature_configuration, has_grep_includes):
165    # Are linkstamps supported by the C++ toolchain?
166    return (cc_common.is_enabled(feature_configuration = feature_configuration, feature_name = "linkstamps") and
167            # Is Bazel recent enough to support Starlark linkstamps?
168            hasattr(cc_common, "register_linkstamp_compile_action") and
169            # The current rule doesn't define _grep_includes attribute; this
170            # attribute is required for compiling linkstamps.
171            has_grep_includes)
172
173def _should_use_pic(cc_toolchain, feature_configuration, crate_type, compilation_mode):
174    """Whether or not [PIC][pic] should be enabled
175
176    [pic]: https://en.wikipedia.org/wiki/Position-independent_code
177
178    Args:
179        cc_toolchain (CcToolchainInfo): The current `cc_toolchain`.
180        feature_configuration (FeatureConfiguration): Feature configuration to be queried.
181        crate_type (str): A Rust target's crate type.
182        compilation_mode: The compilation mode.
183
184    Returns:
185        bool: Whether or not [PIC][pic] should be enabled.
186    """
187
188    # We use the same logic to select between `pic` and `nopic` outputs as the C++ rules:
189    # - For shared libraries - we use `pic`. This covers `dylib`, `cdylib` and `proc-macro` crate types.
190    # - In `fastbuild` and `dbg` mode we use `pic` by default.
191    # - In `opt` mode we use `nopic` outputs to build binaries.
192    if crate_type in ("cdylib", "dylib", "proc-macro"):
193        return cc_toolchain.needs_pic_for_dynamic_libraries(feature_configuration = feature_configuration)
194    elif compilation_mode in ("fastbuild", "dbg"):
195        return True
196    return False
197
198def _is_proc_macro(crate_info):
199    return "proc-macro" in (crate_info.type, crate_info.wrapped_crate_type)
200
201def collect_deps(
202        deps,
203        proc_macro_deps,
204        aliases):
205    """Walks through dependencies and collects the transitive dependencies.
206
207    Args:
208        deps (list): The deps from ctx.attr.deps.
209        proc_macro_deps (list): The proc_macro deps from ctx.attr.proc_macro_deps.
210        aliases (dict): A dict mapping aliased targets to their actual Crate information.
211
212    Returns:
213        tuple: Returns a tuple of:
214            DepInfo,
215            BuildInfo,
216            linkstamps (depset[CcLinkstamp]): A depset of CcLinkstamps that need to be compiled and linked into all linked binaries when applicable.
217
218    """
219    direct_crates = []
220    transitive_crates = []
221    transitive_data = []
222    transitive_proc_macro_data = []
223    transitive_noncrates = []
224    transitive_build_infos = []
225    transitive_link_search_paths = []
226    build_info = None
227    linkstamps = []
228    transitive_crate_outputs = []
229    transitive_metadata_outputs = []
230
231    crate_deps = []
232    for dep in depset(transitive = [deps, proc_macro_deps]).to_list():
233        crate_group = None
234
235        if type(dep) == "Target" and rust_common.crate_group_info in dep:
236            crate_group = dep[rust_common.crate_group_info]
237        elif type(dep) == "struct" and hasattr(dep, "crate_group_info") and dep.crate_group_info != None:
238            crate_group = dep.crate_group_info
239        else:
240            crate_deps.append(dep)
241
242        if crate_group:
243            for dep_variant_info in crate_group.dep_variant_infos.to_list():
244                crate_deps.append(struct(
245                    crate_info = dep_variant_info.crate_info,
246                    dep_info = dep_variant_info.dep_info,
247                    cc_info = dep_variant_info.cc_info,
248                ))
249
250    aliases = {k.label: v for k, v in aliases.items()}
251    for dep in crate_deps:
252        (crate_info, dep_info) = _get_crate_and_dep_info(dep)
253        cc_info = _get_cc_info(dep)
254        dep_build_info = _get_build_info(dep)
255
256        if cc_info:
257            linkstamps.append(cc_info.linking_context.linkstamps())
258
259        if crate_info:
260            # This dependency is a rust_library
261
262            # When crate_info.owner is set, we use it. When the dep type is Target we get the
263            # label from dep.label
264            owner = getattr(crate_info, "owner", dep.label if type(dep) == "Target" else None)
265
266            direct_crates.append(AliasableDepInfo(
267                name = aliases.get(owner, crate_info.name),
268                dep = crate_info,
269            ))
270
271            transitive_crates.append(
272                depset(
273                    [crate_info],
274                    transitive = [] if _is_proc_macro(crate_info) else [dep_info.transitive_crates],
275                ),
276            )
277
278            if _is_proc_macro(crate_info):
279                # This crate's data and its non-macro dependencies' data are proc macro data.
280                transitive_proc_macro_data.append(crate_info.data)
281                transitive_proc_macro_data.append(dep_info.transitive_data)
282            else:
283                # This crate's proc macro dependencies' data are proc macro data.
284                transitive_proc_macro_data.append(dep_info.transitive_proc_macro_data)
285
286                # Track transitive non-macro data in case a proc macro depends on this crate.
287                transitive_data.append(crate_info.data)
288                transitive_data.append(dep_info.transitive_data)
289
290            # If this dependency produces metadata, add it to the metadata outputs.
291            # If it doesn't (for example a custom library that exports crate_info),
292            # we depend on crate_info.output.
293            depend_on = crate_info.metadata
294            if not crate_info.metadata:
295                depend_on = crate_info.output
296
297            # If this dependency is a proc_macro, it still can be used for lib crates
298            # that produce metadata.
299            # In that case, we don't depend on its metadata dependencies.
300            transitive_metadata_outputs.append(
301                depset(
302                    [depend_on],
303                    transitive = [] if _is_proc_macro(crate_info) else [dep_info.transitive_metadata_outputs],
304                ),
305            )
306
307            transitive_crate_outputs.append(
308                depset(
309                    [crate_info.output],
310                    transitive = [] if _is_proc_macro(crate_info) else [dep_info.transitive_crate_outputs],
311                ),
312            )
313
314            if "proc-macro" not in [crate_info.type, crate_info.wrapped_crate_type]:
315                transitive_noncrates.append(dep_info.transitive_noncrates)
316                transitive_link_search_paths.append(dep_info.link_search_path_files)
317
318            transitive_build_infos.append(dep_info.transitive_build_infos)
319        elif cc_info or dep_build_info:
320            if cc_info:
321                # This dependency is a cc_library
322                transitive_noncrates.append(cc_info.linking_context.linker_inputs)
323
324            if dep_build_info:
325                if build_info:
326                    fail("Several deps are providing build information, " +
327                         "only one is allowed in the dependencies")
328                build_info = dep_build_info
329                transitive_build_infos.append(depset([build_info]))
330                if build_info.link_search_paths:
331                    transitive_link_search_paths.append(depset([build_info.link_search_paths]))
332        else:
333            fail("rust targets can only depend on rust_library, rust_*_library or cc_library " +
334                 "targets.")
335
336    transitive_crates_depset = depset(transitive = transitive_crates)
337    transitive_data_depset = depset(transitive = transitive_data)
338    transitive_proc_macro_data_depset = depset(transitive = transitive_proc_macro_data)
339
340    return (
341        rust_common.dep_info(
342            direct_crates = depset(direct_crates),
343            transitive_crates = transitive_crates_depset,
344            transitive_data = transitive_data_depset,
345            transitive_proc_macro_data = transitive_proc_macro_data_depset,
346            transitive_noncrates = depset(
347                transitive = transitive_noncrates,
348                order = "topological",  # dylib link flag ordering matters.
349            ),
350            transitive_crate_outputs = depset(transitive = transitive_crate_outputs),
351            transitive_metadata_outputs = depset(transitive = transitive_metadata_outputs),
352            transitive_build_infos = depset(transitive = transitive_build_infos),
353            link_search_path_files = depset(transitive = transitive_link_search_paths),
354            dep_env = build_info.dep_env if build_info else None,
355        ),
356        build_info,
357        depset(transitive = linkstamps),
358    )
359
360def _collect_libs_from_linker_inputs(linker_inputs, use_pic):
361    # TODO: We could let the user choose how to link, instead of always preferring to link static libraries.
362    return [
363        get_preferred_artifact(lib, use_pic)
364        for li in linker_inputs
365        for lib in li.libraries
366    ]
367
368def _get_crate_and_dep_info(dep):
369    if type(dep) == "Target" and rust_common.crate_info in dep:
370        return (dep[rust_common.crate_info], dep[rust_common.dep_info])
371    elif type(dep) == "struct" and hasattr(dep, "crate_info"):
372        return (dep.crate_info, dep.dep_info)
373    return (None, None)
374
375def _get_cc_info(dep):
376    if type(dep) == "Target" and CcInfo in dep:
377        return dep[CcInfo]
378    elif type(dep) == "struct" and hasattr(dep, "cc_info"):
379        return dep.cc_info
380    return None
381
382def _get_build_info(dep):
383    if type(dep) == "Target" and BuildInfo in dep:
384        return dep[BuildInfo]
385    elif type(dep) == "struct" and hasattr(dep, "build_info"):
386        return dep.build_info
387    return None
388
389def get_cc_user_link_flags(ctx):
390    """Get the current target's linkopt flags
391
392    Args:
393        ctx (ctx): The current rule's context object
394
395    Returns:
396        depset: The flags passed to Bazel by --linkopt option.
397    """
398    return ctx.fragments.cpp.linkopts
399
400def get_linker_and_args(ctx, attr, crate_type, cc_toolchain, feature_configuration, rpaths, add_flags_for_binary = False):
401    """Gathers cc_common linker information
402
403    Args:
404        ctx (ctx): The current target's context object
405        attr (struct): Attributes to use in gathering linker args
406        crate_type (str): The target crate's type (i.e. "bin", "proc-macro", etc.).
407        cc_toolchain (CcToolchain): cc_toolchain for which we are creating build variables.
408        feature_configuration (FeatureConfiguration): Feature configuration to be queried.
409        rpaths (depset): Depset of directories where loader will look for libraries at runtime.
410        add_flags_for_binary (bool, optional): Whether to add "bin" link flags to the command regardless of `crate_type`.
411
412
413    Returns:
414        tuple: A tuple of the following items:
415            - (str): The tool path for given action.
416            - (sequence): A flattened command line flags for given action.
417            - (dict): Environment variables to be set for given action.
418    """
419    user_link_flags = get_cc_user_link_flags(ctx)
420
421    if crate_type in ("bin") or add_flags_for_binary:
422        is_linking_dynamic_library = False
423        action_name = CPP_LINK_EXECUTABLE_ACTION_NAME
424    elif crate_type in ("dylib"):
425        is_linking_dynamic_library = True
426        action_name = CPP_LINK_NODEPS_DYNAMIC_LIBRARY_ACTION_NAME
427    elif crate_type in ("staticlib"):
428        is_linking_dynamic_library = False
429        action_name = CPP_LINK_STATIC_LIBRARY_ACTION_NAME
430    elif crate_type in ("cdylib", "proc-macro"):
431        # Proc macros get compiled as shared libraries to be loaded by the compiler.
432        is_linking_dynamic_library = True
433        action_name = CPP_LINK_DYNAMIC_LIBRARY_ACTION_NAME
434    elif crate_type in ("lib", "rlib"):
435        fail("Invalid `crate_type` for linking action: {}".format(crate_type))
436    else:
437        fail("Unknown `crate_type`: {}".format(crate_type))
438
439    # Add linkopts from dependencies. This includes linkopts from transitive
440    # dependencies since they get merged up.
441    for dep in getattr(attr, "deps", []):
442        if CcInfo in dep and dep[CcInfo].linking_context:
443            for linker_input in dep[CcInfo].linking_context.linker_inputs.to_list():
444                for flag in linker_input.user_link_flags:
445                    user_link_flags.append(flag)
446    link_variables = cc_common.create_link_variables(
447        feature_configuration = feature_configuration,
448        cc_toolchain = cc_toolchain,
449        is_linking_dynamic_library = is_linking_dynamic_library,
450        runtime_library_search_directories = rpaths,
451        user_link_flags = user_link_flags,
452    )
453    link_args = cc_common.get_memory_inefficient_command_line(
454        feature_configuration = feature_configuration,
455        action_name = action_name,
456        variables = link_variables,
457    )
458    link_env = cc_common.get_environment_variables(
459        feature_configuration = feature_configuration,
460        action_name = action_name,
461        variables = link_variables,
462    )
463    ld = cc_common.get_tool_for_action(
464        feature_configuration = feature_configuration,
465        action_name = action_name,
466    )
467
468    return ld, link_args, link_env
469
470def _process_build_scripts(
471        build_info,
472        dep_info,
473        compile_inputs,
474        include_link_flags = True):
475    """Gathers the outputs from a target's `cargo_build_script` action.
476
477    Args:
478        build_info (BuildInfo): The target Build's dependency info.
479        dep_info (DepInfo): The Depinfo provider form the target Crate's set of inputs.
480        compile_inputs (depset): A set of all files that will participate in the build.
481        include_link_flags (bool, optional): Whether to include flags like `-l` that instruct the linker to search for a library.
482
483    Returns:
484        tuple: A tuple: A tuple of the following items:
485            - (depset[File]): A list of all build info `OUT_DIR` File objects
486            - (str): The `OUT_DIR` of the current build info
487            - (File): An optional path to a generated environment file from a `cargo_build_script` target
488            - (depset[File]): All direct and transitive build flags from the current build info.
489    """
490    extra_inputs, out_dir, build_env_file, build_flags_files = _create_extra_input_args(build_info, dep_info, include_link_flags = include_link_flags)
491    compile_inputs = depset(transitive = [extra_inputs, compile_inputs])
492    return compile_inputs, out_dir, build_env_file, build_flags_files
493
494def _symlink_for_ambiguous_lib(actions, toolchain, crate_info, lib):
495    """Constructs a disambiguating symlink for a library dependency.
496
497    Args:
498      actions (Actions): The rule's context actions object.
499      toolchain: The Rust toolchain object.
500      crate_info (CrateInfo): The target crate's info.
501      lib (File): The library to symlink to.
502
503    Returns:
504      (File): The disambiguating symlink for the library.
505    """
506    # FIXME: Once the relative order part of the native-link-modifiers rustc
507    # feature is stable, we should be able to eliminate the need to construct
508    # symlinks by passing the full paths to the libraries.
509    # https://github.com/rust-lang/rust/issues/81490.
510
511    # Take the absolute value of hash() since it could be negative.
512    path_hash = abs(hash(lib.path))
513    lib_name = get_lib_name_for_windows(lib) if toolchain.target_os.startswith("windows") else get_lib_name_default(lib)
514
515    if toolchain.target_os.startswith("windows"):
516        prefix = ""
517        extension = ".lib"
518    elif lib_name.endswith(".pic"):
519        # Strip the .pic suffix
520        lib_name = lib_name[:-4]
521        prefix = "lib"
522        extension = ".pic.a"
523    else:
524        prefix = "lib"
525        extension = ".a"
526
527    # Ensure the symlink follows the lib<name>.a pattern on Unix-like platforms
528    # or <name>.lib on Windows.
529    # Add a hash of the original library path to disambiguate libraries with the same basename.
530    symlink_name = "{}{}-{}{}".format(prefix, lib_name, path_hash, extension)
531
532    # Add the symlink to a target crate-specific _ambiguous_libs/ subfolder,
533    # to avoid possible collisions with sibling crates that may depend on the
534    # same ambiguous libraries.
535    symlink = actions.declare_file("_ambiguous_libs/" + crate_info.output.basename + "/" + symlink_name)
536    actions.symlink(
537        output = symlink,
538        target_file = lib,
539        progress_message = "Creating symlink to ambiguous lib: {}".format(lib.path),
540    )
541    return symlink
542
543def _disambiguate_libs(actions, toolchain, crate_info, dep_info, use_pic):
544    """Constructs disambiguating symlinks for ambiguous library dependencies.
545
546    The symlinks are all created in a _ambiguous_libs/ subfolder specific to
547    the target crate to avoid possible collisions with sibling crates that may
548    depend on the same ambiguous libraries.
549
550    Args:
551      actions (Actions): The rule's context actions object.
552      toolchain: The Rust toolchain object.
553      crate_info (CrateInfo): The target crate's info.
554      dep_info: (DepInfo): The target crate's dependency info.
555      use_pic: (boolean): Whether the build should use PIC.
556
557    Returns:
558      dict[String, File]: A mapping from ambiguous library paths to their
559        disambiguating symlink.
560    """
561    # FIXME: Once the relative order part of the native-link-modifiers rustc
562    # feature is stable, we should be able to eliminate the need to construct
563    # symlinks by passing the full paths to the libraries.
564    # https://github.com/rust-lang/rust/issues/81490.
565
566    # A dictionary from file paths of ambiguous libraries to the corresponding
567    # symlink.
568    ambiguous_libs = {}
569
570    # A dictionary maintaining a mapping from preferred library name to the
571    # last visited artifact with that name.
572    visited_libs = {}
573    for link_input in dep_info.transitive_noncrates.to_list():
574        for lib in link_input.libraries:
575            # FIXME: Dynamic libs are not disambiguated right now, there are
576            # cases where those have a non-standard name with version (e.g.,
577            # //test/unit/versioned_libs). We hope that the link modifiers
578            # stabilization will come before we need to make this work.
579            if _is_dylib(lib):
580                continue
581            artifact = get_preferred_artifact(lib, use_pic)
582            name = get_lib_name_for_windows(artifact) if toolchain.target_os.startswith("windows") else get_lib_name_default(artifact)
583
584            # On Linux-like platforms, normally library base names start with
585            # `lib`, following the pattern `lib[name].(a|lo)` and we pass
586            # -lstatic=name.
587            # On Windows, the base name looks like `name.lib` and we pass
588            # -lstatic=name.
589            # FIXME: Under the native-link-modifiers unstable rustc feature,
590            # we could use -lstatic:+verbatim instead.
591            needs_symlink_to_standardize_name = (
592                toolchain.target_os.startswith(("linux", "mac", "darwin")) and
593                artifact.basename.endswith(".a") and not artifact.basename.startswith("lib")
594            ) or (
595                toolchain.target_os.startswith("windows") and not artifact.basename.endswith(".lib")
596            )
597
598            # Detect cases where we need to disambiguate library dependencies
599            # by constructing symlinks.
600            if (
601                needs_symlink_to_standardize_name or
602                # We have multiple libraries with the same name.
603                (name in visited_libs and visited_libs[name].path != artifact.path)
604            ):
605                # Disambiguate the previously visited library (if we just detected
606                # that it is ambiguous) and the current library.
607                if name in visited_libs:
608                    old_path = visited_libs[name].path
609                    if old_path not in ambiguous_libs:
610                        ambiguous_libs[old_path] = _symlink_for_ambiguous_lib(actions, toolchain, crate_info, visited_libs[name])
611                ambiguous_libs[artifact.path] = _symlink_for_ambiguous_lib(actions, toolchain, crate_info, artifact)
612
613            visited_libs[name] = artifact
614    return ambiguous_libs
615
616def _depend_on_metadata(crate_info, force_depend_on_objects):
617    """Determines if we can depend on metadata for this crate.
618
619    By default (when pipelining is disabled or when the crate type needs to link against
620    objects) we depend on the set of object files (.rlib).
621    When pipelining is enabled and the crate type supports depending on metadata,
622    we depend on metadata files only (.rmeta).
623    In some rare cases, even if both of those conditions are true, we still want to
624    depend on objects. This is what force_depend_on_objects is.
625
626    Args:
627        crate_info (CrateInfo): The Crate to determine this for.
628        force_depend_on_objects (bool): if set we will not depend on metadata.
629
630    Returns:
631        Whether we can depend on metadata for this crate.
632    """
633    if force_depend_on_objects:
634        return False
635
636    return crate_info.type in ("rlib", "lib")
637
638def collect_inputs(
639        ctx,
640        file,
641        files,
642        linkstamps,
643        toolchain,
644        cc_toolchain,
645        feature_configuration,
646        crate_info,
647        dep_info,
648        build_info,
649        stamp = False,
650        force_depend_on_objects = False,
651        experimental_use_cc_common_link = False,
652        include_link_flags = True):
653    """Gather's the inputs and required input information for a rustc action
654
655    Args:
656        ctx (ctx): The rule's context object.
657        file (struct): A struct containing files defined in label type attributes marked as `allow_single_file`.
658        files (list): A list of all inputs (`ctx.files`).
659        linkstamps (depset): A depset of CcLinkstamps that need to be compiled and linked into all linked binaries.
660        toolchain (rust_toolchain): The current `rust_toolchain`.
661        cc_toolchain (CcToolchainInfo): The current `cc_toolchain`.
662        feature_configuration (FeatureConfiguration): Feature configuration to be queried.
663        crate_info (CrateInfo): The Crate information of the crate to process build scripts for.
664        dep_info (DepInfo): The target Crate's dependency information.
665        build_info (BuildInfo): The target Crate's build settings.
666        stamp (bool, optional): Whether or not workspace status stamping is enabled. For more details see
667            https://docs.bazel.build/versions/main/user-manual.html#flag--stamp
668        force_depend_on_objects (bool, optional): Forces dependencies of this rule to be objects rather than
669            metadata, even for libraries. This is used in rustdoc tests.
670        experimental_use_cc_common_link (bool, optional): Whether rules_rust uses cc_common.link to link
671            rust binaries.
672        include_link_flags (bool, optional): Whether to include flags like `-l` that instruct the linker to search for a library.
673
674    Returns:
675        tuple: A tuple: A tuple of the following items:
676            - (list): A list of all build info `OUT_DIR` File objects
677            - (str): The `OUT_DIR` of the current build info
678            - (File): An optional path to a generated environment file from a `cargo_build_script` target
679            - (depset[File]): All direct and transitive build flag files from the current build info
680            - (list[File]): Linkstamp outputs
681            - (dict[String, File]): Ambiguous libs, see `_disambiguate_libs`.
682    """
683    linker_script = getattr(file, "linker_script") if hasattr(file, "linker_script") else None
684
685    # TODO: As of writing this comment Bazel used Java CcToolchainInfo.
686    # However there is ongoing work to rewrite provider in Starlark.
687    # rules_rust is not coupled with Bazel release. Remove conditional and change to
688    # _linker_files once Starlark CcToolchainInfo is visible to Bazel.
689    # https://github.com/bazelbuild/rules_rust/issues/2425
690    if hasattr(cc_toolchain, "_linker_files"):
691        linker_depset = cc_toolchain._linker_files
692    else:
693        linker_depset = cc_toolchain.linker_files()
694    compilation_mode = ctx.var["COMPILATION_MODE"]
695
696    use_pic = _should_use_pic(cc_toolchain, feature_configuration, crate_info.type, compilation_mode)
697
698    # Pass linker inputs only for linking-like actions, not for example where
699    # the output is rlib. This avoids quadratic behavior where transitive noncrates are
700    # flattened on each transitive rust_library dependency.
701    additional_transitive_inputs = []
702    ambiguous_libs = {}
703    if crate_info.type not in ("lib", "rlib"):
704        linker_inputs = dep_info.transitive_noncrates.to_list()
705        ambiguous_libs = _disambiguate_libs(ctx.actions, toolchain, crate_info, dep_info, use_pic)
706        additional_transitive_inputs = _collect_libs_from_linker_inputs(linker_inputs, use_pic) + [
707            additional_input
708            for linker_input in linker_inputs
709            for additional_input in linker_input.additional_inputs
710        ] + ambiguous_libs.values()
711
712    # Compute linkstamps. Use the inputs of the binary as inputs to the
713    # linkstamp action to ensure linkstamps are rebuilt whenever binary inputs
714    # change.
715    linkstamp_outs = []
716
717    transitive_crate_outputs = dep_info.transitive_crate_outputs
718    if _depend_on_metadata(crate_info, force_depend_on_objects):
719        transitive_crate_outputs = dep_info.transitive_metadata_outputs
720
721    build_info_inputs = []
722    if build_info:
723        if build_info.rustc_env:
724            build_info_inputs.append(build_info.rustc_env)
725        if build_info.flags:
726            build_info_inputs.append(build_info.flags)
727
728    nolinkstamp_compile_inputs = depset(
729        getattr(files, "data", []) +
730        build_info_inputs +
731        ([toolchain.target_json] if toolchain.target_json else []) +
732        ([] if linker_script == None else [linker_script]),
733        transitive = [
734            linker_depset,
735            crate_info.srcs,
736            transitive_crate_outputs,
737            depset(additional_transitive_inputs),
738            crate_info.compile_data,
739            dep_info.transitive_proc_macro_data,
740            toolchain.all_files,
741        ],
742    )
743
744    # Register linkstamps when linking with rustc (when linking with
745    # cc_common.link linkstamps are handled by cc_common.link itself).
746    if not experimental_use_cc_common_link and crate_info.type in ("bin", "cdylib"):
747        # There is no other way to register an action for each member of a depset than
748        # flattening the depset as of 2021-10-12. Luckily, usually there is only one linkstamp
749        # in a build, and we only flatten the list on binary targets that perform transitive linking,
750        # so it's extremely unlikely that this call to `to_list()` will ever be a performance
751        # problem.
752        for linkstamp in linkstamps.to_list():
753            # The linkstamp output path is based on the binary crate
754            # name and the input linkstamp path. This is to disambiguate
755            # the linkstamp outputs produced by multiple binary crates
756            # that depend on the same linkstamp. We use the same pattern
757            # for the output name as the one used by native cc rules.
758            out_name = "_objs/" + crate_info.output.basename + "/" + linkstamp.file().path[:-len(linkstamp.file().extension)] + "o"
759            linkstamp_out = ctx.actions.declare_file(out_name)
760            linkstamp_outs.append(linkstamp_out)
761            cc_common.register_linkstamp_compile_action(
762                actions = ctx.actions,
763                cc_toolchain = cc_toolchain,
764                feature_configuration = feature_configuration,
765                source_file = linkstamp.file(),
766                output_file = linkstamp_out,
767                compilation_inputs = linkstamp.hdrs(),
768                inputs_for_validation = nolinkstamp_compile_inputs,
769                label_replacement = str(ctx.label),
770                output_replacement = crate_info.output.path,
771            )
772
773    # If stamping is enabled include the volatile and stable status info file
774    stamp_info = [ctx.version_file, ctx.info_file] if stamp else []
775
776    compile_inputs = depset(
777        linkstamp_outs + stamp_info,
778        transitive = [
779            nolinkstamp_compile_inputs,
780        ],
781    )
782
783    # For backwards compatibility, we also check the value of the `rustc_env_files` attribute when
784    # `crate_info.rustc_env_files` is not populated.
785    build_env_files = crate_info.rustc_env_files if crate_info.rustc_env_files else getattr(files, "rustc_env_files", [])
786    compile_inputs, out_dir, build_env_file, build_flags_files = _process_build_scripts(build_info, dep_info, compile_inputs, include_link_flags = include_link_flags)
787    if build_env_file:
788        build_env_files = [f for f in build_env_files] + [build_env_file]
789    compile_inputs = depset(build_env_files, transitive = [compile_inputs])
790    return compile_inputs, out_dir, build_env_files, build_flags_files, linkstamp_outs, ambiguous_libs
791
792def construct_arguments(
793        ctx,
794        attr,
795        file,
796        toolchain,
797        tool_path,
798        cc_toolchain,
799        feature_configuration,
800        crate_info,
801        dep_info,
802        linkstamp_outs,
803        ambiguous_libs,
804        output_hash,
805        rust_flags,
806        out_dir,
807        build_env_files,
808        build_flags_files,
809        emit = ["dep-info", "link"],
810        force_all_deps_direct = False,
811        add_flags_for_binary = False,
812        include_link_flags = True,
813        stamp = False,
814        remap_path_prefix = "",
815        use_json_output = False,
816        build_metadata = False,
817        force_depend_on_objects = False,
818        skip_expanding_rustc_env = False):
819    """Builds an Args object containing common rustc flags
820
821    Args:
822        ctx (ctx): The rule's context object
823        attr (struct): The attributes for the target. These may be different from ctx.attr in an aspect context.
824        file (struct): A struct containing files defined in label type attributes marked as `allow_single_file`.
825        toolchain (rust_toolchain): The current target's `rust_toolchain`
826        tool_path (str): Path to rustc
827        cc_toolchain (CcToolchain): The CcToolchain for the current target.
828        feature_configuration (FeatureConfiguration): Class used to construct command lines from CROSSTOOL features.
829        crate_info (CrateInfo): The CrateInfo provider of the target crate
830        dep_info (DepInfo): The DepInfo provider of the target crate
831        linkstamp_outs (list): Linkstamp outputs of native dependencies
832        ambiguous_libs (dict): Ambiguous libs, see `_disambiguate_libs`
833        output_hash (str): The hashed path of the crate root
834        rust_flags (list): Additional flags to pass to rustc
835        out_dir (str): The path to the output directory for the target Crate.
836        build_env_files (list): Files containing rustc environment variables, for instance from `cargo_build_script` actions.
837        build_flags_files (depset): The output files of a `cargo_build_script` actions containing rustc build flags
838        emit (list): Values for the --emit flag to rustc.
839        force_all_deps_direct (bool, optional): Whether to pass the transitive rlibs with --extern
840            to the commandline as opposed to -L.
841        add_flags_for_binary (bool, optional): Whether to add "bin" link flags to the command regardless of `emit` and `crate_type`.
842        include_link_flags (bool, optional): Whether to include flags like `-l` that instruct the linker to search for a library.
843        stamp (bool, optional): Whether or not workspace status stamping is enabled. For more details see
844            https://docs.bazel.build/versions/main/user-manual.html#flag--stamp
845        remap_path_prefix (str, optional): A value used to remap `${pwd}` to. If set to None, no prefix will be set.
846        use_json_output (bool): Have rustc emit json and process_wrapper parse json messages to output rendered output.
847        build_metadata (bool): Generate CLI arguments for building *only* .rmeta files. This requires use_json_output.
848        force_depend_on_objects (bool): Force using `.rlib` object files instead of metadata (`.rmeta`) files even if they are available.
849        skip_expanding_rustc_env (bool): Whether to skip expanding CrateInfo.rustc_env_attr
850
851    Returns:
852        tuple: A tuple of the following items
853            - (struct): A struct of arguments used to run the `Rustc` action
854                - process_wrapper_flags (Args): Arguments for the process wrapper
855                - rustc_path (Args): Arguments for invoking rustc via the process wrapper
856                - rustc_flags (Args): Rust flags for the Rust compiler
857                - all (list): A list of all `Args` objects in the order listed above.
858                    This is to be passed to the `arguments` parameter of actions
859            - (dict): Common rustc environment variables
860    """
861    if build_metadata and not use_json_output:
862        fail("build_metadata requires parse_json_output")
863
864    output_dir = getattr(crate_info.output, "dirname", None)
865    linker_script = getattr(file, "linker_script", None)
866
867    env = _get_rustc_env(attr, toolchain, crate_info.name)
868
869    # Wrapper args first
870    process_wrapper_flags = ctx.actions.args()
871
872    for build_env_file in build_env_files:
873        process_wrapper_flags.add("--env-file", build_env_file)
874
875    process_wrapper_flags.add_all(build_flags_files, before_each = "--arg-file")
876
877    # Certain rust build processes expect to find files from the environment
878    # variable `$CARGO_MANIFEST_DIR`. Examples of this include pest, tera,
879    # asakuma.
880    #
881    # The compiler and by extension proc-macros see the current working
882    # directory as the Bazel exec root. This is what `$CARGO_MANIFEST_DIR`
883    # would default to but is often the wrong value (e.g. if the source is in a
884    # sub-package or if we are building something in an external repository).
885    # Hence, we need to set `CARGO_MANIFEST_DIR` explicitly.
886    #
887    # Since we cannot get the `exec_root` from starlark, we cheat a little and
888    # use `${pwd}` which resolves the `exec_root` at action execution time.
889    process_wrapper_flags.add("--subst", "pwd=${pwd}")
890
891    # If stamping is enabled, enable the functionality in the process wrapper
892    if stamp:
893        process_wrapper_flags.add("--volatile-status-file", ctx.version_file)
894        process_wrapper_flags.add("--stable-status-file", ctx.info_file)
895
896    # Both ctx.label.workspace_root and ctx.label.package are relative paths
897    # and either can be empty strings. Avoid trailing/double slashes in the path.
898    components = "${{pwd}}/{}/{}".format(ctx.label.workspace_root, ctx.label.package).split("/")
899    env["CARGO_MANIFEST_DIR"] = "/".join([c for c in components if c])
900
901    if out_dir != None:
902        env["OUT_DIR"] = "${pwd}/" + out_dir
903
904    # Arguments for launching rustc from the process wrapper
905    rustc_path = ctx.actions.args()
906    rustc_path.add("--")
907    rustc_path.add(tool_path)
908
909    # Rustc arguments
910    rustc_flags = ctx.actions.args()
911    rustc_flags.set_param_file_format("multiline")
912    rustc_flags.use_param_file("@%s", use_always = False)
913    rustc_flags.add(crate_info.root)
914    rustc_flags.add(crate_info.name, format = "--crate-name=%s")
915    rustc_flags.add(crate_info.type, format = "--crate-type=%s")
916
917    error_format = "human"
918    if hasattr(attr, "_error_format"):
919        error_format = attr._error_format[ErrorFormatInfo].error_format
920
921    if use_json_output:
922        # If --error-format was set to json, we just pass the output through
923        # Otherwise process_wrapper uses the "rendered" field.
924        process_wrapper_flags.add("--rustc-output-format", "json" if error_format == "json" else "rendered")
925
926        # Configure rustc json output by adding artifact notifications.
927        # These will always be filtered out by process_wrapper and will be use to terminate
928        # rustc when appropriate.
929        json = ["artifacts"]
930        if error_format == "short":
931            json.append("diagnostic-short")
932        elif error_format == "human" and toolchain.target_os != "windows":
933            # If the os is not windows, we can get colorized output.
934            json.append("diagnostic-rendered-ansi")
935
936        rustc_flags.add_joined(json, format_joined = "--json=%s", join_with = ",")
937
938        error_format = "json"
939
940    if build_metadata:
941        # Configure process_wrapper to terminate rustc when metadata are emitted
942        process_wrapper_flags.add("--rustc-quit-on-rmeta", "true")
943        if crate_info.rustc_rmeta_output:
944            process_wrapper_flags.add("--output-file", crate_info.rustc_rmeta_output.path)
945    elif crate_info.rustc_output:
946        process_wrapper_flags.add("--output-file", crate_info.rustc_output.path)
947
948    rustc_flags.add(error_format, format = "--error-format=%s")
949
950    # Mangle symbols to disambiguate crates with the same name. This could
951    # happen only for non-final artifacts where we compute an output_hash,
952    # e.g., rust_library.
953    #
954    # For "final" artifacts and ones intended for distribution outside of
955    # Bazel, such as rust_binary, rust_static_library and rust_shared_library,
956    # where output_hash is None we don't need to add these flags.
957    if output_hash:
958        rustc_flags.add(output_hash, format = "--codegen=metadata=-%s")
959        rustc_flags.add(output_hash, format = "--codegen=extra-filename=-%s")
960
961    if output_dir:
962        rustc_flags.add(output_dir, format = "--out-dir=%s")
963
964    compilation_mode = get_compilation_mode_opts(ctx, toolchain)
965    rustc_flags.add(compilation_mode.opt_level, format = "--codegen=opt-level=%s")
966    rustc_flags.add(compilation_mode.debug_info, format = "--codegen=debuginfo=%s")
967    rustc_flags.add(compilation_mode.strip_level, format = "--codegen=strip=%s")
968
969    # For determinism to help with build distribution and such
970    if remap_path_prefix != None:
971        rustc_flags.add("--remap-path-prefix=${{pwd}}={}".format(remap_path_prefix))
972
973    emit_without_paths = []
974    for kind in emit:
975        if kind == "link" and crate_info.type == "bin" and crate_info.output != None:
976            rustc_flags.add(crate_info.output, format = "--emit=link=%s")
977        else:
978            emit_without_paths.append(kind)
979
980    if emit_without_paths:
981        rustc_flags.add_joined(emit_without_paths, format_joined = "--emit=%s", join_with = ",")
982    if error_format != "json":
983        # Color is not compatible with json output.
984        rustc_flags.add("--color=always")
985    rustc_flags.add(toolchain.target_flag_value, format = "--target=%s")
986    if hasattr(attr, "crate_features"):
987        rustc_flags.add_all(getattr(attr, "crate_features"), before_each = "--cfg", format_each = 'feature="%s"')
988    if linker_script:
989        rustc_flags.add(linker_script, format = "--codegen=link-arg=-T%s")
990
991    # Tell Rustc where to find the standard library (or libcore)
992    rustc_flags.add_all(toolchain.rust_std_paths, before_each = "-L", format_each = "%s")
993    rustc_flags.add_all(rust_flags)
994
995    # Gather data path from crate_info since it is inherited from real crate for rust_doc and rust_test
996    # Deduplicate data paths due to https://github.com/bazelbuild/bazel/issues/14681
997    data_paths = depset(direct = getattr(attr, "data", []), transitive = [crate_info.compile_data_targets]).to_list()
998
999    rustc_flags.add_all(
1000        expand_list_element_locations(
1001            ctx,
1002            getattr(attr, "rustc_flags", []),
1003            data_paths,
1004        ),
1005    )
1006    add_edition_flags(rustc_flags, crate_info)
1007
1008    # Link!
1009    if ("link" in emit and crate_info.type not in ["rlib", "lib"]) or add_flags_for_binary:
1010        # Rust's built-in linker can handle linking wasm files. We don't want to attempt to use the cc
1011        # linker since it won't understand.
1012        compilation_mode = ctx.var["COMPILATION_MODE"]
1013        if toolchain.target_arch != "wasm32":
1014            if output_dir:
1015                use_pic = _should_use_pic(cc_toolchain, feature_configuration, crate_info.type, compilation_mode)
1016                rpaths = _compute_rpaths(toolchain, output_dir, dep_info, use_pic)
1017            else:
1018                rpaths = depset()
1019
1020            ld, link_args, link_env = get_linker_and_args(ctx, attr, crate_info.type, cc_toolchain, feature_configuration, rpaths, add_flags_for_binary = add_flags_for_binary)
1021
1022            env.update(link_env)
1023            rustc_flags.add(ld, format = "--codegen=linker=%s")
1024
1025            # Split link args into individual "--codegen=link-arg=" flags to handle nested spaces.
1026            # Additional context: https://github.com/rust-lang/rust/pull/36574
1027            rustc_flags.add_all(link_args, format_each = "--codegen=link-arg=%s")
1028
1029        _add_native_link_flags(rustc_flags, dep_info, linkstamp_outs, ambiguous_libs, crate_info.type, toolchain, cc_toolchain, feature_configuration, compilation_mode, include_link_flags = include_link_flags)
1030
1031    use_metadata = _depend_on_metadata(crate_info, force_depend_on_objects)
1032
1033    # These always need to be added, even if not linking this crate.
1034    add_crate_link_flags(rustc_flags, dep_info, force_all_deps_direct, use_metadata)
1035
1036    needs_extern_proc_macro_flag = _is_proc_macro(crate_info) and crate_info.edition != "2015"
1037    if needs_extern_proc_macro_flag:
1038        rustc_flags.add("--extern")
1039        rustc_flags.add("proc_macro")
1040
1041    if toolchain.llvm_cov and ctx.configuration.coverage_enabled:
1042        # https://doc.rust-lang.org/rustc/instrument-coverage.html
1043        rustc_flags.add("--codegen=instrument-coverage")
1044
1045    if toolchain._experimental_link_std_dylib:
1046        rustc_flags.add("--codegen=prefer-dynamic")
1047
1048    # Make bin crate data deps available to tests.
1049    for data in getattr(attr, "data", []):
1050        if rust_common.crate_info in data:
1051            dep_crate_info = data[rust_common.crate_info]
1052            if dep_crate_info.type == "bin":
1053                # Trying to make CARGO_BIN_EXE_{} canonical across platform by strip out extension if exists
1054                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
1055                env["CARGO_BIN_EXE_" + env_basename] = dep_crate_info.output.short_path
1056
1057    # Add environment variables from the Rust toolchain.
1058    env.update(toolchain.env)
1059
1060    # Update environment with user provided variables.
1061    if skip_expanding_rustc_env:
1062        env.update(crate_info.rustc_env)
1063    else:
1064        env.update(expand_dict_value_locations(
1065            ctx,
1066            crate_info.rustc_env,
1067            data_paths,
1068        ))
1069
1070    # Ensure the sysroot is set for the target platform
1071    if not toolchain._incompatible_no_rustc_sysroot_env:
1072        env["SYSROOT"] = toolchain.sysroot
1073    if toolchain._experimental_toolchain_generated_sysroot:
1074        rustc_flags.add(toolchain.sysroot, format = "--sysroot=%s")
1075
1076    if toolchain._rename_first_party_crates:
1077        env["RULES_RUST_THIRD_PARTY_DIR"] = toolchain._third_party_dir
1078
1079    if crate_info.type in toolchain.extra_rustc_flags_for_crate_types.keys():
1080        rustc_flags.add_all(toolchain.extra_rustc_flags_for_crate_types[crate_info.type])
1081
1082    if is_exec_configuration(ctx):
1083        rustc_flags.add_all(toolchain.extra_exec_rustc_flags)
1084    else:
1085        rustc_flags.add_all(toolchain.extra_rustc_flags)
1086
1087    # extra_rustc_flags apply to the target configuration, not the exec configuration.
1088    if hasattr(ctx.attr, "_extra_rustc_flags") and not is_exec_configuration(ctx):
1089        rustc_flags.add_all(ctx.attr._extra_rustc_flags[ExtraRustcFlagsInfo].extra_rustc_flags)
1090
1091    if hasattr(ctx.attr, "_extra_rustc_flag") and not is_exec_configuration(ctx):
1092        rustc_flags.add_all(ctx.attr._extra_rustc_flag[ExtraRustcFlagsInfo].extra_rustc_flags)
1093
1094    if hasattr(ctx.attr, "_per_crate_rustc_flag") and not is_exec_configuration(ctx):
1095        per_crate_rustc_flags = ctx.attr._per_crate_rustc_flag[PerCrateRustcFlagsInfo].per_crate_rustc_flags
1096        _add_per_crate_rustc_flags(ctx, rustc_flags, crate_info, per_crate_rustc_flags)
1097
1098    if hasattr(ctx.attr, "_extra_exec_rustc_flags") and is_exec_configuration(ctx):
1099        rustc_flags.add_all(ctx.attr._extra_exec_rustc_flags[ExtraExecRustcFlagsInfo].extra_exec_rustc_flags)
1100
1101    if hasattr(ctx.attr, "_extra_exec_rustc_flag") and is_exec_configuration(ctx):
1102        rustc_flags.add_all(ctx.attr._extra_exec_rustc_flag[ExtraExecRustcFlagsInfo].extra_exec_rustc_flags)
1103
1104    if _is_no_std(ctx, toolchain, crate_info):
1105        rustc_flags.add('--cfg=feature="no_std"')
1106
1107    # Needed for bzlmod-aware runfiles resolution.
1108    env["REPOSITORY_NAME"] = ctx.label.workspace_name
1109
1110    # Create a struct which keeps the arguments separate so each may be tuned or
1111    # replaced where necessary
1112    args = struct(
1113        process_wrapper_flags = process_wrapper_flags,
1114        rustc_path = rustc_path,
1115        rustc_flags = rustc_flags,
1116        all = [process_wrapper_flags, rustc_path, rustc_flags],
1117    )
1118
1119    return args, env
1120
1121def rustc_compile_action(
1122        ctx,
1123        attr,
1124        toolchain,
1125        rust_flags = [],
1126        output_hash = None,
1127        force_all_deps_direct = False,
1128        crate_info_dict = None,
1129        skip_expanding_rustc_env = False,
1130        include_coverage = True):
1131    """Create and run a rustc compile action based on the current rule's attributes
1132
1133    Args:
1134        ctx (ctx): The rule's context object
1135        attr (struct): Attributes to use for the rust compile action
1136        toolchain (rust_toolchain): The current `rust_toolchain`
1137        output_hash (str, optional): The hashed path of the crate root. Defaults to None.
1138        rust_flags (list, optional): Additional flags to pass to rustc. Defaults to [].
1139        force_all_deps_direct (bool, optional): Whether to pass the transitive rlibs with --extern
1140            to the commandline as opposed to -L.
1141        crate_info_dict: A mutable dict used to create CrateInfo provider
1142        skip_expanding_rustc_env (bool, optional): Whether to expand CrateInfo.rustc_env
1143        include_coverage (bool, optional): Whether to generate coverage information or not.
1144
1145    Returns:
1146        list: A list of the following providers:
1147            - (CrateInfo): info for the crate we just built; same as `crate_info` parameter.
1148            - (DepInfo): The transitive dependencies of this crate.
1149            - (DefaultInfo): The output file for this crate, and its runfiles.
1150    """
1151    crate_info = rust_common.create_crate_info(**crate_info_dict)
1152
1153    build_metadata = crate_info_dict.get("metadata", None)
1154    rustc_output = crate_info_dict.get("rustc_output", None)
1155    rustc_rmeta_output = crate_info_dict.get("rustc_rmeta_output", None)
1156
1157    # Determine whether to use cc_common.link:
1158    #  * either if experimental_use_cc_common_link is 1,
1159    #  * or if experimental_use_cc_common_link is -1 and
1160    #    the toolchain experimental_use_cc_common_link is true.
1161    experimental_use_cc_common_link = False
1162    if hasattr(ctx.attr, "experimental_use_cc_common_link"):
1163        if ctx.attr.experimental_use_cc_common_link == 0:
1164            experimental_use_cc_common_link = False
1165        elif ctx.attr.experimental_use_cc_common_link == 1:
1166            experimental_use_cc_common_link = True
1167        elif ctx.attr.experimental_use_cc_common_link == -1:
1168            experimental_use_cc_common_link = toolchain._experimental_use_cc_common_link
1169
1170    dep_info, build_info, linkstamps = collect_deps(
1171        deps = crate_info_dict["deps"],
1172        proc_macro_deps = crate_info_dict["proc_macro_deps"],
1173        aliases = crate_info_dict["aliases"],
1174    )
1175    extra_disabled_features = [RUST_LINK_CC_FEATURE]
1176    if crate_info.type in ["bin", "cdylib"] and dep_info.transitive_noncrates.to_list():
1177        # One or more of the transitive deps is a cc_library / cc_import
1178        extra_disabled_features = []
1179    cc_toolchain, feature_configuration = find_cc_toolchain(ctx, extra_disabled_features)
1180    if not _are_linkstamps_supported(
1181        feature_configuration = feature_configuration,
1182        has_grep_includes = hasattr(ctx.attr, "_use_grep_includes"),
1183    ):
1184        linkstamps = depset([])
1185
1186    # Determine if the build is currently running with --stamp
1187    stamp = is_stamping_enabled(attr)
1188
1189    compile_inputs, out_dir, build_env_files, build_flags_files, linkstamp_outs, ambiguous_libs = collect_inputs(
1190        ctx = ctx,
1191        file = ctx.file,
1192        files = ctx.files,
1193        linkstamps = linkstamps,
1194        toolchain = toolchain,
1195        cc_toolchain = cc_toolchain,
1196        feature_configuration = feature_configuration,
1197        crate_info = crate_info,
1198        dep_info = dep_info,
1199        build_info = build_info,
1200        stamp = stamp,
1201        experimental_use_cc_common_link = experimental_use_cc_common_link,
1202    )
1203
1204    # The types of rustc outputs to emit.
1205    # If we build metadata, we need to keep the command line of the two invocations
1206    # (rlib and rmeta) as similar as possible, otherwise rustc rejects the rmeta as
1207    # a candidate.
1208    # Because of that we need to add emit=metadata to both the rlib and rmeta invocation.
1209    #
1210    # When cc_common linking is enabled, emit a `.o` file, which is later
1211    # passed to the cc_common.link action.
1212    emit = ["dep-info", "link"]
1213    if build_metadata:
1214        emit.append("metadata")
1215    if experimental_use_cc_common_link:
1216        emit = ["obj"]
1217
1218    args, env_from_args = construct_arguments(
1219        ctx = ctx,
1220        attr = attr,
1221        file = ctx.file,
1222        toolchain = toolchain,
1223        tool_path = toolchain.rustc.path,
1224        cc_toolchain = cc_toolchain,
1225        emit = emit,
1226        feature_configuration = feature_configuration,
1227        crate_info = crate_info,
1228        dep_info = dep_info,
1229        linkstamp_outs = linkstamp_outs,
1230        ambiguous_libs = ambiguous_libs,
1231        output_hash = output_hash,
1232        rust_flags = rust_flags,
1233        out_dir = out_dir,
1234        build_env_files = build_env_files,
1235        build_flags_files = build_flags_files,
1236        force_all_deps_direct = force_all_deps_direct,
1237        stamp = stamp,
1238        use_json_output = bool(build_metadata) or bool(rustc_output) or bool(rustc_rmeta_output),
1239        skip_expanding_rustc_env = skip_expanding_rustc_env,
1240    )
1241
1242    args_metadata = None
1243    if build_metadata:
1244        args_metadata, _ = construct_arguments(
1245            ctx = ctx,
1246            attr = attr,
1247            file = ctx.file,
1248            toolchain = toolchain,
1249            tool_path = toolchain.rustc.path,
1250            cc_toolchain = cc_toolchain,
1251            emit = emit,
1252            feature_configuration = feature_configuration,
1253            crate_info = crate_info,
1254            dep_info = dep_info,
1255            linkstamp_outs = linkstamp_outs,
1256            ambiguous_libs = ambiguous_libs,
1257            output_hash = output_hash,
1258            rust_flags = rust_flags,
1259            out_dir = out_dir,
1260            build_env_files = build_env_files,
1261            build_flags_files = build_flags_files,
1262            force_all_deps_direct = force_all_deps_direct,
1263            stamp = stamp,
1264            use_json_output = True,
1265            build_metadata = True,
1266        )
1267
1268    env = dict(ctx.configuration.default_shell_env)
1269
1270    # this is the final list of env vars
1271    env.update(env_from_args)
1272
1273    if hasattr(attr, "version") and attr.version != "0.0.0":
1274        formatted_version = " v{}".format(attr.version)
1275    else:
1276        formatted_version = ""
1277
1278    # Declares the outputs of the rustc compile action.
1279    # By default this is the binary output; if cc_common.link is used, this is
1280    # the main `.o` file (`output_o` below).
1281    outputs = [crate_info.output]
1282
1283    # The `.o` output file, only used for linking via cc_common.link.
1284    output_o = None
1285    if experimental_use_cc_common_link:
1286        obj_ext = ".o"
1287        output_o = ctx.actions.declare_file(crate_info.name + obj_ext, sibling = crate_info.output)
1288        outputs = [output_o]
1289
1290    # For a cdylib that might be added as a dependency to a cc_* target on Windows, it is important to include the
1291    # interface library that rustc generates in the output files.
1292    interface_library = None
1293    if toolchain.target_os == "windows" and crate_info.type == "cdylib":
1294        # Rustc generates the import library with a `.dll.lib` extension rather than the usual `.lib` one that msvc
1295        # expects (see https://github.com/rust-lang/rust/pull/29520 for more context).
1296        interface_library = ctx.actions.declare_file(crate_info.output.basename + ".lib", sibling = crate_info.output)
1297        outputs.append(interface_library)
1298
1299    # The action might generate extra output that we don't want to include in the `DefaultInfo` files.
1300    action_outputs = list(outputs)
1301    if rustc_output:
1302        action_outputs.append(rustc_output)
1303
1304    # Get the compilation mode for the current target.
1305    compilation_mode = get_compilation_mode_opts(ctx, toolchain)
1306
1307    # Rustc generates a pdb file (on Windows) or a dsym folder (on macos) so provide it in an output group for crate
1308    # types that benefit from having debug information in a separate file.
1309    pdb_file = None
1310    dsym_folder = None
1311    if crate_info.type in ("cdylib", "bin"):
1312        if toolchain.target_os == "windows" and compilation_mode.strip_level == "none":
1313            pdb_file = ctx.actions.declare_file(crate_info.output.basename[:-len(crate_info.output.extension)] + "pdb", sibling = crate_info.output)
1314            action_outputs.append(pdb_file)
1315        elif toolchain.target_os == "darwin":
1316            dsym_folder = ctx.actions.declare_directory(crate_info.output.basename + ".dSYM", sibling = crate_info.output)
1317            action_outputs.append(dsym_folder)
1318
1319    if ctx.executable._process_wrapper:
1320        # Run as normal
1321        ctx.actions.run(
1322            executable = ctx.executable._process_wrapper,
1323            inputs = compile_inputs,
1324            outputs = action_outputs,
1325            env = env,
1326            arguments = args.all,
1327            mnemonic = "Rustc",
1328            progress_message = "Compiling Rust {} {}{} ({} files)".format(
1329                crate_info.type,
1330                ctx.label.name,
1331                formatted_version,
1332                len(crate_info.srcs.to_list()),
1333            ),
1334            toolchain = "@rules_rust//rust:toolchain_type",
1335        )
1336        if args_metadata:
1337            ctx.actions.run(
1338                executable = ctx.executable._process_wrapper,
1339                inputs = compile_inputs,
1340                outputs = [build_metadata] + [x for x in [rustc_rmeta_output] if x],
1341                env = env,
1342                arguments = args_metadata.all,
1343                mnemonic = "RustcMetadata",
1344                progress_message = "Compiling Rust metadata {} {}{} ({} files)".format(
1345                    crate_info.type,
1346                    ctx.label.name,
1347                    formatted_version,
1348                    len(crate_info.srcs.to_list()),
1349                ),
1350                toolchain = "@rules_rust//rust:toolchain_type",
1351            )
1352    elif hasattr(ctx.executable, "_bootstrap_process_wrapper"):
1353        # Run without process_wrapper
1354        if build_env_files or build_flags_files or stamp or build_metadata:
1355            fail("build_env_files, build_flags_files, stamp, build_metadata are not supported when building without process_wrapper")
1356        ctx.actions.run(
1357            executable = ctx.executable._bootstrap_process_wrapper,
1358            inputs = compile_inputs,
1359            outputs = action_outputs,
1360            env = env,
1361            arguments = [args.rustc_path, args.rustc_flags],
1362            mnemonic = "Rustc",
1363            progress_message = "Compiling Rust (without process_wrapper) {} {}{} ({} files)".format(
1364                crate_info.type,
1365                ctx.label.name,
1366                formatted_version,
1367                len(crate_info.srcs.to_list()),
1368            ),
1369            toolchain = "@rules_rust//rust:toolchain_type",
1370        )
1371    else:
1372        fail("No process wrapper was defined for {}".format(ctx.label))
1373
1374    if experimental_use_cc_common_link:
1375        # Wrap the main `.o` file into a compilation output suitable for
1376        # cc_common.link. The main `.o` file is useful in both PIC and non-PIC
1377        # modes.
1378        compilation_outputs = cc_common.create_compilation_outputs(
1379            objects = depset([output_o]),
1380            pic_objects = depset([output_o]),
1381        )
1382
1383        malloc_library = ctx.attr._custom_malloc or ctx.attr.malloc
1384
1385        # Collect the linking contexts of the standard library and dependencies.
1386        linking_contexts = [
1387            malloc_library[CcInfo].linking_context,
1388            _get_std_and_alloc_info(ctx, toolchain, crate_info).linking_context,
1389            toolchain.stdlib_linkflags.linking_context,
1390        ]
1391
1392        for dep in crate_info.deps.to_list():
1393            if dep.cc_info:
1394                linking_contexts.append(dep.cc_info.linking_context)
1395
1396        # In the cc_common.link action we need to pass the name of the final
1397        # binary (output) relative to the package of this target.
1398        # We compute it by stripping the path to the package directory,
1399        # which is a prefix of the path of `crate_info.output`.
1400
1401        # The path to the package dir, including a trailing "/".
1402        package_dir = ctx.bin_dir.path + "/"
1403
1404        # For external repositories, workspace root is not part of the output
1405        # path when sibling repository layout is used (the repository name is
1406        # part of the bin_dir). This scenario happens when the workspace root
1407        # starts with "../"
1408        if ctx.label.workspace_root and not ctx.label.workspace_root.startswith("../"):
1409            package_dir = package_dir + ctx.label.workspace_root + "/"
1410        if ctx.label.package:
1411            package_dir = package_dir + ctx.label.package + "/"
1412
1413        if not crate_info.output.path.startswith(package_dir):
1414            fail("The package dir path", package_dir, "should be a prefix of the crate_info.output.path", crate_info.output.path)
1415
1416        output_relative_to_package = crate_info.output.path[len(package_dir):]
1417
1418        # Compile actions that produce shared libraries create output of the form "libfoo.so" for linux and macos;
1419        # cc_common.link expects us to pass "foo" to the name parameter. We cannot simply use crate_info.name because
1420        # the name of the crate does not always match the name of output file, e.g a crate named foo-bar will produce
1421        # a (lib)foo_bar output file.
1422        if crate_info.type == "cdylib":
1423            output_lib = crate_info.output.basename
1424            if toolchain.target_os != "windows":
1425                # Strip the leading "lib" prefix
1426                output_lib = output_lib[3:]
1427
1428            # Strip the file extension
1429            output_lib = output_lib[:-(1 + len(crate_info.output.extension))]
1430
1431            # Remove the basename (which contains the undesired 'lib' prefix and the file extension)
1432            output_relative_to_package = output_relative_to_package[:-len(crate_info.output.basename)]
1433
1434            # Append the name of the library
1435            output_relative_to_package = output_relative_to_package + output_lib
1436
1437        cc_common.link(
1438            actions = ctx.actions,
1439            feature_configuration = feature_configuration,
1440            cc_toolchain = cc_toolchain,
1441            linking_contexts = linking_contexts,
1442            compilation_outputs = compilation_outputs,
1443            name = output_relative_to_package,
1444            stamp = ctx.attr.stamp,
1445            output_type = "executable" if crate_info.type == "bin" else "dynamic_library",
1446        )
1447
1448        outputs = [crate_info.output]
1449
1450    coverage_runfiles = []
1451    if toolchain.llvm_cov and ctx.configuration.coverage_enabled and crate_info.is_test:
1452        coverage_runfiles = [toolchain.llvm_cov, toolchain.llvm_profdata]
1453
1454    experimental_use_coverage_metadata_files = toolchain._experimental_use_coverage_metadata_files
1455
1456    dynamic_libraries = [
1457        library_to_link.dynamic_library
1458        for dep in getattr(ctx.attr, "deps", [])
1459        if CcInfo in dep
1460        for linker_input in dep[CcInfo].linking_context.linker_inputs.to_list()
1461        for library_to_link in linker_input.libraries
1462        if _is_dylib(library_to_link)
1463    ]
1464    runfiles = ctx.runfiles(
1465        files = getattr(ctx.files, "data", []) +
1466                ([] if experimental_use_coverage_metadata_files else coverage_runfiles) +
1467                dynamic_libraries,
1468        collect_data = True,
1469    )
1470    if getattr(ctx.attr, "crate", None):
1471        runfiles = runfiles.merge(ctx.attr.crate[DefaultInfo].default_runfiles)
1472        runfiles = runfiles.merge(ctx.attr.crate[DefaultInfo].data_runfiles)
1473
1474    # TODO: Remove after some resolution to
1475    # https://github.com/bazelbuild/rules_rust/issues/771
1476    out_binary = getattr(attr, "out_binary", False)
1477
1478    executable = crate_info.output if crate_info.type == "bin" or crate_info.is_test or out_binary else None
1479
1480    instrumented_files_kwargs = {
1481        "dependency_attributes": ["deps", "crate"],
1482        "extensions": ["rs"],
1483        "source_attributes": ["srcs"],
1484    }
1485
1486    if experimental_use_coverage_metadata_files:
1487        instrumented_files_kwargs.update({
1488            "metadata_files": coverage_runfiles + [executable] if executable else [],
1489        })
1490
1491    providers = [
1492        DefaultInfo(
1493            # nb. This field is required for cc_library to depend on our output.
1494            files = depset(outputs),
1495            runfiles = runfiles,
1496            executable = executable,
1497        ),
1498    ]
1499
1500    # When invoked by aspects (and when running `bazel coverage`), the
1501    # baseline_coverage.dat created here will conflict with the baseline_coverage.dat of the
1502    # underlying target, which is a build failure. So we add an option to disable it so that this
1503    # function can be invoked from aspects for rules that have its own InstrumentedFilesInfo.
1504    if include_coverage:
1505        providers.append(
1506            coverage_common.instrumented_files_info(
1507                ctx,
1508                **instrumented_files_kwargs
1509            ),
1510        )
1511
1512    if crate_info_dict != None:
1513        crate_info_dict.update({
1514            "rustc_env": env,
1515        })
1516        crate_info = rust_common.create_crate_info(**crate_info_dict)
1517
1518    if crate_info.type in ["staticlib", "cdylib"]:
1519        # These rules are not supposed to be depended on by other rust targets, and
1520        # as such they shouldn't provide a CrateInfo. However, one may still want to
1521        # write a rust_test for them, so we provide the CrateInfo wrapped in a provider
1522        # that rust_test understands.
1523        providers.extend([rust_common.test_crate_info(crate = crate_info), dep_info])
1524    else:
1525        providers.extend([crate_info, dep_info])
1526
1527    providers += establish_cc_info(ctx, attr, crate_info, toolchain, cc_toolchain, feature_configuration, interface_library)
1528
1529    output_group_info = {}
1530
1531    if pdb_file:
1532        output_group_info["pdb_file"] = depset([pdb_file])
1533    if dsym_folder:
1534        output_group_info["dsym_folder"] = depset([dsym_folder])
1535    if build_metadata:
1536        output_group_info["build_metadata"] = depset([build_metadata])
1537    if build_metadata:
1538        output_group_info["build_metadata"] = depset([build_metadata])
1539        if rustc_rmeta_output:
1540            output_group_info["rustc_rmeta_output"] = depset([rustc_rmeta_output])
1541    if rustc_output:
1542        output_group_info["rustc_output"] = depset([rustc_output])
1543
1544    if output_group_info:
1545        providers.append(OutputGroupInfo(**output_group_info))
1546
1547    return providers
1548
1549def _is_no_std(ctx, toolchain, crate_info):
1550    if is_exec_configuration(ctx) or crate_info.is_test:
1551        return False
1552    if toolchain._no_std == "off":
1553        return False
1554    return True
1555
1556def _get_std_and_alloc_info(ctx, toolchain, crate_info):
1557    if is_exec_configuration(ctx):
1558        return toolchain.libstd_and_allocator_ccinfo
1559    if toolchain._experimental_use_global_allocator:
1560        if _is_no_std(ctx, toolchain, crate_info):
1561            return toolchain.nostd_and_global_allocator_cc_info
1562        else:
1563            return toolchain.libstd_and_global_allocator_ccinfo
1564    else:
1565        return toolchain.libstd_and_allocator_ccinfo
1566
1567def _is_dylib(dep):
1568    return not bool(dep.static_library or dep.pic_static_library)
1569
1570def _collect_nonstatic_linker_inputs(cc_info):
1571    shared_linker_inputs = []
1572    for linker_input in cc_info.linking_context.linker_inputs.to_list():
1573        dylibs = [
1574            lib
1575            for lib in linker_input.libraries
1576            if _is_dylib(lib)
1577        ]
1578        if dylibs:
1579            shared_linker_inputs.append(cc_common.create_linker_input(
1580                owner = linker_input.owner,
1581                libraries = depset(dylibs),
1582            ))
1583    return shared_linker_inputs
1584
1585def establish_cc_info(ctx, attr, crate_info, toolchain, cc_toolchain, feature_configuration, interface_library):
1586    """If the produced crate is suitable yield a CcInfo to allow for interop with cc rules
1587
1588    Args:
1589        ctx (ctx): The rule's context object
1590        attr (struct): Attributes to use in gathering CcInfo
1591        crate_info (CrateInfo): The CrateInfo provider of the target crate
1592        toolchain (rust_toolchain): The current `rust_toolchain`
1593        cc_toolchain (CcToolchainInfo): The current `CcToolchainInfo`
1594        feature_configuration (FeatureConfiguration): Feature configuration to be queried.
1595        interface_library (File): Optional interface library for cdylib crates on Windows.
1596
1597    Returns:
1598        list: A list containing the CcInfo provider
1599    """
1600
1601    # A test will not need to produce CcInfo as nothing can depend on test targets
1602    if crate_info.is_test:
1603        return []
1604
1605    # Only generate CcInfo for particular crate types
1606    if crate_info.type not in ("staticlib", "cdylib", "rlib", "lib"):
1607        return []
1608
1609    # TODO: Remove after some resolution to
1610    # https://github.com/bazelbuild/rules_rust/issues/771
1611    if getattr(attr, "out_binary", False):
1612        return []
1613
1614    if crate_info.type == "staticlib":
1615        library_to_link = cc_common.create_library_to_link(
1616            actions = ctx.actions,
1617            feature_configuration = feature_configuration,
1618            cc_toolchain = cc_toolchain,
1619            static_library = crate_info.output,
1620            # TODO(hlopko): handle PIC/NOPIC correctly
1621            pic_static_library = crate_info.output,
1622            alwayslink = getattr(attr, "alwayslink", False),
1623        )
1624    elif crate_info.type in ("rlib", "lib"):
1625        # bazel hard-codes a check for endswith((".a", ".pic.a",
1626        # ".lib")) in create_library_to_link, so we work around that
1627        # by creating a symlink to the .rlib with a .a extension.
1628        dot_a = make_static_lib_symlink(ctx.label.package, ctx.actions, crate_info.output)
1629
1630        # TODO(hlopko): handle PIC/NOPIC correctly
1631        library_to_link = cc_common.create_library_to_link(
1632            actions = ctx.actions,
1633            feature_configuration = feature_configuration,
1634            cc_toolchain = cc_toolchain,
1635            static_library = dot_a,
1636            # TODO(hlopko): handle PIC/NOPIC correctly
1637            pic_static_library = dot_a,
1638            alwayslink = getattr(attr, "alwayslink", False),
1639        )
1640    elif crate_info.type == "cdylib":
1641        library_to_link = cc_common.create_library_to_link(
1642            actions = ctx.actions,
1643            feature_configuration = feature_configuration,
1644            cc_toolchain = cc_toolchain,
1645            dynamic_library = crate_info.output,
1646            interface_library = interface_library,
1647        )
1648    else:
1649        fail("Unexpected case")
1650
1651    link_input = cc_common.create_linker_input(
1652        owner = ctx.label,
1653        libraries = depset([library_to_link]),
1654    )
1655
1656    linking_context = cc_common.create_linking_context(
1657        # TODO - What to do for no_std?
1658        linker_inputs = depset([link_input]),
1659    )
1660
1661    cc_infos = [
1662        CcInfo(linking_context = linking_context),
1663        toolchain.stdlib_linkflags,
1664    ]
1665
1666    # Flattening is okay since crate_info.deps only records direct deps.
1667    for dep in crate_info.deps.to_list():
1668        if dep.cc_info:
1669            # A Rust staticlib or shared library doesn't need to propagate linker inputs
1670            # of its dependencies, except for shared libraries.
1671            if crate_info.type in ["cdylib", "staticlib"]:
1672                shared_linker_inputs = _collect_nonstatic_linker_inputs(dep.cc_info)
1673                if shared_linker_inputs:
1674                    linking_context = cc_common.create_linking_context(
1675                        linker_inputs = depset(shared_linker_inputs),
1676                    )
1677                    cc_infos.append(CcInfo(linking_context = linking_context))
1678            else:
1679                cc_infos.append(dep.cc_info)
1680
1681    if crate_info.type in ("rlib", "lib"):
1682        libstd_and_allocator_cc_info = _get_std_and_alloc_info(ctx, toolchain, crate_info)
1683        if libstd_and_allocator_cc_info:
1684            # TODO: if we already have an rlib in our deps, we could skip this
1685            cc_infos.append(libstd_and_allocator_cc_info)
1686
1687    return [cc_common.merge_cc_infos(cc_infos = cc_infos)]
1688
1689def add_edition_flags(args, crate):
1690    """Adds the Rust edition flag to an arguments object reference
1691
1692    Args:
1693        args (Args): A reference to an Args object
1694        crate (CrateInfo): A CrateInfo provider
1695    """
1696    if crate.edition != "2015":
1697        args.add(crate.edition, format = "--edition=%s")
1698
1699def _create_extra_input_args(build_info, dep_info, include_link_flags = True):
1700    """Gather additional input arguments from transitive dependencies
1701
1702    Args:
1703        build_info (BuildInfo): The BuildInfo provider from the target Crate's set of inputs.
1704        dep_info (DepInfo): The Depinfo provider form the target Crate's set of inputs.
1705        include_link_flags (bool, optional): Whether to include flags like `-l` that instruct the linker to search for a library.
1706
1707    Returns:
1708        tuple: A tuple of the following items:
1709            - (depset[File]): A list of all build info `OUT_DIR` File objects
1710            - (str): The `OUT_DIR` of the current build info
1711            - (File): An optional generated environment file from a `cargo_build_script` target
1712            - (depset[File]): All direct and transitive build flag files from the current build info to be passed to rustc.
1713    """
1714    input_files = []
1715    input_depsets = []
1716
1717    # Arguments to the commandline line wrapper that are going to be used
1718    # to create the final command line
1719    out_dir = None
1720    build_env_file = None
1721    build_flags_files = []
1722
1723    if build_info:
1724        if build_info.out_dir:
1725            out_dir = build_info.out_dir.path
1726            input_files.append(build_info.out_dir)
1727        build_env_file = build_info.rustc_env
1728        if build_info.flags:
1729            build_flags_files.append(build_info.flags)
1730        if build_info.linker_flags and include_link_flags:
1731            build_flags_files.append(build_info.linker_flags)
1732            input_files.append(build_info.linker_flags)
1733
1734        input_depsets.append(build_info.compile_data)
1735
1736    return (
1737        depset(input_files, transitive = [dep_info.link_search_path_files] + input_depsets),
1738        out_dir,
1739        build_env_file,
1740        depset(build_flags_files, transitive = [dep_info.link_search_path_files]),
1741    )
1742
1743def _compute_rpaths(toolchain, output_dir, dep_info, use_pic):
1744    """Determine the artifact's rpaths relative to the bazel root for runtime linking of shared libraries.
1745
1746    Args:
1747        toolchain (rust_toolchain): The current `rust_toolchain`
1748        output_dir (str): The output directory of the current target
1749        dep_info (DepInfo): The current target's dependency info
1750        use_pic: If set, prefers pic_static_library over static_library.
1751
1752    Returns:
1753        depset: A set of relative paths from the output directory to each dependency
1754    """
1755
1756    # Windows has no rpath equivalent, so always return an empty depset.
1757    # Fuchsia assembles shared libraries during packaging.
1758    if toolchain.target_os == "windows" or toolchain.target_os == "fuchsia":
1759        return depset([])
1760
1761    dylibs = [
1762        get_preferred_artifact(lib, use_pic)
1763        for linker_input in dep_info.transitive_noncrates.to_list()
1764        for lib in linker_input.libraries
1765        if _is_dylib(lib)
1766    ]
1767
1768    # Include std dylib if dylib linkage is enabled
1769    if toolchain._experimental_link_std_dylib:
1770        # TODO: Make toolchain.rust_std to only include libstd.so
1771        # When dylib linkage is enabled, toolchain.rust_std should only need to
1772        # include libstd.so. Hence, no filtering needed.
1773        for file in toolchain.rust_std.to_list():
1774            if is_std_dylib(file):
1775                dylibs.append(file)
1776
1777    if not dylibs:
1778        return depset([])
1779
1780    # For darwin, dylibs compiled by Bazel will fail to be resolved at runtime
1781    # without a version of Bazel that includes
1782    # https://github.com/bazelbuild/bazel/pull/13427. This is known to not be
1783    # included in Bazel 4.1 and below.
1784    if toolchain.target_os not in ["linux", "darwin", "android"]:
1785        fail("Runtime linking is not supported on {}, but found {}".format(
1786            toolchain.target_os,
1787            dep_info.transitive_noncrates,
1788        ))
1789
1790    # Multiple dylibs can be present in the same directory, so deduplicate them.
1791    return depset([
1792        relativize(lib_dir, output_dir)
1793        for lib_dir in _get_dir_names(dylibs)
1794    ])
1795
1796def _get_dir_names(files):
1797    """Returns a list of directory names from the given list of File objects
1798
1799    Args:
1800        files (list): A list of File objects
1801
1802    Returns:
1803        list: A list of directory names for all files
1804    """
1805    dirs = {}
1806    for f in files:
1807        dirs[f.dirname] = None
1808    return dirs.keys()
1809
1810def add_crate_link_flags(args, dep_info, force_all_deps_direct = False, use_metadata = False):
1811    """Adds link flags to an Args object reference
1812
1813    Args:
1814        args (Args): An arguments object reference
1815        dep_info (DepInfo): The current target's dependency info
1816        force_all_deps_direct (bool, optional): Whether to pass the transitive rlibs with --extern
1817            to the commandline as opposed to -L.
1818        use_metadata (bool, optional): Build command line arugments using metadata for crates that provide it.
1819    """
1820
1821    direct_crates = depset(
1822        transitive = [
1823            dep_info.direct_crates,
1824            dep_info.transitive_crates,
1825        ],
1826    ) if force_all_deps_direct else dep_info.direct_crates
1827
1828    crate_to_link_flags = _crate_to_link_flag_metadata if use_metadata else _crate_to_link_flag
1829    args.add_all(direct_crates, uniquify = True, map_each = crate_to_link_flags)
1830
1831    args.add_all(
1832        dep_info.transitive_crates,
1833        map_each = _get_crate_dirname,
1834        uniquify = True,
1835        format_each = "-Ldependency=%s",
1836    )
1837
1838def _crate_to_link_flag_metadata(crate):
1839    """A helper macro used by `add_crate_link_flags` for adding crate link flags to a Arg object
1840
1841    Args:
1842        crate (CrateInfo|AliasableDepInfo): A CrateInfo or an AliasableDepInfo provider
1843
1844    Returns:
1845        list: Link flags for the given provider
1846    """
1847
1848    # This is AliasableDepInfo, we should use the alias as a crate name
1849    if hasattr(crate, "dep"):
1850        name = crate.name
1851        crate_info = crate.dep
1852    else:
1853        name = crate.name
1854        crate_info = crate
1855
1856    lib_or_meta = crate_info.metadata
1857    if not crate_info.metadata:
1858        lib_or_meta = crate_info.output
1859    return ["--extern={}={}".format(name, lib_or_meta.path)]
1860
1861def _crate_to_link_flag(crate):
1862    """A helper macro used by `add_crate_link_flags` for adding crate link flags to a Arg object
1863
1864    Args:
1865        crate (CrateInfo|AliasableDepInfo): A CrateInfo or an AliasableDepInfo provider
1866
1867    Returns:
1868        list: Link flags for the given provider
1869    """
1870
1871    # This is AliasableDepInfo, we should use the alias as a crate name
1872    if hasattr(crate, "dep"):
1873        name = crate.name
1874        crate_info = crate.dep
1875    else:
1876        name = crate.name
1877        crate_info = crate
1878    return ["--extern={}={}".format(name, crate_info.output.path)]
1879
1880def _get_crate_dirname(crate):
1881    """A helper macro used by `add_crate_link_flags` for getting the directory name of the current crate's output path
1882
1883    Args:
1884        crate (CrateInfo): A CrateInfo provider from the current rule
1885
1886    Returns:
1887        str: The directory name of the the output File that will be produced.
1888    """
1889    return crate.output.dirname
1890
1891def _portable_link_flags(lib, use_pic, ambiguous_libs, get_lib_name, for_windows = False, for_darwin = False, flavor_msvc = False):
1892    artifact = get_preferred_artifact(lib, use_pic)
1893    if ambiguous_libs and artifact.path in ambiguous_libs:
1894        artifact = ambiguous_libs[artifact.path]
1895    if lib.static_library or lib.pic_static_library:
1896        # To ensure appropriate linker library argument order, in the presence
1897        # of both native libraries that depend on rlibs and rlibs that depend
1898        # on native libraries, we use an approach where we "sandwich" the
1899        # rust libraries between two similar sections of all of native
1900        # libraries:
1901        # n1 n2 ... r1 r2 ... n1 n2 ...
1902        # A         B         C
1903        # This way any dependency from a native library to a rust library
1904        # is resolved from A to B, and any dependency from a rust library to
1905        # a native one is resolved from B to C.
1906        # The question of resolving dependencies from a native library from A
1907        # to any rust library is addressed in a different place, where we
1908        # create symlinks to the rlibs, pretending they are native libraries,
1909        # and adding references to these symlinks in the native section A.
1910        # We rely in the behavior of -Clink-arg to put the linker args
1911        # at the end of the linker invocation constructed by rustc.
1912
1913        # We skip adding `-Clink-arg=-l` for libstd and libtest from the standard library, as
1914        # these two libraries are present both as an `.rlib` and a `.so` format.
1915        # On linux, Rustc adds a -Bdynamic to the linker command line before the libraries specified
1916        # with `-Clink-arg`, which leads to us linking against the `.so`s but not putting the
1917        # corresponding value to the runtime library search paths, which results in a
1918        # "cannot open shared object file: No such file or directory" error at exectuion time.
1919        # We can fix this by adding a `-Clink-arg=-Bstatic` on linux, but we don't have that option for
1920        # macos. The proper solution for this issue would be to remove `libtest-{hash}.so` and `libstd-{hash}.so`
1921        # from the toolchain. However, it is not enough to change the toolchain's `rust_std_{...}` filegroups
1922        # here: https://github.com/bazelbuild/rules_rust/blob/a9d5d894ad801002d007b858efd154e503796b9f/rust/private/repository_utils.bzl#L144
1923        # because rustc manages to escape the sandbox and still finds them at linking time.
1924        # We need to modify the repository rules to erase those files completely.
1925        if "lib/rustlib" in artifact.path and (
1926            artifact.basename.startswith("libtest-") or artifact.basename.startswith("libstd-") or
1927            artifact.basename.startswith("test-") or artifact.basename.startswith("std-")
1928        ):
1929            return [] if for_darwin else ["-lstatic=%s" % get_lib_name(artifact)]
1930
1931        if for_windows:
1932            if flavor_msvc:
1933                return [
1934                    "-lstatic=%s" % get_lib_name(artifact),
1935                    "-Clink-arg={}".format(artifact.basename),
1936                ]
1937            else:
1938                return [
1939                    "-lstatic=%s" % get_lib_name(artifact),
1940                    "-Clink-arg=-l{}".format(artifact.basename),
1941                ]
1942        else:
1943            return [
1944                "-lstatic=%s" % get_lib_name(artifact),
1945                "-Clink-arg=-l{}".format(get_lib_name(artifact)),
1946            ]
1947    elif _is_dylib(lib):
1948        return [
1949            "-ldylib=%s" % get_lib_name(artifact),
1950        ]
1951
1952    return []
1953
1954def _make_link_flags_windows(make_link_flags_args, flavor_msvc):
1955    linker_input, use_pic, ambiguous_libs, include_link_flags = make_link_flags_args
1956    ret = []
1957    for lib in linker_input.libraries:
1958        if lib.alwayslink:
1959            if flavor_msvc:
1960                ret.extend(["-C", "link-arg=/WHOLEARCHIVE:%s" % get_preferred_artifact(lib, use_pic).path])
1961            else:
1962                ret.extend([
1963                    "-C",
1964                    "link-arg=-Wl,--whole-archive",
1965                    "-C",
1966                    ("link-arg=%s" % get_preferred_artifact(lib, use_pic).path),
1967                    "-C",
1968                    "link-arg=-Wl,--no-whole-archive",
1969                ])
1970        elif include_link_flags:
1971            ret.extend(_portable_link_flags(lib, use_pic, ambiguous_libs, get_lib_name_for_windows, for_windows = True, flavor_msvc = flavor_msvc))
1972    return ret
1973
1974def _make_link_flags_windows_msvc(make_link_flags_args):
1975    return _make_link_flags_windows(make_link_flags_args, flavor_msvc = True)
1976
1977def _make_link_flags_windows_gnu(make_link_flags_args):
1978    return _make_link_flags_windows(make_link_flags_args, flavor_msvc = False)
1979
1980def _make_link_flags_darwin(make_link_flags_args):
1981    linker_input, use_pic, ambiguous_libs, include_link_flags = make_link_flags_args
1982    ret = []
1983    for lib in linker_input.libraries:
1984        if lib.alwayslink:
1985            ret.extend([
1986                "-C",
1987                ("link-arg=-Wl,-force_load,%s" % get_preferred_artifact(lib, use_pic).path),
1988            ])
1989        elif include_link_flags:
1990            ret.extend(_portable_link_flags(lib, use_pic, ambiguous_libs, get_lib_name_default, for_darwin = True))
1991    return ret
1992
1993def _make_link_flags_default(make_link_flags_args):
1994    linker_input, use_pic, ambiguous_libs, include_link_flags = make_link_flags_args
1995    ret = []
1996    for lib in linker_input.libraries:
1997        if lib.alwayslink:
1998            ret.extend([
1999                "-C",
2000                "link-arg=-Wl,--whole-archive",
2001                "-C",
2002                ("link-arg=%s" % get_preferred_artifact(lib, use_pic).path),
2003                "-C",
2004                "link-arg=-Wl,--no-whole-archive",
2005            ])
2006        elif include_link_flags:
2007            ret.extend(_portable_link_flags(lib, use_pic, ambiguous_libs, get_lib_name_default))
2008    return ret
2009
2010def _libraries_dirnames(make_link_flags_args):
2011    link_input, use_pic, _, _ = make_link_flags_args
2012
2013    # De-duplicate names.
2014    return depset([get_preferred_artifact(lib, use_pic).dirname for lib in link_input.libraries]).to_list()
2015
2016def _add_native_link_flags(args, dep_info, linkstamp_outs, ambiguous_libs, crate_type, toolchain, cc_toolchain, feature_configuration, compilation_mode, include_link_flags = True):
2017    """Adds linker flags for all dependencies of the current target.
2018
2019    Args:
2020        args (Args): The Args struct for a ctx.action
2021        dep_info (DepInfo): Dependency Info provider
2022        linkstamp_outs (list): Linkstamp outputs of native dependencies
2023        ambiguous_libs (dict): Ambiguous libs, see `_disambiguate_libs`
2024        crate_type: Crate type of the current target
2025        toolchain (rust_toolchain): The current `rust_toolchain`
2026        cc_toolchain (CcToolchainInfo): The current `cc_toolchain`
2027        feature_configuration (FeatureConfiguration): feature configuration to use with cc_toolchain
2028        compilation_mode (bool): The compilation mode for this build.
2029        include_link_flags (bool, optional): Whether to include flags like `-l` that instruct the linker to search for a library.
2030    """
2031    if crate_type in ["lib", "rlib"]:
2032        return
2033
2034    use_pic = _should_use_pic(cc_toolchain, feature_configuration, crate_type, compilation_mode)
2035
2036    if toolchain.target_os == "windows":
2037        make_link_flags = _make_link_flags_windows_msvc if toolchain.target_triple.abi == "msvc" else _make_link_flags_windows_gnu
2038        get_lib_name = get_lib_name_for_windows
2039    elif toolchain.target_os.startswith(("mac", "darwin", "ios")):
2040        make_link_flags = _make_link_flags_darwin
2041        get_lib_name = get_lib_name_default
2042    else:
2043        make_link_flags = _make_link_flags_default
2044        get_lib_name = get_lib_name_default
2045
2046    # TODO(hlopko): Remove depset flattening by using lambdas once we are on >=Bazel 5.0
2047    make_link_flags_args = [(arg, use_pic, ambiguous_libs, include_link_flags) for arg in dep_info.transitive_noncrates.to_list()]
2048    args.add_all(make_link_flags_args, map_each = _libraries_dirnames, uniquify = True, format_each = "-Lnative=%s")
2049    if ambiguous_libs:
2050        # If there are ambiguous libs, the disambiguation symlinks to them are
2051        # all created in the same directory. Add it to the library search path.
2052        ambiguous_libs_dirname = ambiguous_libs.values()[0].dirname
2053        args.add(ambiguous_libs_dirname, format = "-Lnative=%s")
2054
2055    args.add_all(make_link_flags_args, map_each = make_link_flags)
2056
2057    args.add_all(linkstamp_outs, before_each = "-C", format_each = "link-args=%s")
2058
2059    if crate_type in ["dylib", "cdylib"]:
2060        # For shared libraries we want to link C++ runtime library dynamically
2061        # (for example libstdc++.so or libc++.so).
2062        args.add_all(
2063            cc_toolchain.dynamic_runtime_lib(feature_configuration = feature_configuration),
2064            map_each = _get_dirname,
2065            format_each = "-Lnative=%s",
2066        )
2067        if include_link_flags:
2068            args.add_all(
2069                cc_toolchain.dynamic_runtime_lib(feature_configuration = feature_configuration),
2070                map_each = get_lib_name,
2071                format_each = "-ldylib=%s",
2072            )
2073    else:
2074        # For all other crate types we want to link C++ runtime library statically
2075        # (for example libstdc++.a or libc++.a).
2076        args.add_all(
2077            cc_toolchain.static_runtime_lib(feature_configuration = feature_configuration),
2078            map_each = _get_dirname,
2079            format_each = "-Lnative=%s",
2080        )
2081        if include_link_flags:
2082            args.add_all(
2083                cc_toolchain.static_runtime_lib(feature_configuration = feature_configuration),
2084                map_each = get_lib_name,
2085                format_each = "-lstatic=%s",
2086            )
2087
2088def _get_dirname(file):
2089    """A helper function for `_add_native_link_flags`.
2090
2091    Args:
2092        file (File): The target file
2093
2094    Returns:
2095        str: Directory name of `file`
2096    """
2097    return file.dirname
2098
2099def _add_per_crate_rustc_flags(ctx, args, crate_info, per_crate_rustc_flags):
2100    """Adds matching per-crate rustc flags to an arguments object reference
2101
2102    Args:
2103        ctx (ctx): The source rule's context object
2104        args (Args): A reference to an Args object
2105        crate_info (CrateInfo): A CrateInfo provider
2106        per_crate_rustc_flags (list): A list of per_crate_rustc_flag values
2107    """
2108    for per_crate_rustc_flag in per_crate_rustc_flags:
2109        at_index = per_crate_rustc_flag.find("@")
2110        if at_index == -1:
2111            fail("per_crate_rustc_flag '{}' does not follow the expected format: prefix_filter@flag".format(per_crate_rustc_flag))
2112
2113        prefix_filter = per_crate_rustc_flag[:at_index]
2114        flag = per_crate_rustc_flag[at_index + 1:]
2115        if not flag:
2116            fail("per_crate_rustc_flag '{}' does not follow the expected format: prefix_filter@flag".format(per_crate_rustc_flag))
2117
2118        label_string = str(ctx.label)
2119        label = label_string[1:] if label_string.startswith("@//") else label_string
2120        execution_path = crate_info.root.path
2121
2122        if label.startswith(prefix_filter) or execution_path.startswith(prefix_filter):
2123            args.add(flag)
2124
2125def _error_format_impl(ctx):
2126    """Implementation of the `error_format` rule
2127
2128    Args:
2129        ctx (ctx): The rule's context object
2130
2131    Returns:
2132        list: A list containing the ErrorFormatInfo provider
2133    """
2134    raw = ctx.build_setting_value
2135    if raw not in _error_format_values:
2136        fail("{} expected a value in `{}` but got `{}`".format(
2137            ctx.label,
2138            _error_format_values,
2139            raw,
2140        ))
2141    return [ErrorFormatInfo(error_format = raw)]
2142
2143error_format = rule(
2144    doc = (
2145        "Change the [--error-format](https://doc.rust-lang.org/rustc/command-line-arguments.html#option-error-format) " +
2146        "flag from the command line with `--@rules_rust//:error_format`. See rustc documentation for valid values."
2147    ),
2148    implementation = _error_format_impl,
2149    build_setting = config.string(flag = True),
2150)
2151
2152def _rustc_output_diagnostics_impl(ctx):
2153    """Implementation of the `rustc_output_diagnostics` rule
2154
2155    Args:
2156        ctx (ctx): The rule's context object
2157
2158    Returns:
2159        list: A list containing the RustcOutputDiagnosticsInfo provider
2160    """
2161    return [RustcOutputDiagnosticsInfo(
2162        rustc_output_diagnostics = ctx.build_setting_value,
2163    )]
2164
2165rustc_output_diagnostics = rule(
2166    doc = (
2167        "Setting this flag from the command line with `--@rules_rust//:rustc_output_diagnostics` " +
2168        "makes rules_rust save rustc json output(suitable for consumption by rust-analyzer) in a file. " +
2169        "These are accessible via the " +
2170        "`rustc_rmeta_output`(for pipelined compilation) and `rustc_output` output groups. " +
2171        "You can find these using `bazel cquery`"
2172    ),
2173    implementation = _rustc_output_diagnostics_impl,
2174    build_setting = config.bool(flag = True),
2175)
2176
2177def _extra_rustc_flags_impl(ctx):
2178    return ExtraRustcFlagsInfo(extra_rustc_flags = ctx.build_setting_value)
2179
2180extra_rustc_flags = rule(
2181    doc = (
2182        "Add additional rustc_flags from the command line with `--@rules_rust//:extra_rustc_flags`. " +
2183        "This flag should only be used for flags that need to be applied across the entire build. For options that " +
2184        "apply to individual crates, use the rustc_flags attribute on the individual crate's rule instead. NOTE: " +
2185        "These flags not applied to the exec configuration (proc-macros, cargo_build_script, etc); " +
2186        "use `--@rules_rust//:extra_exec_rustc_flags` to apply flags to the exec configuration."
2187    ),
2188    implementation = _extra_rustc_flags_impl,
2189    build_setting = config.string_list(flag = True),
2190)
2191
2192def _extra_rustc_flag_impl(ctx):
2193    return ExtraRustcFlagsInfo(extra_rustc_flags = [f for f in ctx.build_setting_value if f != ""])
2194
2195extra_rustc_flag = rule(
2196    doc = (
2197        "Add additional rustc_flag from the command line with `--@rules_rust//:extra_rustc_flag`. " +
2198        "Multiple uses are accumulated and appended after the extra_rustc_flags."
2199    ),
2200    implementation = _extra_rustc_flag_impl,
2201    build_setting = config.string(flag = True, allow_multiple = True),
2202)
2203
2204def _extra_exec_rustc_flags_impl(ctx):
2205    return ExtraExecRustcFlagsInfo(extra_exec_rustc_flags = ctx.build_setting_value)
2206
2207extra_exec_rustc_flags = rule(
2208    doc = (
2209        "Add additional rustc_flags in the exec configuration from the command line with `--@rules_rust//:extra_exec_rustc_flags`. " +
2210        "This flag should only be used for flags that need to be applied across the entire build. " +
2211        "These flags only apply to the exec configuration (proc-macros, cargo_build_script, etc)."
2212    ),
2213    implementation = _extra_exec_rustc_flags_impl,
2214    build_setting = config.string_list(flag = True),
2215)
2216
2217def _extra_exec_rustc_flag_impl(ctx):
2218    return ExtraExecRustcFlagsInfo(extra_exec_rustc_flags = [f for f in ctx.build_setting_value if f != ""])
2219
2220extra_exec_rustc_flag = rule(
2221    doc = (
2222        "Add additional rustc_flags in the exec configuration from the command line with `--@rules_rust//:extra_exec_rustc_flag`. " +
2223        "Multiple uses are accumulated and appended after the extra_exec_rustc_flags."
2224    ),
2225    implementation = _extra_exec_rustc_flag_impl,
2226    build_setting = config.string(flag = True, allow_multiple = True),
2227)
2228
2229def _per_crate_rustc_flag_impl(ctx):
2230    return PerCrateRustcFlagsInfo(per_crate_rustc_flags = [f for f in ctx.build_setting_value if f != ""])
2231
2232per_crate_rustc_flag = rule(
2233    doc = (
2234        "Add additional rustc_flag to matching crates from the command line with `--@rules_rust//:experimental_per_crate_rustc_flag`. " +
2235        "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." +
2236        "The label matching uses the canonical form of the label (i.e //package:label_name)." +
2237        "The execution path is the relative path to your workspace directory including the base name (including extension) of the crate root." +
2238        "This flag is only applied to the exec configuration (proc-macros, cargo_build_script, etc)." +
2239        "Multiple uses are accumulated."
2240    ),
2241    implementation = _per_crate_rustc_flag_impl,
2242    build_setting = config.string(flag = True, allow_multiple = True),
2243)
2244
2245def _no_std_impl(ctx):
2246    value = str(ctx.attr._no_std[BuildSettingInfo].value)
2247    if is_exec_configuration(ctx):
2248        return [config_common.FeatureFlagInfo(value = "off")]
2249    return [config_common.FeatureFlagInfo(value = value)]
2250
2251no_std = rule(
2252    doc = (
2253        "No std; we need this so that we can distinguish between host and exec"
2254    ),
2255    attrs = {
2256        "_no_std": attr.label(default = "//:no_std"),
2257    },
2258    implementation = _no_std_impl,
2259)
2260