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