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