xref: /aosp_15_r20/external/bazelbuild-rules_rust/rust/private/rust.bzl (revision d4726bddaa87cc4778e7472feed243fa4b6c267f)
1*d4726bddSHONG Yifan# Copyright 2015 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"""Rust rule implementations"""
16*d4726bddSHONG Yifan
17*d4726bddSHONG Yifanload("@bazel_skylib//lib:paths.bzl", "paths")
18*d4726bddSHONG Yifanload("//rust/private:common.bzl", "COMMON_PROVIDERS", "rust_common")
19*d4726bddSHONG Yifanload("//rust/private:providers.bzl", "BuildInfo")
20*d4726bddSHONG Yifanload("//rust/private:rustc.bzl", "rustc_compile_action")
21*d4726bddSHONG Yifanload(
22*d4726bddSHONG Yifan    "//rust/private:utils.bzl",
23*d4726bddSHONG Yifan    "can_build_metadata",
24*d4726bddSHONG Yifan    "compute_crate_name",
25*d4726bddSHONG Yifan    "crate_root_src",
26*d4726bddSHONG Yifan    "dedent",
27*d4726bddSHONG Yifan    "determine_lib_name",
28*d4726bddSHONG Yifan    "determine_output_hash",
29*d4726bddSHONG Yifan    "expand_dict_value_locations",
30*d4726bddSHONG Yifan    "find_toolchain",
31*d4726bddSHONG Yifan    "generate_output_diagnostics",
32*d4726bddSHONG Yifan    "get_edition",
33*d4726bddSHONG Yifan    "get_import_macro_deps",
34*d4726bddSHONG Yifan    "transform_deps",
35*d4726bddSHONG Yifan    "transform_sources",
36*d4726bddSHONG Yifan)
37*d4726bddSHONG Yifan
38*d4726bddSHONG Yifan# TODO(marco): Separate each rule into its own file.
39*d4726bddSHONG Yifan
40*d4726bddSHONG Yifandef _assert_no_deprecated_attributes(_ctx):
41*d4726bddSHONG Yifan    """Forces a failure if any deprecated attributes were specified
42*d4726bddSHONG Yifan
43*d4726bddSHONG Yifan    Args:
44*d4726bddSHONG Yifan        _ctx (ctx): The current rule's context object
45*d4726bddSHONG Yifan    """
46*d4726bddSHONG Yifan    pass
47*d4726bddSHONG Yifan
48*d4726bddSHONG Yifandef _assert_correct_dep_mapping(ctx):
49*d4726bddSHONG Yifan    """Forces a failure if proc_macro_deps and deps are mixed inappropriately
50*d4726bddSHONG Yifan
51*d4726bddSHONG Yifan    Args:
52*d4726bddSHONG Yifan        ctx (ctx): The current rule's context object
53*d4726bddSHONG Yifan    """
54*d4726bddSHONG Yifan    for dep in ctx.attr.deps:
55*d4726bddSHONG Yifan        if rust_common.crate_info in dep:
56*d4726bddSHONG Yifan            if dep[rust_common.crate_info].type == "proc-macro":
57*d4726bddSHONG Yifan                fail(
58*d4726bddSHONG Yifan                    "{} listed {} in its deps, but it is a proc-macro. It should instead be in the bazel property proc_macro_deps.".format(
59*d4726bddSHONG Yifan                        ctx.label,
60*d4726bddSHONG Yifan                        dep.label,
61*d4726bddSHONG Yifan                    ),
62*d4726bddSHONG Yifan                )
63*d4726bddSHONG Yifan    for dep in ctx.attr.proc_macro_deps:
64*d4726bddSHONG Yifan        type = dep[rust_common.crate_info].type
65*d4726bddSHONG Yifan        if type != "proc-macro":
66*d4726bddSHONG Yifan            fail(
67*d4726bddSHONG Yifan                "{} listed {} in its proc_macro_deps, but it is not proc-macro, it is a {}. It should probably instead be listed in deps.".format(
68*d4726bddSHONG Yifan                    ctx.label,
69*d4726bddSHONG Yifan                    dep.label,
70*d4726bddSHONG Yifan                    type,
71*d4726bddSHONG Yifan                ),
72*d4726bddSHONG Yifan            )
73*d4726bddSHONG Yifan
74*d4726bddSHONG Yifandef _rust_library_impl(ctx):
75*d4726bddSHONG Yifan    """The implementation of the `rust_library` rule.
76*d4726bddSHONG Yifan
77*d4726bddSHONG Yifan    This rule provides CcInfo, so it can be used everywhere Bazel
78*d4726bddSHONG Yifan    expects rules_cc, but care must be taken to have the correct
79*d4726bddSHONG Yifan    dependencies on an allocator and std implemetation as needed.
80*d4726bddSHONG Yifan
81*d4726bddSHONG Yifan    Args:
82*d4726bddSHONG Yifan        ctx (ctx): The rule's context object
83*d4726bddSHONG Yifan
84*d4726bddSHONG Yifan    Returns:
85*d4726bddSHONG Yifan        list: A list of providers.
86*d4726bddSHONG Yifan    """
87*d4726bddSHONG Yifan    return _rust_library_common(ctx, "rlib")
88*d4726bddSHONG Yifan
89*d4726bddSHONG Yifandef _rust_static_library_impl(ctx):
90*d4726bddSHONG Yifan    """The implementation of the `rust_static_library` rule.
91*d4726bddSHONG Yifan
92*d4726bddSHONG Yifan    This rule provides CcInfo, so it can be used everywhere Bazel
93*d4726bddSHONG Yifan    expects rules_cc.
94*d4726bddSHONG Yifan
95*d4726bddSHONG Yifan    Args:
96*d4726bddSHONG Yifan        ctx (ctx): The rule's context object
97*d4726bddSHONG Yifan
98*d4726bddSHONG Yifan    Returns:
99*d4726bddSHONG Yifan        list: A list of providers.
100*d4726bddSHONG Yifan    """
101*d4726bddSHONG Yifan    return _rust_library_common(ctx, "staticlib")
102*d4726bddSHONG Yifan
103*d4726bddSHONG Yifandef _rust_shared_library_impl(ctx):
104*d4726bddSHONG Yifan    """The implementation of the `rust_shared_library` rule.
105*d4726bddSHONG Yifan
106*d4726bddSHONG Yifan    This rule provides CcInfo, so it can be used everywhere Bazel
107*d4726bddSHONG Yifan    expects rules_cc.
108*d4726bddSHONG Yifan
109*d4726bddSHONG Yifan    On Windows, a PDB file containing debugging information is available under
110*d4726bddSHONG Yifan    the key `pdb_file` in `OutputGroupInfo`. Similarly on macOS, a dSYM folder
111*d4726bddSHONG Yifan    is available under the key `dsym_folder` in `OutputGroupInfo`.
112*d4726bddSHONG Yifan
113*d4726bddSHONG Yifan    Args:
114*d4726bddSHONG Yifan        ctx (ctx): The rule's context object
115*d4726bddSHONG Yifan
116*d4726bddSHONG Yifan    Returns:
117*d4726bddSHONG Yifan        list: A list of providers.
118*d4726bddSHONG Yifan    """
119*d4726bddSHONG Yifan    return _rust_library_common(ctx, "cdylib")
120*d4726bddSHONG Yifan
121*d4726bddSHONG Yifandef _rust_proc_macro_impl(ctx):
122*d4726bddSHONG Yifan    """The implementation of the `rust_proc_macro` rule.
123*d4726bddSHONG Yifan
124*d4726bddSHONG Yifan    Args:
125*d4726bddSHONG Yifan        ctx (ctx): The rule's context object
126*d4726bddSHONG Yifan
127*d4726bddSHONG Yifan    Returns:
128*d4726bddSHONG Yifan        list: A list of providers.
129*d4726bddSHONG Yifan    """
130*d4726bddSHONG Yifan    return _rust_library_common(ctx, "proc-macro")
131*d4726bddSHONG Yifan
132*d4726bddSHONG Yifandef _rust_library_common(ctx, crate_type):
133*d4726bddSHONG Yifan    """The common implementation of the library-like rules.
134*d4726bddSHONG Yifan
135*d4726bddSHONG Yifan    Args:
136*d4726bddSHONG Yifan        ctx (ctx): The rule's context object
137*d4726bddSHONG Yifan        crate_type (String): one of lib|rlib|dylib|staticlib|cdylib|proc-macro
138*d4726bddSHONG Yifan
139*d4726bddSHONG Yifan    Returns:
140*d4726bddSHONG Yifan        list: A list of providers. See `rustc_compile_action`
141*d4726bddSHONG Yifan    """
142*d4726bddSHONG Yifan    _assert_no_deprecated_attributes(ctx)
143*d4726bddSHONG Yifan    _assert_correct_dep_mapping(ctx)
144*d4726bddSHONG Yifan
145*d4726bddSHONG Yifan    toolchain = find_toolchain(ctx)
146*d4726bddSHONG Yifan
147*d4726bddSHONG Yifan    crate_name = compute_crate_name(ctx.workspace_name, ctx.label, toolchain, ctx.attr.crate_name)
148*d4726bddSHONG Yifan
149*d4726bddSHONG Yifan    crate_root = getattr(ctx.file, "crate_root", None)
150*d4726bddSHONG Yifan    if not crate_root:
151*d4726bddSHONG Yifan        crate_root = crate_root_src(ctx.attr.name, ctx.files.srcs, crate_type)
152*d4726bddSHONG Yifan    srcs, crate_root = transform_sources(ctx, ctx.files.srcs, crate_root)
153*d4726bddSHONG Yifan
154*d4726bddSHONG Yifan    # Determine unique hash for this rlib.
155*d4726bddSHONG Yifan    # Note that we don't include a hash for `cdylib` and `staticlib` since they are meant to be consumed externally
156*d4726bddSHONG Yifan    # and having a deterministic name is important since it ends up embedded in the executable. This is problematic
157*d4726bddSHONG Yifan    # when one needs to include the library with a specific filename into a larger application.
158*d4726bddSHONG Yifan    # (see https://github.com/bazelbuild/rules_rust/issues/405#issuecomment-993089889 for more details)
159*d4726bddSHONG Yifan    if crate_type in ["cdylib", "staticlib"]:
160*d4726bddSHONG Yifan        output_hash = None
161*d4726bddSHONG Yifan    else:
162*d4726bddSHONG Yifan        output_hash = determine_output_hash(crate_root, ctx.label)
163*d4726bddSHONG Yifan
164*d4726bddSHONG Yifan    rust_lib_name = determine_lib_name(
165*d4726bddSHONG Yifan        crate_name,
166*d4726bddSHONG Yifan        crate_type,
167*d4726bddSHONG Yifan        toolchain,
168*d4726bddSHONG Yifan        output_hash,
169*d4726bddSHONG Yifan    )
170*d4726bddSHONG Yifan    rust_lib = ctx.actions.declare_file(rust_lib_name)
171*d4726bddSHONG Yifan    rust_metadata = None
172*d4726bddSHONG Yifan    rustc_rmeta_output = None
173*d4726bddSHONG Yifan    if can_build_metadata(toolchain, ctx, crate_type) and not ctx.attr.disable_pipelining:
174*d4726bddSHONG Yifan        rust_metadata = ctx.actions.declare_file(
175*d4726bddSHONG Yifan            paths.replace_extension(rust_lib_name, ".rmeta"),
176*d4726bddSHONG Yifan            sibling = rust_lib,
177*d4726bddSHONG Yifan        )
178*d4726bddSHONG Yifan        rustc_rmeta_output = generate_output_diagnostics(ctx, rust_metadata)
179*d4726bddSHONG Yifan
180*d4726bddSHONG Yifan    deps = transform_deps(ctx.attr.deps)
181*d4726bddSHONG Yifan    proc_macro_deps = transform_deps(ctx.attr.proc_macro_deps + get_import_macro_deps(ctx))
182*d4726bddSHONG Yifan
183*d4726bddSHONG Yifan    return rustc_compile_action(
184*d4726bddSHONG Yifan        ctx = ctx,
185*d4726bddSHONG Yifan        attr = ctx.attr,
186*d4726bddSHONG Yifan        toolchain = toolchain,
187*d4726bddSHONG Yifan        output_hash = output_hash,
188*d4726bddSHONG Yifan        crate_info_dict = dict(
189*d4726bddSHONG Yifan            name = crate_name,
190*d4726bddSHONG Yifan            type = crate_type,
191*d4726bddSHONG Yifan            root = crate_root,
192*d4726bddSHONG Yifan            srcs = depset(srcs),
193*d4726bddSHONG Yifan            deps = depset(deps),
194*d4726bddSHONG Yifan            proc_macro_deps = depset(proc_macro_deps),
195*d4726bddSHONG Yifan            aliases = ctx.attr.aliases,
196*d4726bddSHONG Yifan            output = rust_lib,
197*d4726bddSHONG Yifan            rustc_output = generate_output_diagnostics(ctx, rust_lib),
198*d4726bddSHONG Yifan            metadata = rust_metadata,
199*d4726bddSHONG Yifan            rustc_rmeta_output = rustc_rmeta_output,
200*d4726bddSHONG Yifan            edition = get_edition(ctx.attr, toolchain, ctx.label),
201*d4726bddSHONG Yifan            rustc_env = ctx.attr.rustc_env,
202*d4726bddSHONG Yifan            rustc_env_files = ctx.files.rustc_env_files,
203*d4726bddSHONG Yifan            is_test = False,
204*d4726bddSHONG Yifan            data = depset(ctx.files.data),
205*d4726bddSHONG Yifan            compile_data = depset(ctx.files.compile_data),
206*d4726bddSHONG Yifan            compile_data_targets = depset(ctx.attr.compile_data),
207*d4726bddSHONG Yifan            owner = ctx.label,
208*d4726bddSHONG Yifan        ),
209*d4726bddSHONG Yifan    )
210*d4726bddSHONG Yifan
211*d4726bddSHONG Yifandef _rust_binary_impl(ctx):
212*d4726bddSHONG Yifan    """The implementation of the `rust_binary` rule
213*d4726bddSHONG Yifan
214*d4726bddSHONG Yifan    Args:
215*d4726bddSHONG Yifan        ctx (ctx): The rule's context object
216*d4726bddSHONG Yifan
217*d4726bddSHONG Yifan    Returns:
218*d4726bddSHONG Yifan        list: A list of providers. See `rustc_compile_action`
219*d4726bddSHONG Yifan    """
220*d4726bddSHONG Yifan    toolchain = find_toolchain(ctx)
221*d4726bddSHONG Yifan    crate_name = compute_crate_name(ctx.workspace_name, ctx.label, toolchain, ctx.attr.crate_name)
222*d4726bddSHONG Yifan    _assert_correct_dep_mapping(ctx)
223*d4726bddSHONG Yifan
224*d4726bddSHONG Yifan    output = ctx.actions.declare_file(ctx.label.name + toolchain.binary_ext)
225*d4726bddSHONG Yifan
226*d4726bddSHONG Yifan    deps = transform_deps(ctx.attr.deps)
227*d4726bddSHONG Yifan    proc_macro_deps = transform_deps(ctx.attr.proc_macro_deps + get_import_macro_deps(ctx))
228*d4726bddSHONG Yifan
229*d4726bddSHONG Yifan    crate_root = getattr(ctx.file, "crate_root", None)
230*d4726bddSHONG Yifan    if not crate_root:
231*d4726bddSHONG Yifan        crate_root = crate_root_src(ctx.attr.name, ctx.files.srcs, ctx.attr.crate_type)
232*d4726bddSHONG Yifan    srcs, crate_root = transform_sources(ctx, ctx.files.srcs, crate_root)
233*d4726bddSHONG Yifan
234*d4726bddSHONG Yifan    providers = rustc_compile_action(
235*d4726bddSHONG Yifan        ctx = ctx,
236*d4726bddSHONG Yifan        attr = ctx.attr,
237*d4726bddSHONG Yifan        toolchain = toolchain,
238*d4726bddSHONG Yifan        crate_info_dict = dict(
239*d4726bddSHONG Yifan            name = crate_name,
240*d4726bddSHONG Yifan            type = ctx.attr.crate_type,
241*d4726bddSHONG Yifan            root = crate_root,
242*d4726bddSHONG Yifan            srcs = depset(srcs),
243*d4726bddSHONG Yifan            deps = depset(deps),
244*d4726bddSHONG Yifan            proc_macro_deps = depset(proc_macro_deps),
245*d4726bddSHONG Yifan            aliases = ctx.attr.aliases,
246*d4726bddSHONG Yifan            output = output,
247*d4726bddSHONG Yifan            rustc_output = generate_output_diagnostics(ctx, output),
248*d4726bddSHONG Yifan            edition = get_edition(ctx.attr, toolchain, ctx.label),
249*d4726bddSHONG Yifan            rustc_env = ctx.attr.rustc_env,
250*d4726bddSHONG Yifan            rustc_env_files = ctx.files.rustc_env_files,
251*d4726bddSHONG Yifan            is_test = False,
252*d4726bddSHONG Yifan            data = depset(ctx.files.data),
253*d4726bddSHONG Yifan            compile_data = depset(ctx.files.compile_data),
254*d4726bddSHONG Yifan            compile_data_targets = depset(ctx.attr.compile_data),
255*d4726bddSHONG Yifan            owner = ctx.label,
256*d4726bddSHONG Yifan        ),
257*d4726bddSHONG Yifan    )
258*d4726bddSHONG Yifan
259*d4726bddSHONG Yifan    providers.append(RunEnvironmentInfo(
260*d4726bddSHONG Yifan        environment = expand_dict_value_locations(
261*d4726bddSHONG Yifan            ctx,
262*d4726bddSHONG Yifan            ctx.attr.env,
263*d4726bddSHONG Yifan            ctx.attr.data,
264*d4726bddSHONG Yifan        ),
265*d4726bddSHONG Yifan    ))
266*d4726bddSHONG Yifan
267*d4726bddSHONG Yifan    return providers
268*d4726bddSHONG Yifan
269*d4726bddSHONG Yifandef get_rust_test_flags(attr):
270*d4726bddSHONG Yifan    """Determine the desired rustc flags for test targets.
271*d4726bddSHONG Yifan
272*d4726bddSHONG Yifan    Args:
273*d4726bddSHONG Yifan        attr (dict): Attributes of a rule
274*d4726bddSHONG Yifan
275*d4726bddSHONG Yifan    Returns:
276*d4726bddSHONG Yifan        List: A list of test flags
277*d4726bddSHONG Yifan    """
278*d4726bddSHONG Yifan    if getattr(attr, "use_libtest_harness", True):
279*d4726bddSHONG Yifan        rust_flags = ["--test"]
280*d4726bddSHONG Yifan    else:
281*d4726bddSHONG Yifan        rust_flags = ["--cfg", "test"]
282*d4726bddSHONG Yifan
283*d4726bddSHONG Yifan    return rust_flags
284*d4726bddSHONG Yifan
285*d4726bddSHONG Yifandef _rust_test_impl(ctx):
286*d4726bddSHONG Yifan    """The implementation of the `rust_test` rule.
287*d4726bddSHONG Yifan
288*d4726bddSHONG Yifan    Args:
289*d4726bddSHONG Yifan        ctx (ctx): The ctx object for the current target.
290*d4726bddSHONG Yifan
291*d4726bddSHONG Yifan    Returns:
292*d4726bddSHONG Yifan        list: The list of providers. See `rustc_compile_action`
293*d4726bddSHONG Yifan    """
294*d4726bddSHONG Yifan    _assert_no_deprecated_attributes(ctx)
295*d4726bddSHONG Yifan    _assert_correct_dep_mapping(ctx)
296*d4726bddSHONG Yifan
297*d4726bddSHONG Yifan    toolchain = find_toolchain(ctx)
298*d4726bddSHONG Yifan
299*d4726bddSHONG Yifan    crate_type = "bin"
300*d4726bddSHONG Yifan    deps = transform_deps(ctx.attr.deps)
301*d4726bddSHONG Yifan    proc_macro_deps = transform_deps(ctx.attr.proc_macro_deps + get_import_macro_deps(ctx))
302*d4726bddSHONG Yifan
303*d4726bddSHONG Yifan    if ctx.attr.crate and ctx.attr.srcs:
304*d4726bddSHONG Yifan        fail("rust_test.crate and rust_test.srcs are mutually exclusive. Update {} to use only one of these attributes".format(
305*d4726bddSHONG Yifan            ctx.label,
306*d4726bddSHONG Yifan        ))
307*d4726bddSHONG Yifan
308*d4726bddSHONG Yifan    if ctx.attr.crate:
309*d4726bddSHONG Yifan        # Target is building the crate in `test` config
310*d4726bddSHONG Yifan        crate = ctx.attr.crate[rust_common.crate_info] if rust_common.crate_info in ctx.attr.crate else ctx.attr.crate[rust_common.test_crate_info].crate
311*d4726bddSHONG Yifan
312*d4726bddSHONG Yifan        output_hash = determine_output_hash(crate.root, ctx.label)
313*d4726bddSHONG Yifan        output = ctx.actions.declare_file(
314*d4726bddSHONG Yifan            "test-%s/%s%s" % (
315*d4726bddSHONG Yifan                output_hash,
316*d4726bddSHONG Yifan                ctx.label.name,
317*d4726bddSHONG Yifan                toolchain.binary_ext,
318*d4726bddSHONG Yifan            ),
319*d4726bddSHONG Yifan        )
320*d4726bddSHONG Yifan
321*d4726bddSHONG Yifan        srcs, crate_root = transform_sources(ctx, ctx.files.srcs, getattr(ctx.file, "crate_root", None))
322*d4726bddSHONG Yifan
323*d4726bddSHONG Yifan        # Optionally join compile data
324*d4726bddSHONG Yifan        if crate.compile_data:
325*d4726bddSHONG Yifan            compile_data = depset(ctx.files.compile_data, transitive = [crate.compile_data])
326*d4726bddSHONG Yifan        else:
327*d4726bddSHONG Yifan            compile_data = depset(ctx.files.compile_data)
328*d4726bddSHONG Yifan        if crate.compile_data_targets:
329*d4726bddSHONG Yifan            compile_data_targets = depset(ctx.attr.compile_data, transitive = [crate.compile_data_targets])
330*d4726bddSHONG Yifan        else:
331*d4726bddSHONG Yifan            compile_data_targets = depset(ctx.attr.compile_data)
332*d4726bddSHONG Yifan        rustc_env_files = ctx.files.rustc_env_files + crate.rustc_env_files
333*d4726bddSHONG Yifan
334*d4726bddSHONG Yifan        # crate.rustc_env is already expanded upstream in rust_library rule implementation
335*d4726bddSHONG Yifan        rustc_env = dict(crate.rustc_env)
336*d4726bddSHONG Yifan        data_paths = depset(direct = getattr(ctx.attr, "data", [])).to_list()
337*d4726bddSHONG Yifan        rustc_env.update(expand_dict_value_locations(
338*d4726bddSHONG Yifan            ctx,
339*d4726bddSHONG Yifan            ctx.attr.rustc_env,
340*d4726bddSHONG Yifan            data_paths,
341*d4726bddSHONG Yifan        ))
342*d4726bddSHONG Yifan
343*d4726bddSHONG Yifan        # Build the test binary using the dependency's srcs.
344*d4726bddSHONG Yifan        crate_info_dict = dict(
345*d4726bddSHONG Yifan            name = crate.name,
346*d4726bddSHONG Yifan            type = crate_type,
347*d4726bddSHONG Yifan            root = crate.root,
348*d4726bddSHONG Yifan            srcs = depset(srcs, transitive = [crate.srcs]),
349*d4726bddSHONG Yifan            deps = depset(deps, transitive = [crate.deps]),
350*d4726bddSHONG Yifan            proc_macro_deps = depset(proc_macro_deps, transitive = [crate.proc_macro_deps]),
351*d4726bddSHONG Yifan            aliases = ctx.attr.aliases,
352*d4726bddSHONG Yifan            output = output,
353*d4726bddSHONG Yifan            rustc_output = generate_output_diagnostics(ctx, output),
354*d4726bddSHONG Yifan            edition = crate.edition,
355*d4726bddSHONG Yifan            rustc_env = rustc_env,
356*d4726bddSHONG Yifan            rustc_env_files = rustc_env_files,
357*d4726bddSHONG Yifan            is_test = True,
358*d4726bddSHONG Yifan            compile_data = compile_data,
359*d4726bddSHONG Yifan            compile_data_targets = compile_data_targets,
360*d4726bddSHONG Yifan            wrapped_crate_type = crate.type,
361*d4726bddSHONG Yifan            owner = ctx.label,
362*d4726bddSHONG Yifan        )
363*d4726bddSHONG Yifan    else:
364*d4726bddSHONG Yifan        crate_root = getattr(ctx.file, "crate_root", None)
365*d4726bddSHONG Yifan
366*d4726bddSHONG Yifan        if not crate_root:
367*d4726bddSHONG Yifan            crate_root_type = "lib" if ctx.attr.use_libtest_harness else "bin"
368*d4726bddSHONG Yifan            crate_root = crate_root_src(ctx.attr.name, ctx.files.srcs, crate_root_type)
369*d4726bddSHONG Yifan        srcs, crate_root = transform_sources(ctx, ctx.files.srcs, crate_root)
370*d4726bddSHONG Yifan
371*d4726bddSHONG Yifan        output_hash = determine_output_hash(crate_root, ctx.label)
372*d4726bddSHONG Yifan        output = ctx.actions.declare_file(
373*d4726bddSHONG Yifan            "test-%s/%s%s" % (
374*d4726bddSHONG Yifan                output_hash,
375*d4726bddSHONG Yifan                ctx.label.name,
376*d4726bddSHONG Yifan                toolchain.binary_ext,
377*d4726bddSHONG Yifan            ),
378*d4726bddSHONG Yifan        )
379*d4726bddSHONG Yifan
380*d4726bddSHONG Yifan        data_paths = depset(direct = getattr(ctx.attr, "data", [])).to_list()
381*d4726bddSHONG Yifan        rustc_env = expand_dict_value_locations(
382*d4726bddSHONG Yifan            ctx,
383*d4726bddSHONG Yifan            ctx.attr.rustc_env,
384*d4726bddSHONG Yifan            data_paths,
385*d4726bddSHONG Yifan        )
386*d4726bddSHONG Yifan
387*d4726bddSHONG Yifan        # Target is a standalone crate. Build the test binary as its own crate.
388*d4726bddSHONG Yifan        crate_info_dict = dict(
389*d4726bddSHONG Yifan            name = compute_crate_name(ctx.workspace_name, ctx.label, toolchain, ctx.attr.crate_name),
390*d4726bddSHONG Yifan            type = crate_type,
391*d4726bddSHONG Yifan            root = crate_root,
392*d4726bddSHONG Yifan            srcs = depset(srcs),
393*d4726bddSHONG Yifan            deps = depset(deps),
394*d4726bddSHONG Yifan            proc_macro_deps = depset(proc_macro_deps),
395*d4726bddSHONG Yifan            aliases = ctx.attr.aliases,
396*d4726bddSHONG Yifan            output = output,
397*d4726bddSHONG Yifan            rustc_output = generate_output_diagnostics(ctx, output),
398*d4726bddSHONG Yifan            edition = get_edition(ctx.attr, toolchain, ctx.label),
399*d4726bddSHONG Yifan            rustc_env = rustc_env,
400*d4726bddSHONG Yifan            rustc_env_files = ctx.files.rustc_env_files,
401*d4726bddSHONG Yifan            is_test = True,
402*d4726bddSHONG Yifan            compile_data = depset(ctx.files.compile_data),
403*d4726bddSHONG Yifan            compile_data_targets = depset(ctx.attr.compile_data),
404*d4726bddSHONG Yifan            owner = ctx.label,
405*d4726bddSHONG Yifan        )
406*d4726bddSHONG Yifan
407*d4726bddSHONG Yifan    providers = rustc_compile_action(
408*d4726bddSHONG Yifan        ctx = ctx,
409*d4726bddSHONG Yifan        attr = ctx.attr,
410*d4726bddSHONG Yifan        toolchain = toolchain,
411*d4726bddSHONG Yifan        crate_info_dict = crate_info_dict,
412*d4726bddSHONG Yifan        rust_flags = get_rust_test_flags(ctx.attr),
413*d4726bddSHONG Yifan        skip_expanding_rustc_env = True,
414*d4726bddSHONG Yifan    )
415*d4726bddSHONG Yifan    data = getattr(ctx.attr, "data", [])
416*d4726bddSHONG Yifan
417*d4726bddSHONG Yifan    env = expand_dict_value_locations(
418*d4726bddSHONG Yifan        ctx,
419*d4726bddSHONG Yifan        getattr(ctx.attr, "env", {}),
420*d4726bddSHONG Yifan        data,
421*d4726bddSHONG Yifan    )
422*d4726bddSHONG Yifan    if toolchain.llvm_cov and ctx.configuration.coverage_enabled:
423*d4726bddSHONG Yifan        if not toolchain.llvm_profdata:
424*d4726bddSHONG Yifan            fail("toolchain.llvm_profdata is required if toolchain.llvm_cov is set.")
425*d4726bddSHONG Yifan
426*d4726bddSHONG Yifan        if toolchain._experimental_use_coverage_metadata_files:
427*d4726bddSHONG Yifan            llvm_cov_path = toolchain.llvm_cov.path
428*d4726bddSHONG Yifan            llvm_profdata_path = toolchain.llvm_profdata.path
429*d4726bddSHONG Yifan        else:
430*d4726bddSHONG Yifan            llvm_cov_path = toolchain.llvm_cov.short_path
431*d4726bddSHONG Yifan            if llvm_cov_path.startswith("../"):
432*d4726bddSHONG Yifan                llvm_cov_path = llvm_cov_path[len("../"):]
433*d4726bddSHONG Yifan
434*d4726bddSHONG Yifan            llvm_profdata_path = toolchain.llvm_profdata.short_path
435*d4726bddSHONG Yifan            if llvm_profdata_path.startswith("../"):
436*d4726bddSHONG Yifan                llvm_profdata_path = llvm_profdata_path[len("../"):]
437*d4726bddSHONG Yifan
438*d4726bddSHONG Yifan        env["RUST_LLVM_COV"] = llvm_cov_path
439*d4726bddSHONG Yifan        env["RUST_LLVM_PROFDATA"] = llvm_profdata_path
440*d4726bddSHONG Yifan    components = "{}/{}".format(ctx.label.workspace_root, ctx.label.package).split("/")
441*d4726bddSHONG Yifan    env["CARGO_MANIFEST_DIR"] = "/".join([c for c in components if c])
442*d4726bddSHONG Yifan    providers.append(testing.TestEnvironment(env))
443*d4726bddSHONG Yifan
444*d4726bddSHONG Yifan    return providers
445*d4726bddSHONG Yifan
446*d4726bddSHONG Yifandef _rust_library_group_impl(ctx):
447*d4726bddSHONG Yifan    dep_variant_infos = []
448*d4726bddSHONG Yifan    dep_variant_transitive_infos = []
449*d4726bddSHONG Yifan    runfiles = []
450*d4726bddSHONG Yifan
451*d4726bddSHONG Yifan    for dep in ctx.attr.deps:
452*d4726bddSHONG Yifan        if rust_common.crate_info in dep:
453*d4726bddSHONG Yifan            dep_variant_infos.append(rust_common.dep_variant_info(
454*d4726bddSHONG Yifan                crate_info = dep[rust_common.crate_info] if rust_common.crate_info in dep else None,
455*d4726bddSHONG Yifan                dep_info = dep[rust_common.dep_info] if rust_common.crate_info in dep else None,
456*d4726bddSHONG Yifan                build_info = dep[BuildInfo] if BuildInfo in dep else None,
457*d4726bddSHONG Yifan                cc_info = dep[CcInfo] if CcInfo in dep else None,
458*d4726bddSHONG Yifan                crate_group_info = None,
459*d4726bddSHONG Yifan            ))
460*d4726bddSHONG Yifan        elif rust_common.crate_group_info in dep:
461*d4726bddSHONG Yifan            dep_variant_transitive_infos.append(dep[rust_common.crate_group_info].dep_variant_infos)
462*d4726bddSHONG Yifan        else:
463*d4726bddSHONG Yifan            fail("crate_group_info targets can only depend on rust_library or rust_library_group targets.")
464*d4726bddSHONG Yifan
465*d4726bddSHONG Yifan        if dep[DefaultInfo].default_runfiles != None:
466*d4726bddSHONG Yifan            runfiles.append(dep[DefaultInfo].default_runfiles)
467*d4726bddSHONG Yifan
468*d4726bddSHONG Yifan    return [
469*d4726bddSHONG Yifan        rust_common.crate_group_info(
470*d4726bddSHONG Yifan            dep_variant_infos = depset(dep_variant_infos, transitive = dep_variant_transitive_infos),
471*d4726bddSHONG Yifan        ),
472*d4726bddSHONG Yifan        DefaultInfo(runfiles = ctx.runfiles().merge_all(runfiles)),
473*d4726bddSHONG Yifan        coverage_common.instrumented_files_info(
474*d4726bddSHONG Yifan            ctx,
475*d4726bddSHONG Yifan            dependency_attributes = ["deps"],
476*d4726bddSHONG Yifan        ),
477*d4726bddSHONG Yifan    ]
478*d4726bddSHONG Yifan
479*d4726bddSHONG Yifandef _stamp_attribute(default_value):
480*d4726bddSHONG Yifan    return attr.int(
481*d4726bddSHONG Yifan        doc = dedent("""\
482*d4726bddSHONG Yifan            Whether to encode build information into the `Rustc` action. Possible values:
483*d4726bddSHONG Yifan
484*d4726bddSHONG Yifan            - `stamp = 1`: Always stamp the build information into the `Rustc` action, even in \
485*d4726bddSHONG Yifan            [--nostamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) builds. \
486*d4726bddSHONG Yifan            This setting should be avoided, since it potentially kills remote caching for the target and \
487*d4726bddSHONG Yifan            any downstream actions that depend on it.
488*d4726bddSHONG Yifan
489*d4726bddSHONG Yifan            - `stamp = 0`: Always replace build information by constant values. This gives good build result caching.
490*d4726bddSHONG Yifan
491*d4726bddSHONG Yifan            - `stamp = -1`: Embedding of build information is controlled by the \
492*d4726bddSHONG Yifan            [--[no]stamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) flag.
493*d4726bddSHONG Yifan
494*d4726bddSHONG Yifan            Stamped targets are not rebuilt unless their dependencies change.
495*d4726bddSHONG Yifan
496*d4726bddSHONG Yifan            For example if a `rust_library` is stamped, and a `rust_binary` depends on that library, the stamped
497*d4726bddSHONG Yifan            library won't be rebuilt when we change sources of the `rust_binary`. This is different from how
498*d4726bddSHONG Yifan            [`cc_library.linkstamps`](https://docs.bazel.build/versions/main/be/c-cpp.html#cc_library.linkstamp)
499*d4726bddSHONG Yifan            behaves.
500*d4726bddSHONG Yifan        """),
501*d4726bddSHONG Yifan        default = default_value,
502*d4726bddSHONG Yifan        values = [1, 0, -1],
503*d4726bddSHONG Yifan    )
504*d4726bddSHONG Yifan
505*d4726bddSHONG Yifan# Internal attributes core to Rustc actions.
506*d4726bddSHONG YifanRUSTC_ATTRS = {
507*d4726bddSHONG Yifan    "_cc_toolchain": attr.label(
508*d4726bddSHONG Yifan        doc = (
509*d4726bddSHONG Yifan            "In order to use find_cc_toolchain, your rule has to depend " +
510*d4726bddSHONG Yifan            "on C++ toolchain. See `@rules_cc//cc:find_cc_toolchain.bzl` " +
511*d4726bddSHONG Yifan            "docs for details."
512*d4726bddSHONG Yifan        ),
513*d4726bddSHONG Yifan        default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"),
514*d4726bddSHONG Yifan    ),
515*d4726bddSHONG Yifan    "_error_format": attr.label(
516*d4726bddSHONG Yifan        default = Label("//:error_format"),
517*d4726bddSHONG Yifan    ),
518*d4726bddSHONG Yifan    "_extra_exec_rustc_flag": attr.label(
519*d4726bddSHONG Yifan        default = Label("//:extra_exec_rustc_flag"),
520*d4726bddSHONG Yifan    ),
521*d4726bddSHONG Yifan    "_extra_exec_rustc_flags": attr.label(
522*d4726bddSHONG Yifan        default = Label("//:extra_exec_rustc_flags"),
523*d4726bddSHONG Yifan    ),
524*d4726bddSHONG Yifan    "_extra_rustc_flag": attr.label(
525*d4726bddSHONG Yifan        default = Label("//:extra_rustc_flag"),
526*d4726bddSHONG Yifan    ),
527*d4726bddSHONG Yifan    "_extra_rustc_flags": attr.label(
528*d4726bddSHONG Yifan        default = Label("//:extra_rustc_flags"),
529*d4726bddSHONG Yifan    ),
530*d4726bddSHONG Yifan    "_is_proc_macro_dep": attr.label(
531*d4726bddSHONG Yifan        default = Label("//rust/private:is_proc_macro_dep"),
532*d4726bddSHONG Yifan    ),
533*d4726bddSHONG Yifan    "_is_proc_macro_dep_enabled": attr.label(
534*d4726bddSHONG Yifan        default = Label("//rust/private:is_proc_macro_dep_enabled"),
535*d4726bddSHONG Yifan    ),
536*d4726bddSHONG Yifan    "_per_crate_rustc_flag": attr.label(
537*d4726bddSHONG Yifan        default = Label("//:experimental_per_crate_rustc_flag"),
538*d4726bddSHONG Yifan    ),
539*d4726bddSHONG Yifan    "_process_wrapper": attr.label(
540*d4726bddSHONG Yifan        doc = "A process wrapper for running rustc on all platforms.",
541*d4726bddSHONG Yifan        default = Label("//util/process_wrapper"),
542*d4726bddSHONG Yifan        executable = True,
543*d4726bddSHONG Yifan        allow_single_file = True,
544*d4726bddSHONG Yifan        cfg = "exec",
545*d4726bddSHONG Yifan    ),
546*d4726bddSHONG Yifan    "_rustc_output_diagnostics": attr.label(
547*d4726bddSHONG Yifan        default = Label("//:rustc_output_diagnostics"),
548*d4726bddSHONG Yifan    ),
549*d4726bddSHONG Yifan}
550*d4726bddSHONG Yifan
551*d4726bddSHONG Yifan_common_attrs = {
552*d4726bddSHONG Yifan    "aliases": attr.label_keyed_string_dict(
553*d4726bddSHONG Yifan        doc = dedent("""\
554*d4726bddSHONG Yifan            Remap crates to a new name or moniker for linkage to this target
555*d4726bddSHONG Yifan
556*d4726bddSHONG Yifan            These are other `rust_library` targets and will be presented as the new name given.
557*d4726bddSHONG Yifan        """),
558*d4726bddSHONG Yifan    ),
559*d4726bddSHONG Yifan    "alwayslink": attr.bool(
560*d4726bddSHONG Yifan        doc = dedent("""\
561*d4726bddSHONG Yifan            If 1, any binary that depends (directly or indirectly) on this library
562*d4726bddSHONG Yifan            will link in all the object files even if some contain no symbols referenced by the binary.
563*d4726bddSHONG Yifan
564*d4726bddSHONG Yifan            This attribute is used by the C++ Starlark API when passing CcInfo providers.
565*d4726bddSHONG Yifan        """),
566*d4726bddSHONG Yifan        default = False,
567*d4726bddSHONG Yifan    ),
568*d4726bddSHONG Yifan    "compile_data": attr.label_list(
569*d4726bddSHONG Yifan        doc = dedent("""\
570*d4726bddSHONG Yifan            List of files used by this rule at compile time.
571*d4726bddSHONG Yifan
572*d4726bddSHONG Yifan            This attribute can be used to specify any data files that are embedded into
573*d4726bddSHONG Yifan            the library, such as via the
574*d4726bddSHONG Yifan            [`include_str!`](https://doc.rust-lang.org/std/macro.include_str!.html)
575*d4726bddSHONG Yifan            macro.
576*d4726bddSHONG Yifan        """),
577*d4726bddSHONG Yifan        allow_files = True,
578*d4726bddSHONG Yifan    ),
579*d4726bddSHONG Yifan    "crate_features": attr.string_list(
580*d4726bddSHONG Yifan        doc = dedent("""\
581*d4726bddSHONG Yifan            List of features to enable for this crate.
582*d4726bddSHONG Yifan
583*d4726bddSHONG Yifan            Features are defined in the code using the `#[cfg(feature = "foo")]`
584*d4726bddSHONG Yifan            configuration option. The features listed here will be passed to `rustc`
585*d4726bddSHONG Yifan            with `--cfg feature="${feature_name}"` flags.
586*d4726bddSHONG Yifan        """),
587*d4726bddSHONG Yifan    ),
588*d4726bddSHONG Yifan    "crate_name": attr.string(
589*d4726bddSHONG Yifan        doc = dedent("""\
590*d4726bddSHONG Yifan            Crate name to use for this target.
591*d4726bddSHONG Yifan
592*d4726bddSHONG Yifan            This must be a valid Rust identifier, i.e. it may contain only alphanumeric characters and underscores.
593*d4726bddSHONG Yifan            Defaults to the target name, with any hyphens replaced by underscores.
594*d4726bddSHONG Yifan        """),
595*d4726bddSHONG Yifan    ),
596*d4726bddSHONG Yifan    "crate_root": attr.label(
597*d4726bddSHONG Yifan        doc = dedent("""\
598*d4726bddSHONG Yifan            The file that will be passed to `rustc` to be used for building this crate.
599*d4726bddSHONG Yifan
600*d4726bddSHONG Yifan            If `crate_root` is not set, then this rule will look for a `lib.rs` file (or `main.rs` for rust_binary)
601*d4726bddSHONG Yifan            or the single file in `srcs` if `srcs` contains only one file.
602*d4726bddSHONG Yifan        """),
603*d4726bddSHONG Yifan        allow_single_file = [".rs"],
604*d4726bddSHONG Yifan    ),
605*d4726bddSHONG Yifan    "data": attr.label_list(
606*d4726bddSHONG Yifan        doc = dedent("""\
607*d4726bddSHONG Yifan            List of files used by this rule at compile time and runtime.
608*d4726bddSHONG Yifan
609*d4726bddSHONG Yifan            If including data at compile time with include_str!() and similar,
610*d4726bddSHONG Yifan            prefer `compile_data` over `data`, to prevent the data also being included
611*d4726bddSHONG Yifan            in the runfiles.
612*d4726bddSHONG Yifan        """),
613*d4726bddSHONG Yifan        allow_files = True,
614*d4726bddSHONG Yifan    ),
615*d4726bddSHONG Yifan    "deps": attr.label_list(
616*d4726bddSHONG Yifan        doc = dedent("""\
617*d4726bddSHONG Yifan            List of other libraries to be linked to this library target.
618*d4726bddSHONG Yifan
619*d4726bddSHONG Yifan            These can be either other `rust_library` targets or `cc_library` targets if
620*d4726bddSHONG Yifan            linking a native library.
621*d4726bddSHONG Yifan        """),
622*d4726bddSHONG Yifan    ),
623*d4726bddSHONG Yifan    "edition": attr.string(
624*d4726bddSHONG Yifan        doc = "The rust edition to use for this crate. Defaults to the edition specified in the rust_toolchain.",
625*d4726bddSHONG Yifan    ),
626*d4726bddSHONG Yifan    # Previously `proc_macro_deps` were a part of `deps`, and then proc_macro_host_transition was
627*d4726bddSHONG Yifan    # used into cfg="host" using `@local_config_platform//:host`.
628*d4726bddSHONG Yifan    # This fails for remote execution, which needs cfg="exec", and there isn't anything like
629*d4726bddSHONG Yifan    # `@local_config_platform//:exec` exposed.
630*d4726bddSHONG Yifan    "proc_macro_deps": attr.label_list(
631*d4726bddSHONG Yifan        doc = dedent("""\
632*d4726bddSHONG Yifan            List of `rust_proc_macro` targets used to help build this library target.
633*d4726bddSHONG Yifan        """),
634*d4726bddSHONG Yifan        cfg = "exec",
635*d4726bddSHONG Yifan        providers = [rust_common.crate_info],
636*d4726bddSHONG Yifan    ),
637*d4726bddSHONG Yifan    "rustc_env": attr.string_dict(
638*d4726bddSHONG Yifan        doc = dedent("""\
639*d4726bddSHONG Yifan            Dictionary of additional `"key": "value"` environment variables to set for rustc.
640*d4726bddSHONG Yifan
641*d4726bddSHONG Yifan            rust_test()/rust_binary() rules can use $(rootpath //package:target) to pass in the
642*d4726bddSHONG Yifan            location of a generated file or external tool. Cargo build scripts that wish to
643*d4726bddSHONG Yifan            expand locations should use cargo_build_script()'s build_script_env argument instead,
644*d4726bddSHONG Yifan            as build scripts are run in a different environment - see cargo_build_script()'s
645*d4726bddSHONG Yifan            documentation for more.
646*d4726bddSHONG Yifan        """),
647*d4726bddSHONG Yifan    ),
648*d4726bddSHONG Yifan    "rustc_env_files": attr.label_list(
649*d4726bddSHONG Yifan        doc = dedent("""\
650*d4726bddSHONG Yifan            Files containing additional environment variables to set for rustc.
651*d4726bddSHONG Yifan
652*d4726bddSHONG Yifan            These files should  contain a single variable per line, of format
653*d4726bddSHONG Yifan            `NAME=value`, and newlines may be included in a value by ending a
654*d4726bddSHONG Yifan            line with a trailing back-slash (`\\\\`).
655*d4726bddSHONG Yifan
656*d4726bddSHONG Yifan            The order that these files will be processed is unspecified, so
657*d4726bddSHONG Yifan            multiple definitions of a particular variable are discouraged.
658*d4726bddSHONG Yifan
659*d4726bddSHONG Yifan            Note that the variables here are subject to
660*d4726bddSHONG Yifan            [workspace status](https://docs.bazel.build/versions/main/user-manual.html#workspace_status)
661*d4726bddSHONG Yifan            stamping should the `stamp` attribute be enabled. Stamp variables
662*d4726bddSHONG Yifan            should be wrapped in brackets in order to be resolved. E.g.
663*d4726bddSHONG Yifan            `NAME={WORKSPACE_STATUS_VARIABLE}`.
664*d4726bddSHONG Yifan        """),
665*d4726bddSHONG Yifan        allow_files = True,
666*d4726bddSHONG Yifan    ),
667*d4726bddSHONG Yifan    "rustc_flags": attr.string_list(
668*d4726bddSHONG Yifan        doc = dedent("""\
669*d4726bddSHONG Yifan            List of compiler flags passed to `rustc`.
670*d4726bddSHONG Yifan
671*d4726bddSHONG Yifan            These strings are subject to Make variable expansion for predefined
672*d4726bddSHONG Yifan            source/output path variables like `$location`, `$execpath`, and
673*d4726bddSHONG Yifan            `$rootpath`. This expansion is useful if you wish to pass a generated
674*d4726bddSHONG Yifan            file of arguments to rustc: `@$(location //package:target)`.
675*d4726bddSHONG Yifan        """),
676*d4726bddSHONG Yifan    ),
677*d4726bddSHONG Yifan    # TODO(stardoc): How do we provide additional documentation to an inherited attribute?
678*d4726bddSHONG Yifan    # "name": attr.string(
679*d4726bddSHONG Yifan    #     doc = "This name will also be used as the name of the crate built by this rule.",
680*d4726bddSHONG Yifan    # `),
681*d4726bddSHONG Yifan    "srcs": attr.label_list(
682*d4726bddSHONG Yifan        doc = dedent("""\
683*d4726bddSHONG Yifan            List of Rust `.rs` source files used to build the library.
684*d4726bddSHONG Yifan
685*d4726bddSHONG Yifan            If `srcs` contains more than one file, then there must be a file either
686*d4726bddSHONG Yifan            named `lib.rs`. Otherwise, `crate_root` must be set to the source file that
687*d4726bddSHONG Yifan            is the root of the crate to be passed to rustc to build this crate.
688*d4726bddSHONG Yifan        """),
689*d4726bddSHONG Yifan        allow_files = [".rs"],
690*d4726bddSHONG Yifan        # Allow use of --compile_one_dependency with rust targets. Support for this feature for
691*d4726bddSHONG Yifan        # non-builtin rulesets is undocumented outside of the bazel source:
692*d4726bddSHONG Yifan        # https://github.com/bazelbuild/bazel/blob/7.1.1/src/main/java/com/google/devtools/build/lib/packages/Attribute.java#L102
693*d4726bddSHONG Yifan        flags = ["DIRECT_COMPILE_TIME_INPUT"],
694*d4726bddSHONG Yifan    ),
695*d4726bddSHONG Yifan    "stamp": _stamp_attribute(
696*d4726bddSHONG Yifan        default_value = 0,
697*d4726bddSHONG Yifan    ),
698*d4726bddSHONG Yifan    "version": attr.string(
699*d4726bddSHONG Yifan        doc = "A version to inject in the cargo environment variable.",
700*d4726bddSHONG Yifan        default = "0.0.0",
701*d4726bddSHONG Yifan    ),
702*d4726bddSHONG Yifan    "_stamp_flag": attr.label(
703*d4726bddSHONG Yifan        doc = "A setting used to determine whether or not the `--stamp` flag is enabled",
704*d4726bddSHONG Yifan        default = Label("//rust/private:stamp"),
705*d4726bddSHONG Yifan    ),
706*d4726bddSHONG Yifan} | RUSTC_ATTRS
707*d4726bddSHONG Yifan
708*d4726bddSHONG Yifan_coverage_attrs = {
709*d4726bddSHONG Yifan    "_collect_cc_coverage": attr.label(
710*d4726bddSHONG Yifan        default = Label("//util/collect_coverage"),
711*d4726bddSHONG Yifan        executable = True,
712*d4726bddSHONG Yifan        cfg = "exec",
713*d4726bddSHONG Yifan    ),
714*d4726bddSHONG Yifan    # Bazel’s coverage runner
715*d4726bddSHONG Yifan    # (https://github.com/bazelbuild/bazel/blob/6.0.0/tools/test/collect_coverage.sh)
716*d4726bddSHONG Yifan    # needs a binary called “lcov_merge.”  Its location is passed in the
717*d4726bddSHONG Yifan    # LCOV_MERGER environmental variable.  For builtin rules, this variable
718*d4726bddSHONG Yifan    # is set automatically based on a magic “$lcov_merger” or
719*d4726bddSHONG Yifan    # “:lcov_merger” attribute, but it’s not possible to create such
720*d4726bddSHONG Yifan    # attributes in Starlark.  Therefore we specify the variable ourselves.
721*d4726bddSHONG Yifan    # Note that the coverage runner runs in the runfiles root instead of
722*d4726bddSHONG Yifan    # the execution root, therefore we use “path” instead of “short_path.”
723*d4726bddSHONG Yifan    "_lcov_merger": attr.label(
724*d4726bddSHONG Yifan        default = configuration_field(fragment = "coverage", name = "output_generator"),
725*d4726bddSHONG Yifan        executable = True,
726*d4726bddSHONG Yifan        cfg = "exec",
727*d4726bddSHONG Yifan    ),
728*d4726bddSHONG Yifan}
729*d4726bddSHONG Yifan
730*d4726bddSHONG Yifan_experimental_use_cc_common_link_attrs = {
731*d4726bddSHONG Yifan    "experimental_use_cc_common_link": attr.int(
732*d4726bddSHONG Yifan        doc = (
733*d4726bddSHONG Yifan            "Whether to use cc_common.link to link rust binaries. " +
734*d4726bddSHONG Yifan            "Possible values: [-1, 0, 1]. " +
735*d4726bddSHONG Yifan            "-1 means use the value of the toolchain.experimental_use_cc_common_link " +
736*d4726bddSHONG Yifan            "boolean build setting to determine. " +
737*d4726bddSHONG Yifan            "0 means do not use cc_common.link (use rustc instead). " +
738*d4726bddSHONG Yifan            "1 means use cc_common.link."
739*d4726bddSHONG Yifan        ),
740*d4726bddSHONG Yifan        values = [-1, 0, 1],
741*d4726bddSHONG Yifan        default = -1,
742*d4726bddSHONG Yifan    ),
743*d4726bddSHONG Yifan    "malloc": attr.label(
744*d4726bddSHONG Yifan        default = Label("@bazel_tools//tools/cpp:malloc"),
745*d4726bddSHONG Yifan        doc = """Override the default dependency on `malloc`.
746*d4726bddSHONG Yifan
747*d4726bddSHONG YifanBy default, Rust binaries linked with cc_common.link are linked against
748*d4726bddSHONG Yifan`@bazel_tools//tools/cpp:malloc"`, which is an empty library and the resulting binary will use
749*d4726bddSHONG Yifanlibc's `malloc`. This label must refer to a `cc_library` rule.
750*d4726bddSHONG Yifan""",
751*d4726bddSHONG Yifan        mandatory = False,
752*d4726bddSHONG Yifan        providers = [[CcInfo]],
753*d4726bddSHONG Yifan    ),  # A late-bound attribute denoting the value of the `--custom_malloc`
754*d4726bddSHONG Yifan    # command line flag (or None if the flag is not provided).
755*d4726bddSHONG Yifan    "_custom_malloc": attr.label(
756*d4726bddSHONG Yifan        default = configuration_field(
757*d4726bddSHONG Yifan            fragment = "cpp",
758*d4726bddSHONG Yifan            name = "custom_malloc",
759*d4726bddSHONG Yifan        ),
760*d4726bddSHONG Yifan        providers = [[CcInfo]],
761*d4726bddSHONG Yifan    ),
762*d4726bddSHONG Yifan}
763*d4726bddSHONG Yifan
764*d4726bddSHONG Yifan_rust_test_attrs = dict({
765*d4726bddSHONG Yifan    "crate": attr.label(
766*d4726bddSHONG Yifan        mandatory = False,
767*d4726bddSHONG Yifan        doc = dedent("""\
768*d4726bddSHONG Yifan            Target inline tests declared in the given crate
769*d4726bddSHONG Yifan
770*d4726bddSHONG Yifan            These tests are typically those that would be held out under
771*d4726bddSHONG Yifan            `#[cfg(test)]` declarations.
772*d4726bddSHONG Yifan        """),
773*d4726bddSHONG Yifan    ),
774*d4726bddSHONG Yifan    "env": attr.string_dict(
775*d4726bddSHONG Yifan        mandatory = False,
776*d4726bddSHONG Yifan        doc = dedent("""\
777*d4726bddSHONG Yifan            Specifies additional environment variables to set when the test is executed by bazel test.
778*d4726bddSHONG Yifan            Values are subject to `$(rootpath)`, `$(execpath)`, location, and
779*d4726bddSHONG Yifan            ["Make variable"](https://docs.bazel.build/versions/master/be/make-variables.html) substitution.
780*d4726bddSHONG Yifan        """),
781*d4726bddSHONG Yifan    ),
782*d4726bddSHONG Yifan    "use_libtest_harness": attr.bool(
783*d4726bddSHONG Yifan        mandatory = False,
784*d4726bddSHONG Yifan        default = True,
785*d4726bddSHONG Yifan        doc = dedent("""\
786*d4726bddSHONG Yifan            Whether to use `libtest`. For targets using this flag, individual tests can be run by using the
787*d4726bddSHONG Yifan            [--test_arg](https://docs.bazel.build/versions/4.0.0/command-line-reference.html#flag--test_arg) flag.
788*d4726bddSHONG Yifan            E.g. `bazel test //src:rust_test --test_arg=foo::test::test_fn`.
789*d4726bddSHONG Yifan        """),
790*d4726bddSHONG Yifan    ),
791*d4726bddSHONG Yifan    "_use_grep_includes": attr.bool(default = True),
792*d4726bddSHONG Yifan}.items() + _coverage_attrs.items() + _experimental_use_cc_common_link_attrs.items())
793*d4726bddSHONG Yifan
794*d4726bddSHONG Yifanrust_library = rule(
795*d4726bddSHONG Yifan    implementation = _rust_library_impl,
796*d4726bddSHONG Yifan    provides = COMMON_PROVIDERS,
797*d4726bddSHONG Yifan    attrs = dict(_common_attrs.items() + {
798*d4726bddSHONG Yifan        "disable_pipelining": attr.bool(
799*d4726bddSHONG Yifan            default = False,
800*d4726bddSHONG Yifan            doc = dedent("""\
801*d4726bddSHONG Yifan                Disables pipelining for this rule if it is globally enabled.
802*d4726bddSHONG Yifan                This will cause this rule to not produce a `.rmeta` file and all the dependent
803*d4726bddSHONG Yifan                crates will instead use the `.rlib` file.
804*d4726bddSHONG Yifan            """),
805*d4726bddSHONG Yifan        ),
806*d4726bddSHONG Yifan    }.items()),
807*d4726bddSHONG Yifan    fragments = ["cpp"],
808*d4726bddSHONG Yifan    toolchains = [
809*d4726bddSHONG Yifan        str(Label("//rust:toolchain_type")),
810*d4726bddSHONG Yifan        "@bazel_tools//tools/cpp:toolchain_type",
811*d4726bddSHONG Yifan    ],
812*d4726bddSHONG Yifan    doc = dedent("""\
813*d4726bddSHONG Yifan        Builds a Rust library crate.
814*d4726bddSHONG Yifan
815*d4726bddSHONG Yifan        Example:
816*d4726bddSHONG Yifan
817*d4726bddSHONG Yifan        Suppose you have the following directory structure for a simple Rust library crate:
818*d4726bddSHONG Yifan
819*d4726bddSHONG Yifan        ```output
820*d4726bddSHONG Yifan        [workspace]/
821*d4726bddSHONG Yifan            WORKSPACE
822*d4726bddSHONG Yifan            hello_lib/
823*d4726bddSHONG Yifan                BUILD
824*d4726bddSHONG Yifan                src/
825*d4726bddSHONG Yifan                    greeter.rs
826*d4726bddSHONG Yifan                    lib.rs
827*d4726bddSHONG Yifan        ```
828*d4726bddSHONG Yifan
829*d4726bddSHONG Yifan        `hello_lib/src/greeter.rs`:
830*d4726bddSHONG Yifan        ```rust
831*d4726bddSHONG Yifan        pub struct Greeter {
832*d4726bddSHONG Yifan            greeting: String,
833*d4726bddSHONG Yifan        }
834*d4726bddSHONG Yifan
835*d4726bddSHONG Yifan        impl Greeter {
836*d4726bddSHONG Yifan            pub fn new(greeting: &str) -> Greeter {
837*d4726bddSHONG Yifan                Greeter { greeting: greeting.to_string(), }
838*d4726bddSHONG Yifan            }
839*d4726bddSHONG Yifan
840*d4726bddSHONG Yifan            pub fn greet(&self, thing: &str) {
841*d4726bddSHONG Yifan                println!("{} {}", &self.greeting, thing);
842*d4726bddSHONG Yifan            }
843*d4726bddSHONG Yifan        }
844*d4726bddSHONG Yifan        ```
845*d4726bddSHONG Yifan
846*d4726bddSHONG Yifan        `hello_lib/src/lib.rs`:
847*d4726bddSHONG Yifan
848*d4726bddSHONG Yifan        ```rust
849*d4726bddSHONG Yifan        pub mod greeter;
850*d4726bddSHONG Yifan        ```
851*d4726bddSHONG Yifan
852*d4726bddSHONG Yifan        `hello_lib/BUILD`:
853*d4726bddSHONG Yifan        ```python
854*d4726bddSHONG Yifan        package(default_visibility = ["//visibility:public"])
855*d4726bddSHONG Yifan
856*d4726bddSHONG Yifan        load("@rules_rust//rust:defs.bzl", "rust_library")
857*d4726bddSHONG Yifan
858*d4726bddSHONG Yifan        rust_library(
859*d4726bddSHONG Yifan            name = "hello_lib",
860*d4726bddSHONG Yifan            srcs = [
861*d4726bddSHONG Yifan                "src/greeter.rs",
862*d4726bddSHONG Yifan                "src/lib.rs",
863*d4726bddSHONG Yifan            ],
864*d4726bddSHONG Yifan        )
865*d4726bddSHONG Yifan        ```
866*d4726bddSHONG Yifan
867*d4726bddSHONG Yifan        Build the library:
868*d4726bddSHONG Yifan        ```output
869*d4726bddSHONG Yifan        $ bazel build //hello_lib
870*d4726bddSHONG Yifan        INFO: Found 1 target...
871*d4726bddSHONG Yifan        Target //examples/rust/hello_lib:hello_lib up-to-date:
872*d4726bddSHONG Yifan        bazel-bin/examples/rust/hello_lib/libhello_lib.rlib
873*d4726bddSHONG Yifan        INFO: Elapsed time: 1.245s, Critical Path: 1.01s
874*d4726bddSHONG Yifan        ```
875*d4726bddSHONG Yifan        """),
876*d4726bddSHONG Yifan)
877*d4726bddSHONG Yifan
878*d4726bddSHONG Yifandef _rust_static_library_transition_impl(settings, attr):
879*d4726bddSHONG Yifan    return {
880*d4726bddSHONG Yifan        "//command_line_option:platforms": str(attr.platform) if attr.platform else settings["//command_line_option:platforms"],
881*d4726bddSHONG Yifan    }
882*d4726bddSHONG Yifan
883*d4726bddSHONG Yifan_rust_static_library_transition = transition(
884*d4726bddSHONG Yifan    implementation = _rust_static_library_transition_impl,
885*d4726bddSHONG Yifan    inputs = [
886*d4726bddSHONG Yifan        "//command_line_option:platforms",
887*d4726bddSHONG Yifan    ],
888*d4726bddSHONG Yifan    outputs = [
889*d4726bddSHONG Yifan        "//command_line_option:platforms",
890*d4726bddSHONG Yifan    ],
891*d4726bddSHONG Yifan)
892*d4726bddSHONG Yifan
893*d4726bddSHONG Yifanrust_static_library = rule(
894*d4726bddSHONG Yifan    implementation = _rust_static_library_impl,
895*d4726bddSHONG Yifan    attrs = dict(_common_attrs.items() + {
896*d4726bddSHONG Yifan        "platform": attr.label(
897*d4726bddSHONG Yifan            doc = "Optional platform to transition the static library to.",
898*d4726bddSHONG Yifan            default = None,
899*d4726bddSHONG Yifan        ),
900*d4726bddSHONG Yifan        "_allowlist_function_transition": attr.label(
901*d4726bddSHONG Yifan            default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
902*d4726bddSHONG Yifan        ),
903*d4726bddSHONG Yifan    }.items()),
904*d4726bddSHONG Yifan    fragments = ["cpp"],
905*d4726bddSHONG Yifan    cfg = _rust_static_library_transition,
906*d4726bddSHONG Yifan    toolchains = [
907*d4726bddSHONG Yifan        str(Label("//rust:toolchain_type")),
908*d4726bddSHONG Yifan        "@bazel_tools//tools/cpp:toolchain_type",
909*d4726bddSHONG Yifan    ],
910*d4726bddSHONG Yifan    provides = [CcInfo],
911*d4726bddSHONG Yifan    doc = dedent("""\
912*d4726bddSHONG Yifan        Builds a Rust static library.
913*d4726bddSHONG Yifan
914*d4726bddSHONG Yifan        This static library will contain all transitively reachable crates and native objects.
915*d4726bddSHONG Yifan        It is meant to be used when producing an artifact that is then consumed by some other build system
916*d4726bddSHONG Yifan        (for example to produce an archive that Python program links against).
917*d4726bddSHONG Yifan
918*d4726bddSHONG Yifan        This rule provides CcInfo, so it can be used everywhere Bazel expects `rules_cc`.
919*d4726bddSHONG Yifan
920*d4726bddSHONG Yifan        When building the whole binary in Bazel, use `rust_library` instead.
921*d4726bddSHONG Yifan        """),
922*d4726bddSHONG Yifan)
923*d4726bddSHONG Yifan
924*d4726bddSHONG Yifandef _rust_shared_library_transition_impl(settings, attr):
925*d4726bddSHONG Yifan    return {
926*d4726bddSHONG Yifan        "//command_line_option:platforms": str(attr.platform) if attr.platform else settings["//command_line_option:platforms"],
927*d4726bddSHONG Yifan    }
928*d4726bddSHONG Yifan
929*d4726bddSHONG Yifan_rust_shared_library_transition = transition(
930*d4726bddSHONG Yifan    implementation = _rust_shared_library_transition_impl,
931*d4726bddSHONG Yifan    inputs = [
932*d4726bddSHONG Yifan        "//command_line_option:platforms",
933*d4726bddSHONG Yifan    ],
934*d4726bddSHONG Yifan    outputs = [
935*d4726bddSHONG Yifan        "//command_line_option:platforms",
936*d4726bddSHONG Yifan    ],
937*d4726bddSHONG Yifan)
938*d4726bddSHONG Yifan
939*d4726bddSHONG Yifanrust_shared_library = rule(
940*d4726bddSHONG Yifan    implementation = _rust_shared_library_impl,
941*d4726bddSHONG Yifan    attrs = dict(_common_attrs.items() + _experimental_use_cc_common_link_attrs.items() + {
942*d4726bddSHONG Yifan        "platform": attr.label(
943*d4726bddSHONG Yifan            doc = "Optional platform to transition the shared library to.",
944*d4726bddSHONG Yifan            default = None,
945*d4726bddSHONG Yifan        ),
946*d4726bddSHONG Yifan        "_allowlist_function_transition": attr.label(
947*d4726bddSHONG Yifan            default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
948*d4726bddSHONG Yifan        ),
949*d4726bddSHONG Yifan        "_use_grep_includes": attr.bool(default = True),
950*d4726bddSHONG Yifan    }.items()),
951*d4726bddSHONG Yifan    fragments = ["cpp"],
952*d4726bddSHONG Yifan    cfg = _rust_shared_library_transition,
953*d4726bddSHONG Yifan    toolchains = [
954*d4726bddSHONG Yifan        str(Label("//rust:toolchain_type")),
955*d4726bddSHONG Yifan        "@bazel_tools//tools/cpp:toolchain_type",
956*d4726bddSHONG Yifan    ],
957*d4726bddSHONG Yifan    provides = [CcInfo],
958*d4726bddSHONG Yifan    doc = dedent("""\
959*d4726bddSHONG Yifan        Builds a Rust shared library.
960*d4726bddSHONG Yifan
961*d4726bddSHONG Yifan        This shared library will contain all transitively reachable crates and native objects.
962*d4726bddSHONG Yifan        It is meant to be used when producing an artifact that is then consumed by some other build system
963*d4726bddSHONG Yifan        (for example to produce a shared library that Python program links against).
964*d4726bddSHONG Yifan
965*d4726bddSHONG Yifan        This rule provides CcInfo, so it can be used everywhere Bazel expects `rules_cc`.
966*d4726bddSHONG Yifan
967*d4726bddSHONG Yifan        When building the whole binary in Bazel, use `rust_library` instead.
968*d4726bddSHONG Yifan        """),
969*d4726bddSHONG Yifan)
970*d4726bddSHONG Yifan
971*d4726bddSHONG Yifandef _proc_macro_dep_transition_impl(settings, _attr):
972*d4726bddSHONG Yifan    if settings["//rust/private:is_proc_macro_dep_enabled"]:
973*d4726bddSHONG Yifan        return {"//rust/private:is_proc_macro_dep": True}
974*d4726bddSHONG Yifan    else:
975*d4726bddSHONG Yifan        return []
976*d4726bddSHONG Yifan
977*d4726bddSHONG Yifan_proc_macro_dep_transition = transition(
978*d4726bddSHONG Yifan    inputs = ["//rust/private:is_proc_macro_dep_enabled"],
979*d4726bddSHONG Yifan    outputs = ["//rust/private:is_proc_macro_dep"],
980*d4726bddSHONG Yifan    implementation = _proc_macro_dep_transition_impl,
981*d4726bddSHONG Yifan)
982*d4726bddSHONG Yifan
983*d4726bddSHONG Yifanrust_proc_macro = rule(
984*d4726bddSHONG Yifan    implementation = _rust_proc_macro_impl,
985*d4726bddSHONG Yifan    provides = COMMON_PROVIDERS,
986*d4726bddSHONG Yifan    # Start by copying the common attributes, then override the `deps` attribute
987*d4726bddSHONG Yifan    # to apply `_proc_macro_dep_transition`. To add this transition we additionally
988*d4726bddSHONG Yifan    # need to declare `_allowlist_function_transition`, see
989*d4726bddSHONG Yifan    # https://docs.bazel.build/versions/main/skylark/config.html#user-defined-transitions.
990*d4726bddSHONG Yifan    attrs = dict(
991*d4726bddSHONG Yifan        _common_attrs.items(),
992*d4726bddSHONG Yifan        _allowlist_function_transition = attr.label(
993*d4726bddSHONG Yifan            default = Label("//tools/allowlists/function_transition_allowlist"),
994*d4726bddSHONG Yifan        ),
995*d4726bddSHONG Yifan        deps = attr.label_list(
996*d4726bddSHONG Yifan            doc = dedent("""\
997*d4726bddSHONG Yifan                List of other libraries to be linked to this library target.
998*d4726bddSHONG Yifan
999*d4726bddSHONG Yifan                These can be either other `rust_library` targets or `cc_library` targets if
1000*d4726bddSHONG Yifan                linking a native library.
1001*d4726bddSHONG Yifan            """),
1002*d4726bddSHONG Yifan            cfg = _proc_macro_dep_transition,
1003*d4726bddSHONG Yifan        ),
1004*d4726bddSHONG Yifan    ),
1005*d4726bddSHONG Yifan    fragments = ["cpp"],
1006*d4726bddSHONG Yifan    toolchains = [
1007*d4726bddSHONG Yifan        str(Label("//rust:toolchain_type")),
1008*d4726bddSHONG Yifan        "@bazel_tools//tools/cpp:toolchain_type",
1009*d4726bddSHONG Yifan    ],
1010*d4726bddSHONG Yifan    doc = dedent("""\
1011*d4726bddSHONG Yifan        Builds a Rust proc-macro crate.
1012*d4726bddSHONG Yifan    """),
1013*d4726bddSHONG Yifan)
1014*d4726bddSHONG Yifan
1015*d4726bddSHONG Yifan_rust_binary_attrs = dict({
1016*d4726bddSHONG Yifan    "crate_type": attr.string(
1017*d4726bddSHONG Yifan        doc = dedent("""\
1018*d4726bddSHONG Yifan            Crate type that will be passed to `rustc` to be used for building this crate.
1019*d4726bddSHONG Yifan
1020*d4726bddSHONG Yifan            This option is a temporary workaround and should be used only when building
1021*d4726bddSHONG Yifan            for WebAssembly targets (//rust/platform:wasi and //rust/platform:wasm).
1022*d4726bddSHONG Yifan        """),
1023*d4726bddSHONG Yifan        default = "bin",
1024*d4726bddSHONG Yifan    ),
1025*d4726bddSHONG Yifan    "env": attr.string_dict(
1026*d4726bddSHONG Yifan        mandatory = False,
1027*d4726bddSHONG Yifan        doc = dedent("""\
1028*d4726bddSHONG Yifan            Specifies additional environment variables to set when the target is executed by bazel run.
1029*d4726bddSHONG Yifan            Values are subject to `$(rootpath)`, `$(execpath)`, location, and
1030*d4726bddSHONG Yifan            ["Make variable"](https://docs.bazel.build/versions/master/be/make-variables.html) substitution.
1031*d4726bddSHONG Yifan
1032*d4726bddSHONG Yifan            Execpath returns absolute path, and in order to be able to construct the absolute path we
1033*d4726bddSHONG Yifan            need to wrap the test binary in a launcher. Using a launcher comes with complications, such as
1034*d4726bddSHONG Yifan            more complicated debugger attachment.
1035*d4726bddSHONG Yifan        """),
1036*d4726bddSHONG Yifan    ),
1037*d4726bddSHONG Yifan    "linker_script": attr.label(
1038*d4726bddSHONG Yifan        doc = dedent("""\
1039*d4726bddSHONG Yifan            Link script to forward into linker via rustc options.
1040*d4726bddSHONG Yifan        """),
1041*d4726bddSHONG Yifan        allow_single_file = True,
1042*d4726bddSHONG Yifan    ),
1043*d4726bddSHONG Yifan    "out_binary": attr.bool(
1044*d4726bddSHONG Yifan        doc = (
1045*d4726bddSHONG Yifan            "Force a target, regardless of it's `crate_type`, to always mark the " +
1046*d4726bddSHONG Yifan            "file as executable. This attribute is only used to support wasm targets but is " +
1047*d4726bddSHONG Yifan            "expected to be removed following a resolution to https://github.com/bazelbuild/rules_rust/issues/771."
1048*d4726bddSHONG Yifan        ),
1049*d4726bddSHONG Yifan        default = False,
1050*d4726bddSHONG Yifan    ),
1051*d4726bddSHONG Yifan    "stamp": _stamp_attribute(default_value = -1),
1052*d4726bddSHONG Yifan    "_use_grep_includes": attr.bool(default = True),
1053*d4726bddSHONG Yifan}.items() + _experimental_use_cc_common_link_attrs.items())
1054*d4726bddSHONG Yifan
1055*d4726bddSHONG Yifandef _rust_binary_transition_impl(settings, attr):
1056*d4726bddSHONG Yifan    return {
1057*d4726bddSHONG Yifan        "//command_line_option:platforms": str(attr.platform) if attr.platform else settings["//command_line_option:platforms"],
1058*d4726bddSHONG Yifan    }
1059*d4726bddSHONG Yifan
1060*d4726bddSHONG Yifan_rust_binary_transition = transition(
1061*d4726bddSHONG Yifan    implementation = _rust_binary_transition_impl,
1062*d4726bddSHONG Yifan    inputs = [
1063*d4726bddSHONG Yifan        "//command_line_option:platforms",
1064*d4726bddSHONG Yifan    ],
1065*d4726bddSHONG Yifan    outputs = [
1066*d4726bddSHONG Yifan        "//command_line_option:platforms",
1067*d4726bddSHONG Yifan    ],
1068*d4726bddSHONG Yifan)
1069*d4726bddSHONG Yifan
1070*d4726bddSHONG Yifanrust_binary = rule(
1071*d4726bddSHONG Yifan    implementation = _rust_binary_impl,
1072*d4726bddSHONG Yifan    provides = COMMON_PROVIDERS,
1073*d4726bddSHONG Yifan    attrs = dict(_common_attrs.items() + _rust_binary_attrs.items() + {
1074*d4726bddSHONG Yifan        "platform": attr.label(
1075*d4726bddSHONG Yifan            doc = "Optional platform to transition the binary to.",
1076*d4726bddSHONG Yifan            default = None,
1077*d4726bddSHONG Yifan        ),
1078*d4726bddSHONG Yifan        "_allowlist_function_transition": attr.label(
1079*d4726bddSHONG Yifan            default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
1080*d4726bddSHONG Yifan        ),
1081*d4726bddSHONG Yifan    }.items()),
1082*d4726bddSHONG Yifan    executable = True,
1083*d4726bddSHONG Yifan    fragments = ["cpp"],
1084*d4726bddSHONG Yifan    cfg = _rust_binary_transition,
1085*d4726bddSHONG Yifan    toolchains = [
1086*d4726bddSHONG Yifan        str(Label("//rust:toolchain_type")),
1087*d4726bddSHONG Yifan        "@bazel_tools//tools/cpp:toolchain_type",
1088*d4726bddSHONG Yifan    ],
1089*d4726bddSHONG Yifan    doc = dedent("""\
1090*d4726bddSHONG Yifan        Builds a Rust binary crate.
1091*d4726bddSHONG Yifan
1092*d4726bddSHONG Yifan        Example:
1093*d4726bddSHONG Yifan
1094*d4726bddSHONG Yifan        Suppose you have the following directory structure for a Rust project with a
1095*d4726bddSHONG Yifan        library crate, `hello_lib`, and a binary crate, `hello_world` that uses the
1096*d4726bddSHONG Yifan        `hello_lib` library:
1097*d4726bddSHONG Yifan
1098*d4726bddSHONG Yifan        ```output
1099*d4726bddSHONG Yifan        [workspace]/
1100*d4726bddSHONG Yifan            WORKSPACE
1101*d4726bddSHONG Yifan            hello_lib/
1102*d4726bddSHONG Yifan                BUILD
1103*d4726bddSHONG Yifan                src/
1104*d4726bddSHONG Yifan                    lib.rs
1105*d4726bddSHONG Yifan            hello_world/
1106*d4726bddSHONG Yifan                BUILD
1107*d4726bddSHONG Yifan                src/
1108*d4726bddSHONG Yifan                    main.rs
1109*d4726bddSHONG Yifan        ```
1110*d4726bddSHONG Yifan
1111*d4726bddSHONG Yifan        `hello_lib/src/lib.rs`:
1112*d4726bddSHONG Yifan        ```rust
1113*d4726bddSHONG Yifan        pub struct Greeter {
1114*d4726bddSHONG Yifan            greeting: String,
1115*d4726bddSHONG Yifan        }
1116*d4726bddSHONG Yifan
1117*d4726bddSHONG Yifan        impl Greeter {
1118*d4726bddSHONG Yifan            pub fn new(greeting: &str) -> Greeter {
1119*d4726bddSHONG Yifan                Greeter { greeting: greeting.to_string(), }
1120*d4726bddSHONG Yifan            }
1121*d4726bddSHONG Yifan
1122*d4726bddSHONG Yifan            pub fn greet(&self, thing: &str) {
1123*d4726bddSHONG Yifan                println!("{} {}", &self.greeting, thing);
1124*d4726bddSHONG Yifan            }
1125*d4726bddSHONG Yifan        }
1126*d4726bddSHONG Yifan        ```
1127*d4726bddSHONG Yifan
1128*d4726bddSHONG Yifan        `hello_lib/BUILD`:
1129*d4726bddSHONG Yifan        ```python
1130*d4726bddSHONG Yifan        package(default_visibility = ["//visibility:public"])
1131*d4726bddSHONG Yifan
1132*d4726bddSHONG Yifan        load("@rules_rust//rust:defs.bzl", "rust_library")
1133*d4726bddSHONG Yifan
1134*d4726bddSHONG Yifan        rust_library(
1135*d4726bddSHONG Yifan            name = "hello_lib",
1136*d4726bddSHONG Yifan            srcs = ["src/lib.rs"],
1137*d4726bddSHONG Yifan        )
1138*d4726bddSHONG Yifan        ```
1139*d4726bddSHONG Yifan
1140*d4726bddSHONG Yifan        `hello_world/src/main.rs`:
1141*d4726bddSHONG Yifan        ```rust
1142*d4726bddSHONG Yifan        extern crate hello_lib;
1143*d4726bddSHONG Yifan
1144*d4726bddSHONG Yifan        fn main() {
1145*d4726bddSHONG Yifan            let hello = hello_lib::Greeter::new("Hello");
1146*d4726bddSHONG Yifan            hello.greet("world");
1147*d4726bddSHONG Yifan        }
1148*d4726bddSHONG Yifan        ```
1149*d4726bddSHONG Yifan
1150*d4726bddSHONG Yifan        `hello_world/BUILD`:
1151*d4726bddSHONG Yifan        ```python
1152*d4726bddSHONG Yifan        load("@rules_rust//rust:defs.bzl", "rust_binary")
1153*d4726bddSHONG Yifan
1154*d4726bddSHONG Yifan        rust_binary(
1155*d4726bddSHONG Yifan            name = "hello_world",
1156*d4726bddSHONG Yifan            srcs = ["src/main.rs"],
1157*d4726bddSHONG Yifan            deps = ["//hello_lib"],
1158*d4726bddSHONG Yifan        )
1159*d4726bddSHONG Yifan        ```
1160*d4726bddSHONG Yifan
1161*d4726bddSHONG Yifan        Build and run `hello_world`:
1162*d4726bddSHONG Yifan        ```
1163*d4726bddSHONG Yifan        $ bazel run //hello_world
1164*d4726bddSHONG Yifan        INFO: Found 1 target...
1165*d4726bddSHONG Yifan        Target //examples/rust/hello_world:hello_world up-to-date:
1166*d4726bddSHONG Yifan        bazel-bin/examples/rust/hello_world/hello_world
1167*d4726bddSHONG Yifan        INFO: Elapsed time: 1.308s, Critical Path: 1.22s
1168*d4726bddSHONG Yifan
1169*d4726bddSHONG Yifan        INFO: Running command line: bazel-bin/examples/rust/hello_world/hello_world
1170*d4726bddSHONG Yifan        Hello world
1171*d4726bddSHONG Yifan        ```
1172*d4726bddSHONG Yifan
1173*d4726bddSHONG Yifan        On Windows, a PDB file containing debugging information is available under
1174*d4726bddSHONG Yifan        the key `pdb_file` in `OutputGroupInfo`. Similarly on macOS, a dSYM folder
1175*d4726bddSHONG Yifan        is available under the key `dsym_folder` in `OutputGroupInfo`.
1176*d4726bddSHONG Yifan"""),
1177*d4726bddSHONG Yifan)
1178*d4726bddSHONG Yifan
1179*d4726bddSHONG Yifandef _common_attrs_for_binary_without_process_wrapper(attrs):
1180*d4726bddSHONG Yifan    new_attr = dict(attrs)
1181*d4726bddSHONG Yifan
1182*d4726bddSHONG Yifan    # use a fake process wrapper
1183*d4726bddSHONG Yifan    new_attr["_process_wrapper"] = attr.label(
1184*d4726bddSHONG Yifan        default = None,
1185*d4726bddSHONG Yifan        executable = True,
1186*d4726bddSHONG Yifan        allow_single_file = True,
1187*d4726bddSHONG Yifan        cfg = "exec",
1188*d4726bddSHONG Yifan    )
1189*d4726bddSHONG Yifan
1190*d4726bddSHONG Yifan    new_attr["_bootstrap_process_wrapper"] = attr.label(
1191*d4726bddSHONG Yifan        default = Label("//util/process_wrapper:bootstrap_process_wrapper"),
1192*d4726bddSHONG Yifan        executable = True,
1193*d4726bddSHONG Yifan        allow_single_file = True,
1194*d4726bddSHONG Yifan        cfg = "exec",
1195*d4726bddSHONG Yifan    )
1196*d4726bddSHONG Yifan
1197*d4726bddSHONG Yifan    # fix stamp = 0
1198*d4726bddSHONG Yifan    new_attr["stamp"] = attr.int(
1199*d4726bddSHONG Yifan        doc = dedent("""\
1200*d4726bddSHONG Yifan            Fix `stamp = 0` as stamping is not supported when building without process_wrapper:
1201*d4726bddSHONG Yifan            https://github.com/bazelbuild/rules_rust/blob/8df4517d370b0c543a01ba38b63e1d5a4104b035/rust/private/rustc.bzl#L955
1202*d4726bddSHONG Yifan        """),
1203*d4726bddSHONG Yifan        default = 0,
1204*d4726bddSHONG Yifan        values = [0],
1205*d4726bddSHONG Yifan    )
1206*d4726bddSHONG Yifan
1207*d4726bddSHONG Yifan    return new_attr
1208*d4726bddSHONG Yifan
1209*d4726bddSHONG Yifan# Provides an internal rust_{binary,library} to use that we can use to build the process
1210*d4726bddSHONG Yifan# wrapper, this breaks the dependency of rust_* on the process wrapper by
1211*d4726bddSHONG Yifan# setting it to None, which the functions in rustc detect and build accordingly.
1212*d4726bddSHONG Yifanrust_binary_without_process_wrapper = rule(
1213*d4726bddSHONG Yifan    implementation = _rust_binary_impl,
1214*d4726bddSHONG Yifan    provides = COMMON_PROVIDERS,
1215*d4726bddSHONG Yifan    attrs = _common_attrs_for_binary_without_process_wrapper(_common_attrs.items() + _rust_binary_attrs.items() + {
1216*d4726bddSHONG Yifan        "platform": attr.label(
1217*d4726bddSHONG Yifan            doc = "Optional platform to transition the binary to.",
1218*d4726bddSHONG Yifan            default = None,
1219*d4726bddSHONG Yifan        ),
1220*d4726bddSHONG Yifan        "_allowlist_function_transition": attr.label(
1221*d4726bddSHONG Yifan            default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
1222*d4726bddSHONG Yifan        ),
1223*d4726bddSHONG Yifan    }.items()),
1224*d4726bddSHONG Yifan    executable = True,
1225*d4726bddSHONG Yifan    fragments = ["cpp"],
1226*d4726bddSHONG Yifan    cfg = _rust_binary_transition,
1227*d4726bddSHONG Yifan    toolchains = [
1228*d4726bddSHONG Yifan        str(Label("//rust:toolchain_type")),
1229*d4726bddSHONG Yifan        "@bazel_tools//tools/cpp:toolchain_type",
1230*d4726bddSHONG Yifan    ],
1231*d4726bddSHONG Yifan)
1232*d4726bddSHONG Yifan
1233*d4726bddSHONG Yifanrust_library_without_process_wrapper = rule(
1234*d4726bddSHONG Yifan    implementation = _rust_library_impl,
1235*d4726bddSHONG Yifan    provides = COMMON_PROVIDERS,
1236*d4726bddSHONG Yifan    attrs = dict(_common_attrs_for_binary_without_process_wrapper(_common_attrs).items()),
1237*d4726bddSHONG Yifan    fragments = ["cpp"],
1238*d4726bddSHONG Yifan    toolchains = [
1239*d4726bddSHONG Yifan        str(Label("//rust:toolchain_type")),
1240*d4726bddSHONG Yifan        "@bazel_tools//tools/cpp:toolchain_type",
1241*d4726bddSHONG Yifan    ],
1242*d4726bddSHONG Yifan)
1243*d4726bddSHONG Yifan
1244*d4726bddSHONG Yifandef _rust_test_transition_impl(settings, attr):
1245*d4726bddSHONG Yifan    return {
1246*d4726bddSHONG Yifan        "//command_line_option:platforms": str(attr.platform) if attr.platform else settings["//command_line_option:platforms"],
1247*d4726bddSHONG Yifan    }
1248*d4726bddSHONG Yifan
1249*d4726bddSHONG Yifan_rust_test_transition = transition(
1250*d4726bddSHONG Yifan    implementation = _rust_test_transition_impl,
1251*d4726bddSHONG Yifan    inputs = [
1252*d4726bddSHONG Yifan        "//command_line_option:platforms",
1253*d4726bddSHONG Yifan    ],
1254*d4726bddSHONG Yifan    outputs = [
1255*d4726bddSHONG Yifan        "//command_line_option:platforms",
1256*d4726bddSHONG Yifan    ],
1257*d4726bddSHONG Yifan)
1258*d4726bddSHONG Yifan
1259*d4726bddSHONG Yifanrust_test = rule(
1260*d4726bddSHONG Yifan    implementation = _rust_test_impl,
1261*d4726bddSHONG Yifan    provides = COMMON_PROVIDERS,
1262*d4726bddSHONG Yifan    attrs = dict(_common_attrs.items() + _rust_test_attrs.items() + {
1263*d4726bddSHONG Yifan        "platform": attr.label(
1264*d4726bddSHONG Yifan            doc = "Optional platform to transition the test to.",
1265*d4726bddSHONG Yifan            default = None,
1266*d4726bddSHONG Yifan        ),
1267*d4726bddSHONG Yifan        "_allowlist_function_transition": attr.label(
1268*d4726bddSHONG Yifan            default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
1269*d4726bddSHONG Yifan        ),
1270*d4726bddSHONG Yifan    }.items()),
1271*d4726bddSHONG Yifan    executable = True,
1272*d4726bddSHONG Yifan    fragments = ["cpp"],
1273*d4726bddSHONG Yifan    cfg = _rust_test_transition,
1274*d4726bddSHONG Yifan    test = True,
1275*d4726bddSHONG Yifan    toolchains = [
1276*d4726bddSHONG Yifan        str(Label("//rust:toolchain_type")),
1277*d4726bddSHONG Yifan        "@bazel_tools//tools/cpp:toolchain_type",
1278*d4726bddSHONG Yifan    ],
1279*d4726bddSHONG Yifan    doc = dedent("""\
1280*d4726bddSHONG Yifan        Builds a Rust test crate.
1281*d4726bddSHONG Yifan
1282*d4726bddSHONG Yifan        Examples:
1283*d4726bddSHONG Yifan
1284*d4726bddSHONG Yifan        Suppose you have the following directory structure for a Rust library crate \
1285*d4726bddSHONG Yifan        with unit test code in the library sources:
1286*d4726bddSHONG Yifan
1287*d4726bddSHONG Yifan        ```output
1288*d4726bddSHONG Yifan        [workspace]/
1289*d4726bddSHONG Yifan            WORKSPACE
1290*d4726bddSHONG Yifan            hello_lib/
1291*d4726bddSHONG Yifan                BUILD
1292*d4726bddSHONG Yifan                src/
1293*d4726bddSHONG Yifan                    lib.rs
1294*d4726bddSHONG Yifan        ```
1295*d4726bddSHONG Yifan
1296*d4726bddSHONG Yifan        `hello_lib/src/lib.rs`:
1297*d4726bddSHONG Yifan        ```rust
1298*d4726bddSHONG Yifan        pub struct Greeter {
1299*d4726bddSHONG Yifan            greeting: String,
1300*d4726bddSHONG Yifan        }
1301*d4726bddSHONG Yifan
1302*d4726bddSHONG Yifan        impl Greeter {
1303*d4726bddSHONG Yifan            pub fn new(greeting: &str) -> Greeter {
1304*d4726bddSHONG Yifan                Greeter { greeting: greeting.to_string(), }
1305*d4726bddSHONG Yifan            }
1306*d4726bddSHONG Yifan
1307*d4726bddSHONG Yifan            pub fn greet(&self, thing: &str) -> String {
1308*d4726bddSHONG Yifan                format!("{} {}", &self.greeting, thing)
1309*d4726bddSHONG Yifan            }
1310*d4726bddSHONG Yifan        }
1311*d4726bddSHONG Yifan
1312*d4726bddSHONG Yifan        #[cfg(test)]
1313*d4726bddSHONG Yifan        mod test {
1314*d4726bddSHONG Yifan            use super::Greeter;
1315*d4726bddSHONG Yifan
1316*d4726bddSHONG Yifan            #[test]
1317*d4726bddSHONG Yifan            fn test_greeting() {
1318*d4726bddSHONG Yifan                let hello = Greeter::new("Hi");
1319*d4726bddSHONG Yifan                assert_eq!("Hi Rust", hello.greet("Rust"));
1320*d4726bddSHONG Yifan            }
1321*d4726bddSHONG Yifan        }
1322*d4726bddSHONG Yifan        ```
1323*d4726bddSHONG Yifan
1324*d4726bddSHONG Yifan        To build and run the tests, simply add a `rust_test` rule with no `srcs`
1325*d4726bddSHONG Yifan        and only depends on the `hello_lib` `rust_library` target via the
1326*d4726bddSHONG Yifan        `crate` attribute:
1327*d4726bddSHONG Yifan
1328*d4726bddSHONG Yifan        `hello_lib/BUILD`:
1329*d4726bddSHONG Yifan        ```python
1330*d4726bddSHONG Yifan        load("@rules_rust//rust:defs.bzl", "rust_library", "rust_test")
1331*d4726bddSHONG Yifan
1332*d4726bddSHONG Yifan        rust_library(
1333*d4726bddSHONG Yifan            name = "hello_lib",
1334*d4726bddSHONG Yifan            srcs = ["src/lib.rs"],
1335*d4726bddSHONG Yifan        )
1336*d4726bddSHONG Yifan
1337*d4726bddSHONG Yifan        rust_test(
1338*d4726bddSHONG Yifan            name = "hello_lib_test",
1339*d4726bddSHONG Yifan            crate = ":hello_lib",
1340*d4726bddSHONG Yifan            # You may add other deps that are specific to the test configuration
1341*d4726bddSHONG Yifan            deps = ["//some/dev/dep"],
1342*d4726bddSHONG Yifan        )
1343*d4726bddSHONG Yifan        ```
1344*d4726bddSHONG Yifan
1345*d4726bddSHONG Yifan        Run the test with `bazel test //hello_lib:hello_lib_test`. The crate
1346*d4726bddSHONG Yifan        will be built using the same crate name as the underlying ":hello_lib"
1347*d4726bddSHONG Yifan        crate.
1348*d4726bddSHONG Yifan
1349*d4726bddSHONG Yifan        ### Example: `test` directory
1350*d4726bddSHONG Yifan
1351*d4726bddSHONG Yifan        Integration tests that live in the [`tests` directory][int-tests], they are \
1352*d4726bddSHONG Yifan        essentially built as separate crates. Suppose you have the following directory \
1353*d4726bddSHONG Yifan        structure where `greeting.rs` is an integration test for the `hello_lib` \
1354*d4726bddSHONG Yifan        library crate:
1355*d4726bddSHONG Yifan
1356*d4726bddSHONG Yifan        [int-tests]: http://doc.rust-lang.org/book/testing.html#the-tests-directory
1357*d4726bddSHONG Yifan
1358*d4726bddSHONG Yifan        ```output
1359*d4726bddSHONG Yifan        [workspace]/
1360*d4726bddSHONG Yifan            WORKSPACE
1361*d4726bddSHONG Yifan            hello_lib/
1362*d4726bddSHONG Yifan                BUILD
1363*d4726bddSHONG Yifan                src/
1364*d4726bddSHONG Yifan                    lib.rs
1365*d4726bddSHONG Yifan                tests/
1366*d4726bddSHONG Yifan                    greeting.rs
1367*d4726bddSHONG Yifan        ```
1368*d4726bddSHONG Yifan
1369*d4726bddSHONG Yifan        `hello_lib/tests/greeting.rs`:
1370*d4726bddSHONG Yifan        ```rust
1371*d4726bddSHONG Yifan        extern crate hello_lib;
1372*d4726bddSHONG Yifan
1373*d4726bddSHONG Yifan        use hello_lib;
1374*d4726bddSHONG Yifan
1375*d4726bddSHONG Yifan        #[test]
1376*d4726bddSHONG Yifan        fn test_greeting() {
1377*d4726bddSHONG Yifan            let hello = greeter::Greeter::new("Hello");
1378*d4726bddSHONG Yifan            assert_eq!("Hello world", hello.greeting("world"));
1379*d4726bddSHONG Yifan        }
1380*d4726bddSHONG Yifan        ```
1381*d4726bddSHONG Yifan
1382*d4726bddSHONG Yifan        To build the `greeting.rs` integration test, simply add a `rust_test` target
1383*d4726bddSHONG Yifan        with `greeting.rs` in `srcs` and a dependency on the `hello_lib` target:
1384*d4726bddSHONG Yifan
1385*d4726bddSHONG Yifan        `hello_lib/BUILD`:
1386*d4726bddSHONG Yifan        ```python
1387*d4726bddSHONG Yifan        load("@rules_rust//rust:defs.bzl", "rust_library", "rust_test")
1388*d4726bddSHONG Yifan
1389*d4726bddSHONG Yifan        rust_library(
1390*d4726bddSHONG Yifan            name = "hello_lib",
1391*d4726bddSHONG Yifan            srcs = ["src/lib.rs"],
1392*d4726bddSHONG Yifan        )
1393*d4726bddSHONG Yifan
1394*d4726bddSHONG Yifan        rust_test(
1395*d4726bddSHONG Yifan            name = "greeting_test",
1396*d4726bddSHONG Yifan            srcs = ["tests/greeting.rs"],
1397*d4726bddSHONG Yifan            deps = [":hello_lib"],
1398*d4726bddSHONG Yifan        )
1399*d4726bddSHONG Yifan        ```
1400*d4726bddSHONG Yifan
1401*d4726bddSHONG Yifan        Run the test with `bazel test //hello_lib:greeting_test`.
1402*d4726bddSHONG Yifan"""),
1403*d4726bddSHONG Yifan)
1404*d4726bddSHONG Yifan
1405*d4726bddSHONG Yifandef rust_test_suite(name, srcs, shared_srcs = [], **kwargs):
1406*d4726bddSHONG Yifan    """A rule for creating a test suite for a set of `rust_test` targets.
1407*d4726bddSHONG Yifan
1408*d4726bddSHONG Yifan    This rule can be used for setting up typical rust [integration tests][it]. Given the following
1409*d4726bddSHONG Yifan    directory structure:
1410*d4726bddSHONG Yifan
1411*d4726bddSHONG Yifan    ```text
1412*d4726bddSHONG Yifan    [crate]/
1413*d4726bddSHONG Yifan        BUILD.bazel
1414*d4726bddSHONG Yifan        src/
1415*d4726bddSHONG Yifan            lib.rs
1416*d4726bddSHONG Yifan            main.rs
1417*d4726bddSHONG Yifan        tests/
1418*d4726bddSHONG Yifan            integrated_test_a.rs
1419*d4726bddSHONG Yifan            integrated_test_b.rs
1420*d4726bddSHONG Yifan            integrated_test_c.rs
1421*d4726bddSHONG Yifan            patterns/
1422*d4726bddSHONG Yifan                fibonacci_test.rs
1423*d4726bddSHONG Yifan            helpers/
1424*d4726bddSHONG Yifan                mod.rs
1425*d4726bddSHONG Yifan    ```
1426*d4726bddSHONG Yifan
1427*d4726bddSHONG Yifan    The rule can be used to generate [rust_test](#rust_test) targets for each source file under `tests`
1428*d4726bddSHONG Yifan    and a [test_suite][ts] which encapsulates all tests.
1429*d4726bddSHONG Yifan
1430*d4726bddSHONG Yifan    ```python
1431*d4726bddSHONG Yifan    load("//rust:defs.bzl", "rust_binary", "rust_library", "rust_test_suite")
1432*d4726bddSHONG Yifan
1433*d4726bddSHONG Yifan    rust_library(
1434*d4726bddSHONG Yifan        name = "math_lib",
1435*d4726bddSHONG Yifan        srcs = ["src/lib.rs"],
1436*d4726bddSHONG Yifan    )
1437*d4726bddSHONG Yifan
1438*d4726bddSHONG Yifan    rust_binary(
1439*d4726bddSHONG Yifan        name = "math_bin",
1440*d4726bddSHONG Yifan        srcs = ["src/main.rs"],
1441*d4726bddSHONG Yifan    )
1442*d4726bddSHONG Yifan
1443*d4726bddSHONG Yifan    rust_test_suite(
1444*d4726bddSHONG Yifan        name = "integrated_tests_suite",
1445*d4726bddSHONG Yifan        srcs = glob(["tests/**"]),
1446*d4726bddSHONG Yifan        shared_srcs=glob(["tests/helpers/**"]),
1447*d4726bddSHONG Yifan        deps = [":math_lib"],
1448*d4726bddSHONG Yifan    )
1449*d4726bddSHONG Yifan    ```
1450*d4726bddSHONG Yifan
1451*d4726bddSHONG Yifan    [it]: https://doc.rust-lang.org/rust-by-example/testing/integration_testing.html
1452*d4726bddSHONG Yifan    [ts]: https://docs.bazel.build/versions/master/be/general.html#test_suite
1453*d4726bddSHONG Yifan
1454*d4726bddSHONG Yifan    Args:
1455*d4726bddSHONG Yifan        name (str): The name of the `test_suite`.
1456*d4726bddSHONG Yifan        srcs (list): All test sources, typically `glob(["tests/**/*.rs"])`.
1457*d4726bddSHONG Yifan        shared_srcs (list): Optional argument for sources shared among tests, typically helper functions.
1458*d4726bddSHONG Yifan        **kwargs (dict): Additional keyword arguments for the underyling [rust_test](#rust_test) targets. The
1459*d4726bddSHONG Yifan            `tags` argument is also passed to the generated `test_suite` target.
1460*d4726bddSHONG Yifan    """
1461*d4726bddSHONG Yifan    tests = []
1462*d4726bddSHONG Yifan
1463*d4726bddSHONG Yifan    for src in srcs:
1464*d4726bddSHONG Yifan        if not src.endswith(".rs"):
1465*d4726bddSHONG Yifan            fail("srcs should have `.rs` extensions")
1466*d4726bddSHONG Yifan
1467*d4726bddSHONG Yifan        if src in shared_srcs:
1468*d4726bddSHONG Yifan            continue
1469*d4726bddSHONG Yifan
1470*d4726bddSHONG Yifan        # Prefixed with `name` to allow parameterization with macros
1471*d4726bddSHONG Yifan        # The test name should not end with `.rs`
1472*d4726bddSHONG Yifan        test_name = name + "_" + src[:-3]
1473*d4726bddSHONG Yifan        rust_test(
1474*d4726bddSHONG Yifan            name = test_name,
1475*d4726bddSHONG Yifan            crate_root = src,
1476*d4726bddSHONG Yifan            srcs = [src] + shared_srcs,
1477*d4726bddSHONG Yifan            **kwargs
1478*d4726bddSHONG Yifan        )
1479*d4726bddSHONG Yifan        tests.append(test_name)
1480*d4726bddSHONG Yifan
1481*d4726bddSHONG Yifan    native.test_suite(
1482*d4726bddSHONG Yifan        name = name,
1483*d4726bddSHONG Yifan        tests = tests,
1484*d4726bddSHONG Yifan        tags = kwargs.get("tags", None),
1485*d4726bddSHONG Yifan    )
1486*d4726bddSHONG Yifan
1487*d4726bddSHONG Yifanrust_library_group = rule(
1488*d4726bddSHONG Yifan    implementation = _rust_library_group_impl,
1489*d4726bddSHONG Yifan    provides = [rust_common.crate_group_info],
1490*d4726bddSHONG Yifan    attrs = {
1491*d4726bddSHONG Yifan        "deps": attr.label_list(
1492*d4726bddSHONG Yifan            doc = "Other dependencies to forward through this crate group.",
1493*d4726bddSHONG Yifan            providers = [[rust_common.crate_group_info], [rust_common.crate_info]],
1494*d4726bddSHONG Yifan        ),
1495*d4726bddSHONG Yifan    },
1496*d4726bddSHONG Yifan    doc = dedent("""\
1497*d4726bddSHONG Yifan        Functions as an alias for a set of dependencies.
1498*d4726bddSHONG Yifan
1499*d4726bddSHONG Yifan        Specifically, the following are equivalent:
1500*d4726bddSHONG Yifan
1501*d4726bddSHONG Yifan        ```starlark
1502*d4726bddSHONG Yifan        rust_library_group(
1503*d4726bddSHONG Yifan            name = "crate_group",
1504*d4726bddSHONG Yifan            deps = [
1505*d4726bddSHONG Yifan                ":crate1",
1506*d4726bddSHONG Yifan                ":crate2",
1507*d4726bddSHONG Yifan            ],
1508*d4726bddSHONG Yifan        )
1509*d4726bddSHONG Yifan
1510*d4726bddSHONG Yifan        rust_library(
1511*d4726bddSHONG Yifan            name = "foobar",
1512*d4726bddSHONG Yifan            deps = [":crate_group"],
1513*d4726bddSHONG Yifan            ...
1514*d4726bddSHONG Yifan        )
1515*d4726bddSHONG Yifan        ```
1516*d4726bddSHONG Yifan
1517*d4726bddSHONG Yifan        and
1518*d4726bddSHONG Yifan
1519*d4726bddSHONG Yifan        ```starlark
1520*d4726bddSHONG Yifan        rust_library(
1521*d4726bddSHONG Yifan            name = "foobar",
1522*d4726bddSHONG Yifan            deps = [
1523*d4726bddSHONG Yifan                ":crate1",
1524*d4726bddSHONG Yifan                ":crate2",
1525*d4726bddSHONG Yifan            ],
1526*d4726bddSHONG Yifan            ...
1527*d4726bddSHONG Yifan        )
1528*d4726bddSHONG Yifan        ```
1529*d4726bddSHONG Yifan    """),
1530*d4726bddSHONG Yifan)
1531