Squashed 'third_party/rules_rust/' changes from bf59038cac..078c6908fc
078c6908fc add cc_common.link support for rust_library and rust_test (#1490)
c07aef0287 Skip supplying rpaths on Fuchsia (#1511)
6ee7c80bdb Propagate rustc_env{,_files} from rust_test.crate (#1443)
1cd0788d2a Apply get_lib_name correctly to the C++ runtime libraries (#1508)
90808f0dc4 Minor cleanup to documentation (#1505)
735640f2df Enable rust-analyzer tests on windows. (#1506)
0f34573166 Updated rules_rust to version 0.9.0 (#1503)
9b61b49934 Promoted crate_universe to non-experimental (#1504)
76360dd354 Implement rules archive release artifact in github action. (#1501)
4e5fac5980 Do not pass `--Clink-arg=-l` for libstd and libtest (#1500)
6c38934636 pipelining: add the ability to disable pipelining for a single rule. (#1499)
867fc37c17 rules_rust: enable pipelined compilation. (#1275)
c97f255dfe Delete deprecated targets (#1496)
43b42884a7 Updated examples to use crate_universe (#1494)
0ffde973e8 Updated `//util/import` to use crate_universe (#1492)
83a03ab03e Updated proto rules to fetch dependencies using crate_universe (#1491)
67e204ff22 fix: `rust_doc_test` failure to find params file (#1418)
0fc834bdfa Updated all toolchain_type definitions to be named `toolchain_type`. (#1479)
3be056a7a3 toolchain files: ensure test depends on std (#1486)
228ebfa6eb Updated rules_rust to version `0.8.1`. (#1484)
685dfda497 Fixed use of rust-analyzer with rust_static_library and rust_shared_library (#1482)
2d7f94543f Fix rust-analyzer being unable to find rust sysroot sources. (#1483)
81a77acde2 Updated rules_rust to version `0.8.0`. (#1472)
caad908848 Give useful error on missing workspace manifest (#1475)
0e86b9dd30 Added `rust_analyzer_toolchain` and repository rules for creating one (#1455)
838e4ea828 Update docs on lockfiles (#1477)
fce1222628 Fix typo in function name (#1478)
1929bfbc3e Added Rust version 1.62.1 (#1476)
9a8e878d3d Fix `rust_binary_without_process_wrapper` build with `--stamp` (#1473)
25baabc205 Updated bindgen version (#1470)
8c9d5c7051 Updated rust-analyzer generator to use clap (#1471)
6d8009dbc8 Update `//bindgen` to use `crate_universe` (#1440)
67c97d44ff Updated `tools/rust_analyzer` to use `crate_universe`. (#1448)
6c285eb28e Updated `wasm_bindgen` rules dependencies. (#1464)
82a437cc17 Fixed crate_universe lockfile checks for crates_repository rule (#1468)
e83d5f3c77 Limit coverage to requested files (#1467)
daff327ea7 Stamp only binaries by default (#1452)
adc80a301d Cleanup crate_universe dependency macros (#1460)
824b121acc Updated header of crate_universe generated files to include a regen command (#1461)
d44e1d8363 feat: add `rustc_flags` attr to `rust_doc` rule (#1458)
6b54feb0ff add a way to distinguish proc-macro deps (#1420)
6070b3c9f4 Fixed missing items in distro artifact (#1450)
1e83e74159 do not add proc-macro deps to transitive_link_search_paths (#1424)
ced94dec1b Fix @libgit2 (#1457)
03d1d5e4ac Add extra_rustc_flag and extra_exec_rustc_flag (#1413)
711adc7985 crate_universe: shorten `crate_universe_crate_index` to `cui` (#1454)
8cb9247f18 Replaced small genrules with uses of bazel_skylib (#1451)
38e841aece Upgrade stardoc (#1444)
674762f66a Updated toolchain repository rules to represent one toolchain per repo (#1442)
b22a6e1416 Re-enable disabled windows jobs in CI (#1353)
2fb94c48fd docs: Update homepage to use latest version (#1441)
389c58fcb1 Updated rules_rust to version `0.7.0`. (#1436)
60f26d49d8 exclude `BUILD` and `WORKSPACE` files from generated crate_universe targets (#1437)
26344d4cd7 Have rust_test put its compilation outputs in a subdirectory (#1434)
8b0f644122 Updated crate_universe version to `0.4.0`. (#1435)
adf92b1534 update crate_universe `--repin` args to not require values. (#1433)
da75146d0a Do not attempt to instrument rust code for coverage if rust_toolchain.llvm-cov is None (#1432)
bde2c36821 Added Rust 1.62.0 (#1427)
7056f22bd0 Fixed crate_universe not finding target library names for "rlib"s (#1429)
3d65214d23 crate_universe support for individually updating packages. (#1426)
5a9d999db9 Updated `attr.label` attribute defaults to use `Label` constructor (#1422)
52fc70145a Added `TemplateVariableInfo` to `rust_toolchain`. (#1416)
7465c1aa29 Add test coverage support (#1324)
c5c3603da6 Bump the min supported bazel version (#1414)
937bdc9d05 Add a `cargo_dep_env` rule for setting build.rs environment variables (#1415)
91466fc0d1 Updated `rules_rust` version to `0.6.0`. (#1397)
97264b79d5 Update wasm_bindgen to use crate universe. (#1398)
d3197a65c5 Updated crate_universe version (to `0.3.0`) and dependencies (#1409)
a15e67d666 Deleted "extra workspace member" functionality from crate_universe (#1406)
5910a7585a Use a vec, not set for rustc_flags for crate_universe annotations (#1404)
3aa88ab067 Deleted deprecated `rust_analyzer` rule. (#1405)
7adf7210d0 cargo: Fix handling of relative sysroots (#1371)
57607239ec Enable rustfmt CI for Windows. (#1403)
30e68b49be Added more "ignore" tags to rustfmt and clippy rules. (#1400)
53ad14eead Added support for vendoring external manifests (#1381)
ff243c6ef0 Reorganized rustfmt source tree (#1399)
94e0044afe Refactored the Rustfmt tool to work cross-platform (#1375)
8fca438124 Ran clang-format on all C++ sources (#1396)
e765719e29 Added TemplateVariableInfo to rust_toolchain (#1377)
81590f4b6a Fixed Clippy bug with `--@rules_rust//:clippy_flags`. (#1392)
d77b9f7c6a Use `target_compatible_with` to make `macos` with `Xcode` happy (#1391)
ec27dbe310 Added comments to internal function (#1378)
a9dd2f9200 Removed deprecated file (#1380)
16175c881c Renamed toolchain files targets (#1376)
c7cb5bd7a8 Support crates that have mix of generated and nongenerated inputs (#1340)
521e649ff4 Avoid using common substrings as encodings. (#1370)
28ac6b133d Use a more compact encoding in the `import` macro. (#1365)
3a099bfa97 Fix incorrect assertion in test_env_launcher test (#1368)
4661093fb1 Use target instead of rule in rust_register_toolchains edition docs (#1366)
652f2802e3 Add `env` attribute to `rust_toolchain`. (#1363)
9520f37f1e Update rules_perl in examples (#1364)
1b451c301e Add armv7-linux-androideabi tier 2 triple (#1362)
0265c293f1 Ensure crate_features to be included correctly in rust_project.json (#1356)
121d65fe6a Updated `rules_rust` version to `0.5.0` (#1360)
aca4ec1a0f crate_universe: fix typo (#1314)
69ca2611c5 Don't leak native dependencies of proc_macro (#1359)
4c7f08b8b9 Fixed missing docs (#1358)
e48bec94de feat: build script toolchains annotations (#1344)
ffb946f4b7 Ensure memchr is linked after libobject (#1349)
edca1d8042 Add developing notes for crate_universe (#1351)
120f911d2f Updated rust_bindgen dependencies API (#1354)
42c4528a5f Added Rust 1.61.0 (#1347)
c05e0c6ab1 Fixed fetch_shas script to correctly include .gz and .xz extensions (#1355)
9770b0dd75 Update apple_support (#1346)
87eb807e67 Added support for Rust 1.61.0 to crate_universe (#1348)
84c1d42128 Temporarily disable windows job in CI. (#1352)
421d30e4ff Remove unnecessary `crate_name` usage in `rust_test_suite`. (#1345)
10185339dd Build `rust_test` targets with `crate` using the same crate name as the underlying library target. (#1332)
0049ce3884 Add support for riscv32 targets (#1323)
3aa6de3c63 remove experimental_use_whole_archive_for_native_deps (#1338)
a066bfed46 Replace slashes with underscores in default crate names. (#1336)
1b91e592d5 Revert "Replace slashes with underscores in default crate names. (#1334)" (#1335)
51f8e302e9 "sandwich" rlibs between native deps in linker order (#1333)
df354904a1 Replace slashes with underscores in default crate names. (#1334)
21eed19188 Bump version to 0.4.0 (#1329)
d3d9abac4d Support . workspace member (#1326)
fccaae3055 Error calling `all_crate_deps` without `Cargo.toml` (#1327)
d7c532cb78 Updated wasm_bindgen dependencies API (#1313)
fb4d5546ea Updated wasm_bindgen rules to only require core `rules_nodejs` rules (#1311)
1590670ae1 Prevents running of clippy on bindgen targets (#1320)
73d0164a34 Add support for aarch64-apple-ios-sim (#1304)
61eee54c73 Add bazel-* directories in cargo_manifest_dir/external_crate to gitignore (#1279)
42f78f25e1 crate_universe: Improved documentation (#1305)
bddc4bd94a Silence warnings for example/test dependencies (#1312)
b04fe3b21f Use tinyjson from crates.io instead of github.com. (#1309)
1cab691d14 Remove doc about STATIC_RUST_URL env var. (#1306)
d86e06a884 Don't propagate non-shared transitive linker inputs from `rust_static|shared_library` (#1299)
5abeb93333 Don't emit `CrateInfo` from `rust_static_library` and `rust_shared_library` (#1298)
0175a1b7aa fix for using a nightly channel after https://github.com/bazelbuild/rules_rust/commit/841fc6fb82141576d91aecb1d3f2656d58b0ab71 (#1302)
e07881fa22 Updated crate_universe docs (#1301)
c63ad973f1 rustc: fix a conditional (#1300)
a6f332fcbe Use __EXEC_ROOT__ paths for genfiles in rust_analyzer aspect (#1293)
97de47df51 Remove 'triple' field from triple struct in favor of 'str' (#1297)
58627f59eb Make get_host_triple public to get a triple from Bazel's repository_ctx (#1289)
612f4362bc Updated `rules_rust` version to `0.3.1` (#1296)
26fa5a15de Fixed build issues in release artifact (#1295)
48bb32f817 crate_universe: Added tests for serialized config files. (#1291)
841fc6fb82 Enable xz archives (#1284)
f7cb22efa6 feat(#514): pass extra flags to clippy (#1264)
e9f8b48711 Updated `rules_rust` version to `0.3.0` (#1288)
c900e1c66c Revert "Add workaround for arm vs armv7 android issue (#1259)" (#1290)
01ebef2fb9 Remove DEFAULT_RUST_EDITION (#1256)
03a70796ab Outside of examples, fill in all `edition` attrs (#1257)
207ee4fbcf feat: support extra deps in rust_doc_test (#1286)
4e7d114a8e Fix typo in render config doc (#1281)
db17f291d3 Fix crate annotation anchor (#1282)
fdb6851a92 Fix target name in `rust_test` example. (#1280)
4fb6e40147 Don't leak additive build file content across crates (#1278)
965044ae2b Remove `rust_test` example which doesn't build. (#1267)
f6e7e0a93f add a stopgap experimental_use_whole_archive_for_native_deps attribute (#1269)
34fd46756a process_wrapper: add support for terminating rustc after it emits rmeta. (#1207)
b778fca0ac crate_universe: propagate build_script_tools (#1266)
f6f27a8734 Add workaround for arm vs armv7 android issue (#1259)
c3f56c2d50 Add the BUILD.bazel file to the wasm_bindgen distro filegroup target (#1246) (#1261)
1f2e6231de Set edition for process_wrapper and cargo_build_script_runner (#1254)
55790492ac Updated Rust to 1.60.0 (#1247)
b594f90f17 Workaround for issue in linux Cargo binaries (#1252)
8f0dd9042e rust_test_suite: ensure crate names are valid (#1248)
4144ddeb9c Updating `rules_rust` version to `0.2.1` (#1243)
65cad76a52 Fixed proto package in release artifact (#1241)
4d8de6e361 Updated repository pin in the docs (#1240)
e5a3970754 Updating `rules_rust` version to `0.2.0` (#1235)
d061bf640e Updated `crate_universe` version to `0.2.0` (#1239)
c0505da0d2 Replace `rust_repositories` with `rust_register_toolchains` in docs (#1237)
145ad7609f Fixed `crates_repository` deleting `.cargo/config.toml` files. (#1227)
20066b05e2 fix: distribute `//tools/rust_analyzer` (#1234)
b58ce89603 Enabled `rust_doc_test` for `crate_universe` (#1232)
d2e2470cbf Fix some unit tests to run from another workspace (#1226)
b03aee039a Fixed `crate_universe` clippy defects (#1228)
41b39f0c99 add bots using lld (and examples with clang as a drive by) (#1221)
84e98e4d2f don't emit --codegen={metadata,extra-filename} for rust_static_library and rust_shared_library (#1222)
e48c834a40 Renamed `crate_index` repository to `crate_universe_crate_index` (#1217)
99b4b2574f fix use of stamping in rust_binary_without_process_wrapper (#1218)
8df4517d37 Add NUM_JOBS env var to cargo build scripts (#1216)
628e85e70f Restrucutred `crate_universe` dependency macros (#1208)
e3d67a0a10 Updated docs to guide users to using releases (#1213)
fd912e644c Updated crate_universe docs. (#1212)
cde4c0826c Delete deprecated `rules` targets (#1210)
26e01c8386 cache the release archive in release actions (#1201)
3205c9d846 Updated crate_universe setup guide (#1202)
c078494678 Don't leak deps from rust_proc_macro (#1206)
7c865ffeb1 Build `_import_macro_dep` in `exec` mode (#1203)
635da93206 Updating `rules_rust` version to `0.1.0`. (#1198)
6c797c9070 disable advanced features of C++ rules (#1200)
86d47a1bba Tweak import macro bootstrap to trick rust analyzer aspect (#1179)
80d197205a Added release workflow (#1195)
cd44b3670a Added support for producing distribution archives (#1194)
a665447989 Traverse custom alias-like rules in the rust analyzer aspect (#1190)
4504983fa9 Add a test showing that rust_analayzer aspect traverses aliases (#1188)
297dd18215 Updated `crate_universe` to version `0.1.0`. (#1197)
0d9c7d5e1b Specify root target for rust_analyzer test (#1189)
4a0352fecd Updated `crate_universe` dependencies (#1196)
5126479254 Fixed crate_universe release tools (#1193)
e840400eb6 Remove last remains of use_process_wrapper flag. (#1192)
eb7db68d96 Fix iOS linker arguments (#1186)
de726a10c9 Create internal rust_binary rule instead of using transitions (#1187)
5e6ad9f638 Regenerated `cargo-raze` outputs with v0.15.0 (#1184)
980b662843 add static_library to get_preferred_artifact with pic (#1183)
97fd329540 Populate CFLAGS and CXXFLAGS when invoking build script. (#1081)
git-subtree-dir: third_party/rules_rust
git-subtree-split: 078c6908fc32c168b58e72cc3884dd8e30419e3a
Signed-off-by: Brian Silverman <bsilver16384@gmail.com>
Change-Id: Ifc218edaa852263bd76835ee7de44de07c08aec2
diff --git a/rust/private/BUILD.bazel b/rust/private/BUILD.bazel
index 929a39e..d175b28 100644
--- a/rust/private/BUILD.bazel
+++ b/rust/private/BUILD.bazel
@@ -1,5 +1,6 @@
load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
load("//rust/private:rust_analyzer.bzl", "rust_analyzer_detect_sysroot")
+load("//rust/private:rustfmt.bzl", "rustfmt_workspace_name")
load("//rust/private:stamp.bzl", "stamp_build_setting")
bzl_library(
@@ -9,16 +10,14 @@
deps = ["//rust/platform:bzl_lib"],
)
-alias(
- name = "rules",
- actual = ":bzl_lib",
- deprecation = "Please use the `@rules_rust//private:bzl_lib` target instead",
- visibility = ["//rust:__subpackages__"],
-)
-
stamp_build_setting(name = "stamp")
rust_analyzer_detect_sysroot(
name = "rust_analyzer_detect_sysroot",
visibility = ["//visibility:public"],
)
+
+rustfmt_workspace_name(
+ name = "rustfmt_workspace_name",
+ visibility = ["//visibility:public"],
+)
diff --git a/rust/private/clippy.bzl b/rust/private/clippy.bzl
index d1464e9..21f3965 100644
--- a/rust/private/clippy.bzl
+++ b/rust/private/clippy.bzl
@@ -29,6 +29,22 @@
"find_toolchain",
)
+ClippyFlagsInfo = provider(
+ doc = "Pass each value as an additional flag to clippy invocations",
+ fields = {"clippy_flags": "List[string] Flags to pass to clippy"},
+)
+
+def _clippy_flags_impl(ctx):
+ return ClippyFlagsInfo(clippy_flags = ctx.build_setting_value)
+
+clippy_flags = rule(
+ doc = (
+ "Add custom clippy flags from the command line with `--@rules_rust//:clippy_flags`."
+ ),
+ implementation = _clippy_flags_impl,
+ build_setting = config.string_list(flag = True),
+)
+
def _get_clippy_ready_crate_info(target, aspect_ctx):
"""Check that a target is suitable for clippy and extract the `CrateInfo` provider from it.
@@ -44,9 +60,16 @@
if target.label.workspace_root.startswith("external"):
return None
- # Targets annotated with `noclippy` will not be formatted
- if aspect_ctx and "noclippy" in aspect_ctx.rule.attr.tags:
- return None
+ # Targets with specific tags will not be formatted
+ if aspect_ctx:
+ ignore_tags = [
+ "noclippy",
+ "no-clippy",
+ ]
+
+ for tag in ignore_tags:
+ if tag in aspect_ctx.rule.attr.tags:
+ return None
# Obviously ignore any targets that don't contain `CrateInfo`
if rust_common.crate_info not in target:
@@ -106,12 +129,18 @@
if crate_info.is_test:
args.rustc_flags.add("--test")
+ clippy_flags = ctx.attr._clippy_flags[ClippyFlagsInfo].clippy_flags
+
# For remote execution purposes, the clippy_out file must be a sibling of crate_info.output
# or rustc may fail to create intermediate output files because the directory does not exist.
if ctx.attr._capture_output[CaptureClippyOutputInfo].capture_output:
clippy_out = ctx.actions.declare_file(ctx.label.name + ".clippy.out", sibling = crate_info.output)
args.process_wrapper_flags.add("--stderr-file", clippy_out.path)
+ if clippy_flags:
+ fail("""Combining @rules_rust//:clippy_flags with @rules_rust//:capture_clippy_output=true is currently not supported.
+See https://github.com/bazelbuild/rules_rust/pull/1264#discussion_r853241339 for more detail.""")
+
# If we are capturing the output, we want the build system to be able to keep going
# and consume the output. Some clippy lints are denials, so we treat them as warnings.
args.rustc_flags.add("-Wclippy::all")
@@ -121,18 +150,14 @@
clippy_out = ctx.actions.declare_file(ctx.label.name + ".clippy.ok", sibling = crate_info.output)
args.process_wrapper_flags.add("--touch-file", clippy_out.path)
- # Turn any warnings from clippy or rustc into an error, as otherwise
- # Bazel will consider the execution result of the aspect to be "success",
- # and Clippy won't be re-triggered unless the source file is modified.
- if "__bindgen" in ctx.rule.attr.tags:
- # bindgen-generated content is likely to trigger warnings, so
- # only fail on clippy warnings
- args.rustc_flags.add("-Dclippy::style")
- args.rustc_flags.add("-Dclippy::correctness")
- args.rustc_flags.add("-Dclippy::complexity")
- args.rustc_flags.add("-Dclippy::perf")
+ if clippy_flags:
+ args.rustc_flags.add_all(clippy_flags)
else:
- # fail on any warning
+ # The user didn't provide any clippy flags explicitly so we apply conservative defaults.
+
+ # Turn any warnings from clippy or rustc into an error, as otherwise
+ # Bazel will consider the execution result of the aspect to be "success",
+ # and Clippy won't be re-triggered unless the source file is modified.
args.rustc_flags.add("-Dwarnings")
# Upstream clippy requires one of these two filenames or it silently uses
@@ -177,6 +202,10 @@
),
default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"),
),
+ "_clippy_flags": attr.label(
+ doc = "Arguments to pass to clippy",
+ default = Label("//:clippy_flags"),
+ ),
"_config": attr.label(
doc = "The `clippy.toml` file used for configuration",
allow_single_file = True,
@@ -186,6 +215,7 @@
doc = "The desired `--error-format` flags for clippy",
default = "//:error_format",
),
+ "_extra_rustc_flag": attr.label(default = "//:extra_rustc_flag"),
"_extra_rustc_flags": attr.label(default = "//:extra_rustc_flags"),
"_process_wrapper": attr.label(
doc = "A process wrapper for running clippy on all platforms",
@@ -196,7 +226,7 @@
},
provides = [ClippyInfo],
toolchains = [
- str(Label("//rust:toolchain")),
+ str(Label("//rust:toolchain_type")),
"@bazel_tools//tools/cpp:toolchain_type",
],
incompatible_use_toolchain_transition = True,
diff --git a/rust/private/common.bzl b/rust/private/common.bzl
index 78a3011..025c4d7 100644
--- a/rust/private/common.bzl
+++ b/rust/private/common.bzl
@@ -23,16 +23,15 @@
In the Bazel lingo, `rust_common` gives the access to the Rust Sandwich API.
"""
-load(":providers.bzl", "CrateInfo", "DepInfo", "StdLibInfo")
+load(":providers.bzl", "CrateInfo", "DepInfo", "StdLibInfo", "TestCrateInfo")
-# These constants only represent the default value for attributes and macros
-# defiend in `rules_rust`. Like any attribute public attribute, they can be
-# overwritten by the user on the rules they're defiend on.
+# This constant only represents the default value for attributes and macros
+# defined in `rules_rust`. Like any attribute public attribute, it can be
+# overwritten by the user on the rules they're defined on.
#
# Note: Code in `.github/workflows/crate_universe.yaml` looks for this line, if
# you remove it or change its format, you will also need to update that code.
-DEFAULT_RUST_VERSION = "1.59.0"
-DEFAULT_RUST_EDITION = "2018"
+DEFAULT_RUST_VERSION = "1.62.1"
def _create_crate_info(**kwargs):
"""A constructor for a `CrateInfo` provider
@@ -48,6 +47,10 @@
"""
if not "wrapped_crate_type" in kwargs:
kwargs.update({"wrapped_crate_type": None})
+ if not "metadata" in kwargs:
+ kwargs.update({"metadata": None})
+ if not "rustc_env_files" in kwargs:
+ kwargs.update({"rustc_env_files": []})
return CrateInfo(**kwargs)
rust_common = struct(
@@ -55,6 +58,6 @@
crate_info = CrateInfo,
dep_info = DepInfo,
stdlib_info = StdLibInfo,
- default_edition = DEFAULT_RUST_EDITION,
+ test_crate_info = TestCrateInfo,
default_version = DEFAULT_RUST_VERSION,
)
diff --git a/rust/private/providers.bzl b/rust/private/providers.bzl
index b471ac0..464444b 100644
--- a/rust/private/providers.bzl
+++ b/rust/private/providers.bzl
@@ -22,12 +22,14 @@
"deps": "depset[DepVariantInfo]: This crate's (rust or cc) dependencies' providers.",
"edition": "str: The edition of this crate.",
"is_test": "bool: If the crate is being compiled in a test context",
+ "metadata": "File: The rmeta file produced for this crate. It is optional.",
"name": "str: The name of this crate.",
"output": "File: The output File that will be produced, depends on crate type.",
"owner": "Label: The label of the target that produced this CrateInfo",
"proc_macro_deps": "depset[DepVariantInfo]: This crate's rust proc_macro dependencies' providers.",
"root": "File: The source File entrypoint to this crate, eg. lib.rs",
"rustc_env": "Dict[String, String]: Additional `\"key\": \"value\"` environment variables to set for rustc.",
+ "rustc_env_files": "[File]: Files containing additional environment variables to set for rustc.",
"srcs": "depset[File]: All source Files that are part of the crate.",
"type": (
"str: The type of this crate " +
@@ -49,6 +51,7 @@
"transitive_build_infos": "depset[BuildInfo]",
"transitive_crate_outputs": "depset[File]: All transitive crate outputs.",
"transitive_crates": "depset[CrateInfo]",
+ "transitive_metadata_outputs": "depset[File]: All transitive metadata dependencies (.rmeta, for crates that provide them) and all transitive object dependencies (.rlib) for crates that don't provide metadata.",
"transitive_noncrates": "depset[LinkerInput]: All transitive dependencies that aren't crates.",
},
)
@@ -89,10 +92,12 @@
"between_core_and_std_files": "List[File]: `.a` files related to all modules except `adler`, `alloc`, `compiler_builtins`, `core`, and `std`.",
"core_files": "List[File]: `.a` files related to the `core` and `adler` modules",
"dot_a_files": "Depset[File]: Generated `.a` files",
+ "memchr_files": "Depset[File]: `.a` files associated with the `memchr` module.",
"self_contained_files": "List[File]: All `.o` files from the `self-contained` directory.",
"srcs": "List[Target]: All targets from the original `srcs` attribute.",
"std_files": "Depset[File]: `.a` files associated with the `std` module.",
"std_rlibs": "List[File]: All `.rlib` files",
+ "test_files": "Depset[File]: `.a` files associated with the `test` module.",
},
)
@@ -107,3 +112,15 @@
"output": "File with the clippy output.",
},
)
+
+TestCrateInfo = provider(
+ doc = "A wrapper around a CrateInfo. " +
+ "Certain rule types, like rust_static_library and rust_shared_library " +
+ "are not intended for consumption by other Rust targets, and should not " +
+ "provide a CrateInfo. However, one should still be able to write a rust_test " +
+ "for them. Thus, we create a CrateInfo, but do not advertise it as such, " +
+ "but rather through this provider, that rust_test understands.",
+ fields = {
+ "crate": "CrateInfo: The underlying CrateInfo of the dependency",
+ },
+)
diff --git a/rust/private/repository_utils.bzl b/rust/private/repository_utils.bzl
index b6c83aa..86d3f78 100644
--- a/rust/private/repository_utils.bzl
+++ b/rust/private/repository_utils.bzl
@@ -7,7 +7,6 @@
"system_to_dylib_ext",
"system_to_staticlib_ext",
"system_to_stdlib_linkflags",
- "triple_to_constraint_set",
"triple_to_system",
)
@@ -15,8 +14,6 @@
DEFAULT_STATIC_RUST_URL_TEMPLATES = ["https://static.rust-lang.org/dist/{}.tar.gz"]
_build_file_for_compiler_template = """\
-load("@rules_rust//rust:toolchain.bzl", "rust_toolchain")
-
filegroup(
name = "rustc",
srcs = ["bin/rustc{binary_ext}"],
@@ -46,7 +43,7 @@
"""
def BUILD_for_compiler(target_triple):
- """Emits a BUILD file the compiler `.tar.gz`.
+ """Emits a BUILD file the compiler archive.
Args:
target_triple (str): The triple of the target platform
@@ -63,8 +60,6 @@
)
_build_file_for_cargo_template = """\
-load("@rules_rust//rust:toolchain.bzl", "rust_toolchain")
-
filegroup(
name = "cargo",
srcs = ["bin/cargo{binary_ext}"],
@@ -72,7 +67,7 @@
)"""
def BUILD_for_cargo(target_triple):
- """Emits a BUILD file the cargo `.tar.gz`.
+ """Emits a BUILD file the cargo archive.
Args:
target_triple (str): The triple of the target platform
@@ -86,8 +81,6 @@
)
_build_file_for_rustfmt_template = """\
-load("@rules_rust//rust:toolchain.bzl", "rust_toolchain")
-
filegroup(
name = "rustfmt_bin",
srcs = ["bin/rustfmt{binary_ext}"],
@@ -102,7 +95,7 @@
"""
def BUILD_for_rustfmt(target_triple):
- """Emits a BUILD file the rustfmt `.tar.gz`.
+ """Emits a BUILD file the rustfmt archive.
Args:
target_triple (str): The triple of the target platform
@@ -116,8 +109,6 @@
)
_build_file_for_clippy_template = """\
-load("@rules_rust//rust:toolchain.bzl", "rust_toolchain")
-
filegroup(
name = "clippy_driver_bin",
srcs = ["bin/clippy-driver{binary_ext}"],
@@ -126,7 +117,7 @@
"""
def BUILD_for_clippy(target_triple):
- """Emits a BUILD file the clippy `.tar.gz`.
+ """Emits a BUILD file the clippy archive.
Args:
target_triple (str): The triple of the target platform
@@ -137,6 +128,35 @@
system = triple_to_system(target_triple)
return _build_file_for_clippy_template.format(binary_ext = system_to_binary_ext(system))
+_build_file_for_llvm_tools = """\
+filegroup(
+ name = "llvm_cov_bin",
+ srcs = ["lib/rustlib/{target_triple}/bin/llvm-cov{binary_ext}"],
+ visibility = ["//visibility:public"],
+)
+
+filegroup(
+ name = "llvm_profdata_bin",
+ srcs = ["lib/rustlib/{target_triple}/bin/llvm-profdata{binary_ext}"],
+ visibility = ["//visibility:public"],
+)
+"""
+
+def BUILD_for_llvm_tools(target_triple):
+ """Emits a BUILD file the llvm-tools binaries.
+
+ Args:
+ target_triple (str): The triple of the target platform
+
+ Returns:
+ str: The contents of a BUILD file
+ """
+ system = triple_to_system(target_triple)
+ return _build_file_for_llvm_tools.format(
+ binary_ext = system_to_binary_ext(system),
+ target_triple = target_triple,
+ )
+
_build_file_for_stdlib_template = """\
load("@rules_rust//rust:toolchain.bzl", "rust_stdlib_filegroup")
@@ -164,7 +184,7 @@
"""
def BUILD_for_stdlib(target_triple):
- """Emits a BUILD file the stdlib `.tar.gz`.
+ """Emits a BUILD file the stdlib archive.
Args:
target_triple (str): The triple of the target platform
@@ -181,16 +201,21 @@
)
_build_file_for_rust_toolchain_template = """\
+load("@rules_rust//rust:toolchain.bzl", "rust_toolchain")
+
rust_toolchain(
- name = "{toolchain_name}_impl",
+ name = "{toolchain_name}",
rust_doc = "@{workspace_name}//:rustdoc",
rust_std = "@{workspace_name}//:rust_std-{target_triple}",
rustc = "@{workspace_name}//:rustc",
rustfmt = {rustfmt_label},
cargo = "@{workspace_name}//:cargo",
clippy_driver = "@{workspace_name}//:clippy_driver_bin",
+ llvm_cov = {llvm_cov_label},
+ llvm_profdata = {llvm_profdata_label},
rustc_lib = "@{workspace_name}//:rustc_lib",
rustc_srcs = {rustc_srcs},
+ allocator_library = {allocator_library},
binary_ext = "{binary_ext}",
staticlib_ext = "{staticlib_ext}",
dylib_ext = "{dylib_ext}",
@@ -209,8 +234,10 @@
exec_triple,
target_triple,
include_rustc_srcs,
+ allocator_library,
default_edition,
include_rustfmt,
+ include_llvm_tools,
stdlib_linkflags = None):
"""Emits a toolchain declaration to match an existing compiler and stdlib.
@@ -220,8 +247,10 @@
exec_triple (str): The rust-style target that this compiler runs on
target_triple (str): The rust-style target triple of the tool
include_rustc_srcs (bool, optional): Whether to download rustc's src code. This is required in order to use rust-analyzer support. Defaults to False.
+ allocator_library (str, optional): Target that provides allocator functions when rust_library targets are embedded in a cc_binary.
default_edition (str): Default Rust edition.
include_rustfmt (bool): Whether rustfmt is present in the toolchain.
+ include_llvm_tools (bool): Whether llvm-tools are present in the toolchain.
stdlib_linkflags (list, optional): Overriden flags needed for linking to rust
stdlib, akin to BAZEL_LINKLIBS. Defaults to
None.
@@ -240,6 +269,14 @@
rustfmt_label = "None"
if include_rustfmt:
rustfmt_label = "\"@{workspace_name}//:rustfmt_bin\"".format(workspace_name = workspace_name)
+ llvm_cov_label = "None"
+ llvm_profdata_label = "None"
+ if include_llvm_tools:
+ llvm_cov_label = "\"@{workspace_name}//:llvm_cov_bin\"".format(workspace_name = workspace_name)
+ llvm_profdata_label = "\"@{workspace_name}//:llvm_profdata_bin\"".format(workspace_name = workspace_name)
+ allocator_library_label = "None"
+ if allocator_library:
+ allocator_library_label = "\"{allocator_library}\"".format(allocator_library = allocator_library)
return _build_file_for_rust_toolchain_template.format(
toolchain_name = name,
@@ -248,12 +285,15 @@
staticlib_ext = system_to_staticlib_ext(system),
dylib_ext = system_to_dylib_ext(system),
rustc_srcs = rustc_srcs,
+ allocator_library = allocator_library_label,
stdlib_linkflags = stdlib_linkflags,
system = system,
default_edition = default_edition,
exec_triple = exec_triple,
target_triple = target_triple,
rustfmt_label = rustfmt_label,
+ llvm_cov_label = llvm_cov_label,
+ llvm_profdata_label = llvm_profdata_label,
)
_build_file_for_toolchain_template = """\
@@ -261,17 +301,23 @@
name = "{name}",
exec_compatible_with = {exec_constraint_sets_serialized},
target_compatible_with = {target_constraint_sets_serialized},
- toolchain = "@{parent_workspace_name}//:{name}_impl",
- toolchain_type = "@rules_rust//rust:toolchain",
+ toolchain = "{toolchain}",
+ toolchain_type = "{toolchain_type}",
)
"""
-def BUILD_for_toolchain(name, parent_workspace_name, exec_triple, target_triple):
+def BUILD_for_toolchain(
+ name,
+ toolchain,
+ toolchain_type,
+ target_compatible_with,
+ exec_compatible_with):
return _build_file_for_toolchain_template.format(
name = name,
- exec_constraint_sets_serialized = serialized_constraint_set_from_triple(exec_triple),
- target_constraint_sets_serialized = serialized_constraint_set_from_triple(target_triple),
- parent_workspace_name = parent_workspace_name,
+ exec_constraint_sets_serialized = exec_compatible_with,
+ target_constraint_sets_serialized = target_compatible_with,
+ toolchain = toolchain,
+ toolchain_type = toolchain_type,
)
def load_rustfmt(ctx):
@@ -311,14 +357,56 @@
ctx,
iso_date = ctx.attr.iso_date,
target_triple = target_triple,
- tool_name = "rust",
- tool_subdirectories = ["rustc", "clippy-preview", "cargo"],
+ tool_name = "rustc",
+ tool_subdirectories = ["rustc"],
version = ctx.attr.version,
)
- compiler_build_file = BUILD_for_compiler(target_triple) + BUILD_for_clippy(target_triple) + BUILD_for_cargo(target_triple)
+ return BUILD_for_compiler(target_triple)
- return compiler_build_file
+def load_clippy(ctx):
+ """Loads Clippy and yields corresponding BUILD for it
+
+ Args:
+ ctx (repository_ctx): A repository_ctx.
+
+ Returns:
+ str: The BUILD file contents for Clippy
+ """
+
+ target_triple = ctx.attr.exec_triple
+ load_arbitrary_tool(
+ ctx,
+ iso_date = ctx.attr.iso_date,
+ target_triple = target_triple,
+ tool_name = "clippy",
+ tool_subdirectories = ["clippy-preview"],
+ version = ctx.attr.version,
+ )
+
+ return BUILD_for_clippy(target_triple)
+
+def load_cargo(ctx):
+ """Loads Cargo and yields corresponding BUILD for it
+
+ Args:
+ ctx (repository_ctx): A repository_ctx.
+
+ Returns:
+ str: The BUILD file contents for Cargo
+ """
+
+ target_triple = ctx.attr.exec_triple
+ load_arbitrary_tool(
+ ctx,
+ iso_date = ctx.attr.iso_date,
+ target_triple = target_triple,
+ tool_name = "cargo",
+ tool_subdirectories = ["cargo"],
+ version = ctx.attr.version,
+ )
+
+ return BUILD_for_cargo(target_triple)
def should_include_rustc_srcs(repository_ctx):
"""Determing whether or not to include rustc sources in the toolchain.
@@ -337,27 +425,24 @@
return getattr(repository_ctx.attr, "include_rustc_srcs", False)
-def load_rust_src(ctx):
+def load_rust_src(ctx, sha256 = ""):
"""Loads the rust source code. Used by the rust-analyzer rust-project.json generator.
Args:
ctx (ctx): A repository_ctx.
+ sha256 (str): The sha256 value for the `rust-src` artifact
"""
tool_suburl = produce_tool_suburl("rust-src", None, ctx.attr.version, ctx.attr.iso_date)
- static_rust = ctx.os.environ.get("STATIC_RUST_URL", "https://static.rust-lang.org")
- url = "{}/dist/{}.tar.gz".format(static_rust, tool_suburl)
+ url = ctx.attr.urls[0].format(tool_suburl)
tool_path = produce_tool_path("rust-src", None, ctx.attr.version)
- archive_path = tool_path + ".tar.gz"
- ctx.download(
+ archive_path = tool_path + _get_tool_extension(ctx)
+ sha256 = sha256 or getattr(ctx.attr, "sha256s", {}).get(archive_path) or FILE_KEY_TO_SHA.get(archive_path) or ""
+ ctx.download_and_extract(
url,
- output = archive_path,
- sha256 = ctx.attr.sha256s.get(tool_suburl) or FILE_KEY_TO_SHA.get(tool_suburl) or "",
- auth = _make_auth_dict(ctx, [url]),
- )
- ctx.extract(
- archive_path,
output = "lib/rustlib/src",
+ sha256 = sha256,
+ auth = _make_auth_dict(ctx, [url]),
stripPrefix = "{}/rust-src/lib/rustlib/src/rust".format(tool_path),
)
ctx.file(
@@ -370,6 +455,21 @@
)""",
)
+_build_file_for_rust_analyzer_toolchain_template = """\
+load("@rules_rust//rust:toolchain.bzl", "rust_analyzer_toolchain")
+
+rust_analyzer_toolchain(
+ name = "{name}",
+ rustc_srcs = "//lib/rustlib/src:rustc_srcs",
+ visibility = ["//visibility:public"],
+)
+"""
+
+def BUILD_for_rust_analyzer_toolchain(name):
+ return _build_file_for_rust_analyzer_toolchain_template.format(
+ name = name,
+ )
+
def load_rust_stdlib(ctx, target_triple):
"""Loads a rust standard library and yields corresponding BUILD for it
@@ -378,7 +478,7 @@
target_triple (str): The rust-style target triple of the tool
Returns:
- str: The BUILD file contents for this stdlib, and a toolchain decl to match
+ str: The BUILD file contents for this stdlib
"""
load_arbitrary_tool(
@@ -390,28 +490,7 @@
version = ctx.attr.version,
)
- toolchain_prefix = ctx.attr.toolchain_name_prefix or DEFAULT_TOOLCHAIN_NAME_PREFIX
- stdlib_build_file = BUILD_for_stdlib(target_triple)
-
- stdlib_linkflags = None
- if "BAZEL_RUST_STDLIB_LINKFLAGS" in ctx.os.environ:
- stdlib_linkflags = ctx.os.environ["BAZEL_RUST_STDLIB_LINKFLAGS"].split(":")
-
- toolchain_build_file = BUILD_for_rust_toolchain(
- name = "{toolchain_prefix}_{target_triple}".format(
- toolchain_prefix = toolchain_prefix,
- target_triple = target_triple,
- ),
- exec_triple = ctx.attr.exec_triple,
- include_rustc_srcs = should_include_rustc_srcs(ctx),
- target_triple = target_triple,
- stdlib_linkflags = stdlib_linkflags,
- workspace_name = ctx.attr.name,
- default_edition = ctx.attr.edition,
- include_rustfmt = not (not ctx.attr.rustfmt_version),
- )
-
- return stdlib_build_file + toolchain_build_file
+ return BUILD_for_stdlib(target_triple)
def load_rustc_dev_nightly(ctx, target_triple):
"""Loads the nightly rustc dev component
@@ -450,6 +529,8 @@
version = ctx.attr.version,
)
+ return BUILD_for_llvm_tools(target_triple)
+
def check_version_valid(version, iso_date, param_prefix = ""):
"""Verifies that the provided rust version and iso_date make sense.
@@ -465,21 +546,6 @@
if version in ("beta", "nightly") and not iso_date:
fail("{param_prefix}iso_date must be specified if version is 'beta' or 'nightly'".format(param_prefix = param_prefix))
-def serialized_constraint_set_from_triple(target_triple):
- """Returns a string representing a set of constraints
-
- Args:
- target_triple (str): The target triple of the constraint set
-
- Returns:
- str: Formatted string representing the serialized constraint
- """
- constraint_set = triple_to_constraint_set(target_triple)
- constraint_set_strs = []
- for constraint in constraint_set:
- constraint_set_strs.append("\"{}\"".format(constraint))
- return "[{}]".format(", ".join(constraint_set_strs))
-
def produce_tool_suburl(tool_name, target_triple, version, iso_date = None):
"""Produces a fully qualified Rust tool name for URL
@@ -520,10 +586,6 @@
- https://static.rust-lang.org/dist/channel-rust-beta.toml
- https://static.rust-lang.org/dist/channel-rust-nightly.toml
- The environment variable `STATIC_RUST_URL` can be used to replace the schema and hostname of
- the URLs used for fetching assets. `https://static.rust-lang.org/dist/channel-rust-stable.toml`
- becomes `${STATIC_RUST_URL}/dist/channel-rust-stable.toml`
-
Args:
ctx (repository_ctx): A repository_ctx (no attrs required).
tool_name (str): The name of the given tool per the archive naming.
@@ -551,32 +613,30 @@
tool_suburl = produce_tool_suburl(tool_name, target_triple, version, iso_date)
urls = []
- static_rust_url_from_env = ctx.os.environ.get("STATIC_RUST_URL")
- if static_rust_url_from_env:
- urls.append("{}/dist/{}.tar.gz".format(static_rust_url_from_env, tool_suburl))
-
for url in getattr(ctx.attr, "urls", DEFAULT_STATIC_RUST_URL_TEMPLATES):
new_url = url.format(tool_suburl)
if new_url not in urls:
urls.append(new_url)
tool_path = produce_tool_path(tool_name, target_triple, version)
- archive_path = "{}.tar.gz".format(tool_path)
- ctx.download(
- urls,
- output = archive_path,
- sha256 = getattr(ctx.attr, "sha256s", dict()).get(tool_suburl) or
- FILE_KEY_TO_SHA.get(tool_suburl) or
- sha256,
- auth = _make_auth_dict(ctx, urls),
- )
+ archive_path = tool_path + _get_tool_extension(ctx)
+ sha256 = getattr(ctx.attr, "sha256s", dict()).get(archive_path) or FILE_KEY_TO_SHA.get(archive_path) or sha256
+
for subdirectory in tool_subdirectories:
- ctx.extract(
- archive_path,
- output = "",
+ # As long as the sha256 value is consistent accross calls here the
+ # cost of downloading an artifact is negated as by Bazel's caching.
+ result = ctx.download_and_extract(
+ urls,
+ sha256 = sha256,
+ auth = _make_auth_dict(ctx, urls),
stripPrefix = "{}/{}".format(tool_path, subdirectory),
)
+ # In the event no sha256 was provided, set it to the value of the first
+ # downloaded item so subsequent downloads use a cached artifact.
+ if not sha256:
+ sha256 = result.sha256
+
def _make_auth_dict(ctx, urls):
auth = getattr(ctx.attr, "auth", {})
if not auth:
@@ -585,3 +645,12 @@
for url in urls:
ret[url] = auth
return ret
+
+def _get_tool_extension(ctx):
+ urls = getattr(ctx.attr, "urls", DEFAULT_STATIC_RUST_URL_TEMPLATES)
+ if urls[0][-7:] == ".tar.gz":
+ return ".tar.gz"
+ elif urls[0][-7:] == ".tar.xz":
+ return ".tar.xz"
+ else:
+ return ""
diff --git a/rust/private/rust.bzl b/rust/private/rust.bzl
index 58cc9c4..571813b 100644
--- a/rust/private/rust.bzl
+++ b/rust/private/rust.bzl
@@ -13,10 +13,12 @@
# limitations under the License.
# buildifier: disable=module-docstring
+load("@bazel_skylib//lib:paths.bzl", "paths")
load("//rust/private:common.bzl", "rust_common")
load("//rust/private:rustc.bzl", "rustc_compile_action")
load(
"//rust/private:utils.bzl",
+ "can_build_metadata",
"compute_crate_name",
"dedent",
"determine_output_hash",
@@ -25,7 +27,6 @@
"get_import_macro_deps",
"transform_deps",
)
-
# TODO(marco): Separate each rule into its own file.
def _assert_no_deprecated_attributes(_ctx):
@@ -105,26 +106,84 @@
extension = extension,
)
-def get_edition(attr, toolchain):
+def get_edition(attr, toolchain, label):
"""Returns the Rust edition from either the current rule's attirbutes or the current `rust_toolchain`
Args:
attr (struct): The current rule's attributes
toolchain (rust_toolchain): The `rust_toolchain` for the current target
+ label (Label): The label of the target being built
Returns:
str: The target Rust edition
"""
if getattr(attr, "edition"):
return attr.edition
+ elif not toolchain.default_edition:
+ fail("Attribute `edition` is required for {}.".format(label))
else:
return toolchain.default_edition
-def crate_root_src(attr, srcs, crate_type):
- """Finds the source file for the crate root.
+def _transform_sources(ctx, srcs, crate_root):
+ """Creates symlinks of the source files if needed.
+
+ Rustc assumes that the source files are located next to the crate root.
+ In case of a mix between generated and non-generated source files, this
+ we violate this assumption, as part of the sources will be located under
+ bazel-out/... . In order to allow for targets that contain both generated
+ and non-generated source files, we generate symlinks for all non-generated
+ files.
Args:
- attr (struct): The attributes of the current target
+ ctx (struct): The current rule's context.
+ srcs (List[File]): The sources listed in the `srcs` attribute
+ crate_root (File): The file specified in the `crate_root` attribute,
+ if it exists, otherwise None
+
+ Returns:
+ Tuple(List[File], File): The transformed srcs and crate_root
+ """
+ has_generated_sources = len([src for src in srcs if not src.is_source]) > 0
+
+ if not has_generated_sources:
+ return srcs, crate_root
+
+ generated_sources = []
+
+ generated_root = crate_root
+
+ if crate_root and (crate_root.is_source or crate_root.root.path != ctx.bin_dir.path):
+ generated_root = ctx.actions.declare_file(crate_root.basename)
+ ctx.actions.symlink(
+ output = generated_root,
+ target_file = crate_root,
+ progress_message = "Creating symlink to source file: {}".format(crate_root.path),
+ )
+ if generated_root:
+ generated_sources.append(generated_root)
+
+ for src in srcs:
+ # We took care of the crate root above.
+ if src == crate_root:
+ continue
+ if src.is_source or src.root.path != ctx.bin_dir.path:
+ src_symlink = ctx.actions.declare_file(src.basename)
+ ctx.actions.symlink(
+ output = src_symlink,
+ target_file = src,
+ progress_message = "Creating symlink to source file: {}".format(src.path),
+ )
+ generated_sources.append(src_symlink)
+ else:
+ generated_sources.append(src)
+
+ return generated_sources, generated_root
+
+def crate_root_src(name, srcs, crate_type):
+ """Determines the source file for the crate root, should it not be specified in `attr.crate_root`.
+
+ Args:
+ name (str): The name of the target.
srcs (list): A list of all sources for the target Crate.
crate_type (str): The type of this crate ("bin", "lib", "rlib", "cdylib", etc).
@@ -135,19 +194,13 @@
"""
default_crate_root_filename = "main.rs" if crate_type == "bin" else "lib.rs"
- crate_root = None
- if hasattr(attr, "crate_root"):
- if attr.crate_root:
- crate_root = attr.crate_root.files.to_list()[0]
-
+ crate_root = (
+ (srcs[0] if len(srcs) == 1 else None) or
+ _shortest_src_with_basename(srcs, default_crate_root_filename) or
+ _shortest_src_with_basename(srcs, name + ".rs")
+ )
if not crate_root:
- crate_root = (
- (srcs[0] if len(srcs) == 1 else None) or
- _shortest_src_with_basename(srcs, default_crate_root_filename) or
- _shortest_src_with_basename(srcs, attr.name + ".rs")
- )
- if not crate_root:
- file_names = [default_crate_root_filename, attr.name + ".rs"]
+ file_names = [default_crate_root_filename, name + ".rs"]
fail("No {} source file found.".format(" or ".join(file_names)), "srcs")
return crate_root
@@ -237,22 +290,23 @@
list: A list of providers. See `rustc_compile_action`
"""
- # Find lib.rs
- crate_root = crate_root_src(ctx.attr, ctx.files.srcs, "lib")
+ srcs, crate_root = _transform_sources(ctx, ctx.files.srcs, getattr(ctx.file, "crate_root", None))
+ if not crate_root:
+ crate_root = crate_root_src(ctx.attr.name, srcs, "lib")
_assert_no_deprecated_attributes(ctx)
_assert_correct_dep_mapping(ctx)
toolchain = find_toolchain(ctx)
# Determine unique hash for this rlib.
- # Note that we don't include a hash for `cdylib` since they are meant to be consumed externally and having a
- # deterministic name is important since it ends up embedded in the executable. This is problematic when one needs
- # to include the library with a specific filename into a larger application.
+ # Note that we don't include a hash for `cdylib` and `staticlib` since they are meant to be consumed externally
+ # and having a deterministic name is important since it ends up embedded in the executable. This is problematic
+ # when one needs to include the library with a specific filename into a larger application.
# (see https://github.com/bazelbuild/rules_rust/issues/405#issuecomment-993089889 for more details)
- if crate_type != "cdylib":
- output_hash = determine_output_hash(crate_root, ctx.label)
- else:
+ if crate_type in ["cdylib", "staticlib"]:
output_hash = None
+ else:
+ output_hash = determine_output_hash(crate_root, ctx.label)
crate_name = compute_crate_name(ctx.workspace_name, ctx.label, toolchain, ctx.attr.crate_name)
rust_lib_name = _determine_lib_name(
@@ -263,6 +317,13 @@
)
rust_lib = ctx.actions.declare_file(rust_lib_name)
+ rust_metadata = None
+ if can_build_metadata(toolchain, ctx, crate_type) and not ctx.attr.disable_pipelining:
+ rust_metadata = ctx.actions.declare_file(
+ paths.replace_extension(rust_lib_name, ".rmeta"),
+ sibling = rust_lib,
+ )
+
deps = transform_deps(ctx.attr.deps)
proc_macro_deps = transform_deps(ctx.attr.proc_macro_deps + get_import_macro_deps(ctx))
@@ -274,13 +335,15 @@
name = crate_name,
type = crate_type,
root = crate_root,
- srcs = depset(ctx.files.srcs),
+ srcs = depset(srcs),
deps = depset(deps),
proc_macro_deps = depset(proc_macro_deps),
aliases = ctx.attr.aliases,
output = rust_lib,
- edition = get_edition(ctx.attr, toolchain),
+ metadata = rust_metadata,
+ edition = get_edition(ctx.attr, toolchain, ctx.label),
rustc_env = ctx.attr.rustc_env,
+ rustc_env_files = ctx.files.rustc_env_files,
is_test = False,
compile_data = depset(ctx.files.compile_data),
owner = ctx.label,
@@ -306,6 +369,10 @@
deps = transform_deps(ctx.attr.deps)
proc_macro_deps = transform_deps(ctx.attr.proc_macro_deps + get_import_macro_deps(ctx))
+ srcs, crate_root = _transform_sources(ctx, ctx.files.srcs, getattr(ctx.file, "crate_root", None))
+ if not crate_root:
+ crate_root = crate_root_src(ctx.attr.name, srcs, ctx.attr.crate_type)
+
return rustc_compile_action(
ctx = ctx,
attr = ctx.attr,
@@ -313,27 +380,26 @@
crate_info = rust_common.create_crate_info(
name = crate_name,
type = ctx.attr.crate_type,
- root = crate_root_src(ctx.attr, ctx.files.srcs, ctx.attr.crate_type),
- srcs = depset(ctx.files.srcs),
+ root = crate_root,
+ srcs = depset(srcs),
deps = depset(deps),
proc_macro_deps = depset(proc_macro_deps),
aliases = ctx.attr.aliases,
output = output,
- edition = get_edition(ctx.attr, toolchain),
+ edition = get_edition(ctx.attr, toolchain, ctx.label),
rustc_env = ctx.attr.rustc_env,
+ rustc_env_files = ctx.files.rustc_env_files,
is_test = False,
compile_data = depset(ctx.files.compile_data),
owner = ctx.label,
),
)
-def _rust_test_common(ctx, toolchain, output):
- """Builds a Rust test binary.
+def _rust_test_impl(ctx):
+ """The implementation of the `rust_test` rule.
Args:
ctx (ctx): The ctx object for the current target.
- toolchain (rust_toolchain): The current `rust_toolchain`
- output (File): The output File that will be produced, depends on crate type.
Returns:
list: The list of providers. See `rustc_compile_action`
@@ -341,7 +407,9 @@
_assert_no_deprecated_attributes(ctx)
_assert_correct_dep_mapping(ctx)
- crate_name = compute_crate_name(ctx.workspace_name, ctx.label, toolchain, ctx.attr.crate_name)
+ toolchain = find_toolchain(ctx)
+
+ srcs, crate_root = _transform_sources(ctx, ctx.files.srcs, getattr(ctx.file, "crate_root", None))
crate_type = "bin"
deps = transform_deps(ctx.attr.deps)
@@ -349,44 +417,70 @@
if ctx.attr.crate:
# Target is building the crate in `test` config
- crate = ctx.attr.crate[rust_common.crate_info]
+ 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
+
+ output_hash = determine_output_hash(crate.root, ctx.label)
+ output = ctx.actions.declare_file(
+ "test-%s/%s%s" % (
+ output_hash,
+ ctx.label.name,
+ toolchain.binary_ext,
+ ),
+ )
# Optionally join compile data
if crate.compile_data:
compile_data = depset(ctx.files.compile_data, transitive = [crate.compile_data])
else:
compile_data = depset(ctx.files.compile_data)
+ rustc_env_files = ctx.files.rustc_env_files + crate.rustc_env_files
+ rustc_env = dict(crate.rustc_env)
+ rustc_env.update(**ctx.attr.rustc_env)
# Build the test binary using the dependency's srcs.
crate_info = rust_common.create_crate_info(
- name = crate_name,
+ name = crate.name,
type = crate_type,
root = crate.root,
- srcs = depset(ctx.files.srcs, transitive = [crate.srcs]),
+ srcs = depset(srcs, transitive = [crate.srcs]),
deps = depset(deps, transitive = [crate.deps]),
proc_macro_deps = depset(proc_macro_deps, transitive = [crate.proc_macro_deps]),
aliases = ctx.attr.aliases,
output = output,
edition = crate.edition,
- rustc_env = ctx.attr.rustc_env,
+ rustc_env = rustc_env,
+ rustc_env_files = rustc_env_files,
is_test = True,
compile_data = compile_data,
wrapped_crate_type = crate.type,
owner = ctx.label,
)
else:
+ if not crate_root:
+ crate_root = crate_root_src(ctx.attr.name, ctx.files.srcs, "lib")
+
+ output_hash = determine_output_hash(crate_root, ctx.label)
+ output = ctx.actions.declare_file(
+ "test-%s/%s%s" % (
+ output_hash,
+ ctx.label.name,
+ toolchain.binary_ext,
+ ),
+ )
+
# Target is a standalone crate. Build the test binary as its own crate.
crate_info = rust_common.create_crate_info(
- name = crate_name,
+ name = compute_crate_name(ctx.workspace_name, ctx.label, toolchain, ctx.attr.crate_name),
type = crate_type,
- root = crate_root_src(ctx.attr, ctx.files.srcs, "lib"),
- srcs = depset(ctx.files.srcs),
+ root = crate_root,
+ srcs = depset(srcs),
deps = depset(deps),
proc_macro_deps = depset(proc_macro_deps),
aliases = ctx.attr.aliases,
output = output,
- edition = get_edition(ctx.attr, toolchain),
+ edition = get_edition(ctx.attr, toolchain, ctx.label),
rustc_env = ctx.attr.rustc_env,
+ rustc_env_files = ctx.files.rustc_env_files,
is_test = True,
compile_data = depset(ctx.files.compile_data),
owner = ctx.label,
@@ -406,27 +500,42 @@
getattr(ctx.attr, "env", {}),
data,
)
+ if toolchain.llvm_cov and ctx.configuration.coverage_enabled:
+ if not toolchain.llvm_profdata:
+ fail("toolchain.llvm_profdata is required if toolchain.llvm_cov is set.")
+
+ env["RUST_LLVM_COV"] = toolchain.llvm_cov.path
+ env["RUST_LLVM_PROFDATA"] = toolchain.llvm_profdata.path
providers.append(testing.TestEnvironment(env))
return providers
-def _rust_test_impl(ctx):
- """The implementation of the `rust_test` rule
+def _stamp_attribute(default_value):
+ return attr.int(
+ doc = dedent("""\
+ Whether to encode build information into the `Rustc` action. Possible values:
- Args:
- ctx (ctx): The rule's context object
+ - `stamp = 1`: Always stamp the build information into the `Rustc` action, even in \
+ [--nostamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) builds. \
+ This setting should be avoided, since it potentially kills remote caching for the target and \
+ any downstream actions that depend on it.
- Returns:
- list: A list of providers. See `_rust_test_common`
- """
- toolchain = find_toolchain(ctx)
+ - `stamp = 0`: Always replace build information by constant values. This gives good build result caching.
- output = ctx.actions.declare_file(
- ctx.label.name + toolchain.binary_ext,
+ - `stamp = -1`: Embedding of build information is controlled by the \
+ [--[no]stamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) flag.
+
+ Stamped targets are not rebuilt unless their dependencies change.
+
+ For example if a `rust_library` is stamped, and a `rust_binary` depends on that library, the stamped
+ library won't be rebuilt when we change sources of the `rust_binary`. This is different from how
+ [`cc_library.linkstamps`](https://docs.bazel.build/versions/main/be/c-cpp.html#cc_library.linkstamp)
+ behaves.
+ """),
+ default = default_value,
+ values = [1, 0, -1],
)
- return _rust_test_common(ctx, toolchain, output)
-
_common_attrs = {
"aliases": attr.label_keyed_string_dict(
doc = dedent("""\
@@ -558,44 +667,51 @@
"""),
allow_files = [".rs"],
),
- "stamp": attr.int(
- doc = dedent("""\
- Whether to encode build information into the `Rustc` action. Possible values:
-
- - `stamp = 1`: Always stamp the build information into the `Rustc` action, even in \
- [--nostamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) builds. \
- This setting should be avoided, since it potentially kills remote caching for the target and \
- any downstream actions that depend on it.
-
- - `stamp = 0`: Always replace build information by constant values. This gives good build result caching.
-
- - `stamp = -1`: Embedding of build information is controlled by the \
- [--[no]stamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) flag.
-
- Stamped targets are not rebuilt unless their dependencies change.
-
- For example if a `rust_library` is stamped, and a `rust_binary` depends on that library, the stamped
- library won't be rebuilt when we change sources of the `rust_binary`. This is different from how
- [`cc_library.linkstamps`](https://docs.bazel.build/versions/main/be/c-cpp.html#cc_library.linkstamp)
- behaves.
- """),
- default = -1,
- values = [1, 0, -1],
- ),
+ "stamp": _stamp_attribute(default_value = 0),
"version": attr.string(
doc = "A version to inject in the cargo environment variable.",
default = "0.0.0",
),
"_cc_toolchain": attr.label(
- default = "@bazel_tools//tools/cpp:current_cc_toolchain",
+ doc = (
+ "In order to use find_cc_toolchain, your rule has to depend " +
+ "on C++ toolchain. See `@rules_cc//cc:find_cc_toolchain.bzl` " +
+ "docs for details."
+ ),
+ default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"),
),
- "_error_format": attr.label(default = "//:error_format"),
- "_extra_exec_rustc_flags": attr.label(default = "//:extra_exec_rustc_flags"),
- "_extra_rustc_flags": attr.label(default = "//:extra_rustc_flags"),
+ "_collect_cc_coverage": attr.label(
+ default = Label("//util:collect_coverage"),
+ executable = True,
+ cfg = "exec",
+ ),
+ "_error_format": attr.label(
+ default = Label("//:error_format"),
+ ),
+ "_extra_exec_rustc_flag": attr.label(
+ default = Label("//:extra_exec_rustc_flag"),
+ ),
+ "_extra_exec_rustc_flags": attr.label(
+ default = Label("//:extra_exec_rustc_flags"),
+ ),
+ "_extra_rustc_flag": attr.label(
+ default = Label("//:extra_rustc_flag"),
+ ),
+ "_extra_rustc_flags": attr.label(
+ default = Label("//:extra_rustc_flags"),
+ ),
"_import_macro_dep": attr.label(
- default = "@rules_rust//util/import",
+ default = Label("//util/import"),
+ cfg = "exec",
+ ),
+ "_is_proc_macro_dep": attr.label(
+ default = Label("//:is_proc_macro_dep"),
+ ),
+ "_is_proc_macro_dep_enabled": attr.label(
+ default = Label("//:is_proc_macro_dep_enabled"),
),
"_process_wrapper": attr.label(
+ doc = "A process wrapper for running rustc on all platforms.",
default = Label("//util/process_wrapper"),
executable = True,
allow_single_file = True,
@@ -607,7 +723,22 @@
),
}
-_rust_test_attrs = {
+_experimental_use_cc_common_link_attrs = {
+ "experimental_use_cc_common_link": attr.int(
+ doc = (
+ "Whether to use cc_common.link to link rust binaries. " +
+ "Possible values: [-1, 0, 1]. " +
+ "-1 means use the value of the toolchain.experimental_use_cc_common_link " +
+ "boolean build setting to determine. " +
+ "0 means do not use cc_common.link (use rustc instead). " +
+ "1 means use cc_common.link."
+ ),
+ values = [-1, 0, 1],
+ default = -1,
+ ),
+}
+
+_rust_test_attrs = dict({
"crate": attr.label(
mandatory = False,
doc = dedent("""\
@@ -644,7 +775,7 @@
default = Label("@bazel_tools//tools/cpp:grep-includes"),
executable = True,
),
-}
+}.items() + _experimental_use_cc_common_link_attrs.items())
_common_providers = [
rust_common.crate_info,
@@ -655,11 +786,20 @@
rust_library = rule(
implementation = _rust_library_impl,
provides = _common_providers,
- attrs = dict(_common_attrs.items()),
+ attrs = dict(_common_attrs.items() + {
+ "disable_pipelining": attr.bool(
+ default = False,
+ doc = dedent("""\
+ Disables pipelining for this rule if it is globally enabled.
+ This will cause this rule to not produce a `.rmeta` file and all the dependent
+ crates will instead use the `.rlib` file.
+ """),
+ ),
+ }.items()),
fragments = ["cpp"],
host_fragments = ["cpp"],
toolchains = [
- str(Label("//rust:toolchain")),
+ str(Label("//rust:toolchain_type")),
"@bazel_tools//tools/cpp:toolchain_type",
],
incompatible_use_toolchain_transition = True,
@@ -731,12 +871,11 @@
rust_static_library = rule(
implementation = _rust_static_library_impl,
- provides = _common_providers,
attrs = dict(_common_attrs.items()),
fragments = ["cpp"],
host_fragments = ["cpp"],
toolchains = [
- str(Label("//rust:toolchain")),
+ str(Label("//rust:toolchain_type")),
"@bazel_tools//tools/cpp:toolchain_type",
],
incompatible_use_toolchain_transition = True,
@@ -755,12 +894,11 @@
rust_shared_library = rule(
implementation = _rust_shared_library_impl,
- provides = _common_providers,
attrs = dict(_common_attrs.items()),
fragments = ["cpp"],
host_fragments = ["cpp"],
toolchains = [
- str(Label("//rust:toolchain")),
+ str(Label("//rust:toolchain_type")),
"@bazel_tools//tools/cpp:toolchain_type",
],
incompatible_use_toolchain_transition = True,
@@ -777,14 +915,42 @@
"""),
)
+def _proc_macro_dep_transition_impl(settings, _attr):
+ if settings["//:is_proc_macro_dep_enabled"]:
+ return {"//:is_proc_macro_dep": True}
+ else:
+ return []
+
+_proc_macro_dep_transition = transition(
+ inputs = ["//:is_proc_macro_dep_enabled"],
+ outputs = ["//:is_proc_macro_dep"],
+ implementation = _proc_macro_dep_transition_impl,
+)
+
rust_proc_macro = rule(
implementation = _rust_proc_macro_impl,
provides = _common_providers,
- attrs = dict(_common_attrs.items()),
+ # Start by copying the common attributes, then override the `deps` attribute
+ # to apply `_proc_macro_dep_transition`. To add this transition we additionally
+ # need to declare `_allowlist_function_transition`, see
+ # https://docs.bazel.build/versions/main/skylark/config.html#user-defined-transitions.
+ attrs = dict(
+ _common_attrs.items(),
+ _allowlist_function_transition = attr.label(default = Label("//tools/allowlists/function_transition_allowlist")),
+ deps = attr.label_list(
+ doc = dedent("""\
+ List of other libraries to be linked to this library target.
+
+ These can be either other `rust_library` targets or `cc_library` targets if
+ linking a native library.
+ """),
+ cfg = _proc_macro_dep_transition,
+ ),
+ ),
fragments = ["cpp"],
host_fragments = ["cpp"],
toolchains = [
- str(Label("//rust:toolchain")),
+ str(Label("//rust:toolchain_type")),
"@bazel_tools//tools/cpp:toolchain_type",
],
incompatible_use_toolchain_transition = True,
@@ -793,7 +959,7 @@
"""),
)
-_rust_binary_attrs = {
+_rust_binary_attrs = dict({
"crate_type": attr.string(
doc = dedent("""\
Crate type that will be passed to `rustc` to be used for building this crate.
@@ -818,13 +984,14 @@
),
default = False,
),
+ "stamp": _stamp_attribute(default_value = -1),
"_grep_includes": attr.label(
allow_single_file = True,
cfg = "exec",
default = Label("@bazel_tools//tools/cpp:grep-includes"),
executable = True,
),
-}
+}.items() + _experimental_use_cc_common_link_attrs.items())
rust_binary = rule(
implementation = _rust_binary_impl,
@@ -834,7 +1001,7 @@
fragments = ["cpp"],
host_fragments = ["cpp"],
toolchains = [
- str(Label("//rust:toolchain")),
+ str(Label("//rust:toolchain_type")),
"@bazel_tools//tools/cpp:toolchain_type",
],
incompatible_use_toolchain_transition = True,
@@ -928,6 +1095,59 @@
"""),
)
+def _common_attrs_for_binary_without_process_wrapper(attrs):
+ new_attr = dict(attrs)
+
+ # use a fake process wrapper
+ new_attr["_process_wrapper"] = attr.label(
+ default = None,
+ executable = True,
+ allow_single_file = True,
+ cfg = "exec",
+ )
+
+ # fix stamp = 0
+ new_attr["stamp"] = attr.int(
+ doc = dedent("""\
+ Fix `stamp = 0` as stamping is not supported when building without process_wrapper:
+ https://github.com/bazelbuild/rules_rust/blob/8df4517d370b0c543a01ba38b63e1d5a4104b035/rust/private/rustc.bzl#L955
+ """),
+ default = 0,
+ values = [0],
+ )
+
+ return new_attr
+
+# Provides an internal rust_{binary,library} to use that we can use to build the process
+# wrapper, this breaks the dependency of rust_* on the process wrapper by
+# setting it to None, which the functions in rustc detect and build accordingly.
+rust_binary_without_process_wrapper = rule(
+ implementation = _rust_binary_impl,
+ provides = _common_providers,
+ attrs = _common_attrs_for_binary_without_process_wrapper(_common_attrs.items() + _rust_binary_attrs.items()),
+ executable = True,
+ fragments = ["cpp"],
+ host_fragments = ["cpp"],
+ toolchains = [
+ str(Label("//rust:toolchain_type")),
+ "@bazel_tools//tools/cpp:toolchain_type",
+ ],
+ incompatible_use_toolchain_transition = True,
+)
+
+rust_library_without_process_wrapper = rule(
+ implementation = _rust_library_impl,
+ provides = _common_providers,
+ attrs = dict(_common_attrs_for_binary_without_process_wrapper(_common_attrs).items()),
+ fragments = ["cpp"],
+ host_fragments = ["cpp"],
+ toolchains = [
+ str(Label("//rust:toolchain_type")),
+ "@bazel_tools//tools/cpp:toolchain_type",
+ ],
+ incompatible_use_toolchain_transition = True,
+)
+
rust_test = rule(
implementation = _rust_test_impl,
provides = _common_providers,
@@ -938,7 +1158,7 @@
host_fragments = ["cpp"],
test = True,
toolchains = [
- str(Label("//rust:toolchain")),
+ str(Label("//rust:toolchain_type")),
"@bazel_tools//tools/cpp:toolchain_type",
],
incompatible_use_toolchain_transition = True,
@@ -987,8 +1207,9 @@
}
```
- To build and run the tests, simply add a `rust_test` rule with no `srcs` and \
- only depends on the `hello_lib` `rust_library` target:
+ To build and run the tests, simply add a `rust_test` rule with no `srcs`
+ and only depends on the `hello_lib` `rust_library` target via the
+ `crate` attribute:
`hello_lib/BUILD`:
```python
@@ -1001,24 +1222,15 @@
rust_test(
name = "hello_lib_test",
- deps = [":hello_lib"],
- )
- ```
-
- Run the test with `bazel build //hello_lib:hello_lib_test`.
-
- To run a crate or lib with the `#[cfg(test)]` configuration, handling inline \
- tests, you should specify the crate directly like so.
-
- ```python
- rust_test(
- name = "hello_lib_test",
crate = ":hello_lib",
# You may add other deps that are specific to the test configuration
deps = ["//some/dev/dep"],
- )
```
+ Run the test with `bazel test //hello_lib:hello_lib_test`. The crate
+ will be built using the same crate name as the underlying ":hello_lib"
+ crate.
+
### Example: `test` directory
Integration tests that live in the [`tests` directory][int-tests], they are \
@@ -1071,7 +1283,7 @@
)
```
- Run the test with `bazel build //hello_lib:hello_lib_test`.
+ Run the test with `bazel test //hello_lib:greeting_test`.
"""),
)
@@ -1138,7 +1350,6 @@
test_name = name + "_" + src[:-3]
rust_test(
name = test_name,
- crate_name = test_name.replace("/", "_"),
srcs = [src],
**kwargs
)
diff --git a/rust/private/rust_analyzer.bzl b/rust/private/rust_analyzer.bzl
index e28ca30..7515fd7 100644
--- a/rust/private/rust_analyzer.bzl
+++ b/rust/private/rust_analyzer.bzl
@@ -39,7 +39,7 @@
)
def _rust_analyzer_aspect_impl(target, ctx):
- if rust_common.crate_info not in target:
+ if rust_common.crate_info not in target and rust_common.test_crate_info not in target:
return []
toolchain = find_toolchain(ctx)
@@ -52,21 +52,30 @@
if hasattr(ctx.rule.attr, "rustc_flags"):
cfgs += [f[6:] for f in ctx.rule.attr.rustc_flags if f.startswith("--cfg ") or f.startswith("--cfg=")]
- # Save BuildInfo if we find any (for build script output)
build_info = None
- for dep in ctx.rule.attr.deps:
- if BuildInfo in dep:
- build_info = dep[BuildInfo]
+ dep_infos = []
+ if hasattr(ctx.rule.attr, "deps"):
+ for dep in ctx.rule.attr.deps:
+ # Save BuildInfo if we find any (for build script output)
+ if BuildInfo in dep:
+ build_info = dep[BuildInfo]
+ dep_infos = [dep[RustAnalyzerInfo] for dep in ctx.rule.attr.deps if RustAnalyzerInfo in dep]
- dep_infos = [dep[RustAnalyzerInfo] for dep in ctx.rule.attr.deps if RustAnalyzerInfo in dep]
if hasattr(ctx.rule.attr, "proc_macro_deps"):
dep_infos += [dep[RustAnalyzerInfo] for dep in ctx.rule.attr.proc_macro_deps if RustAnalyzerInfo in dep]
if hasattr(ctx.rule.attr, "crate") and ctx.rule.attr.crate != None:
dep_infos.append(ctx.rule.attr.crate[RustAnalyzerInfo])
+ if hasattr(ctx.rule.attr, "actual") and ctx.rule.attr.actual != None and RustAnalyzerInfo in ctx.rule.attr.actual:
+ dep_infos.append(ctx.rule.attr.actual[RustAnalyzerInfo])
crate_spec = ctx.actions.declare_file(ctx.label.name + ".rust_analyzer_crate_spec")
- crate_info = target[rust_common.crate_info]
+ if rust_common.crate_info in target:
+ crate_info = target[rust_common.crate_info]
+ elif rust_common.test_crate_info in target:
+ crate_info = target[rust_common.test_crate_info].crate
+ else:
+ fail("Unexpected target type: {}".format(target))
rust_analyzer_info = RustAnalyzerInfo(
crate = crate_info,
@@ -97,7 +106,14 @@
Returns:
(path): The path to the proc macro dylib, or None if this crate is not a proc-macro.
"""
- if target[rust_common.crate_info].type != "proc-macro":
+ if rust_common.crate_info in target:
+ crate_info = target[rust_common.crate_info]
+ elif rust_common.test_crate_info in target:
+ crate_info = target[rust_common.test_crate_info].crate
+ else:
+ return None
+
+ if crate_info.type != "proc-macro":
return None
dylib_ext = system_to_dylib_ext(triple_to_system(toolchain.target_triple))
@@ -111,14 +127,15 @@
return None
rust_analyzer_aspect = aspect(
- attr_aspects = ["deps", "proc_macro_deps", "crate"],
+ attr_aspects = ["deps", "proc_macro_deps", "crate", "actual"],
implementation = _rust_analyzer_aspect_impl,
- toolchains = [str(Label("//rust:toolchain"))],
+ toolchains = [str(Label("//rust:toolchain_type"))],
incompatible_use_toolchain_transition = True,
doc = "Annotates rust rules with RustAnalyzerInfo later used to build a rust-project.json",
)
-_exec_root_tmpl = "__EXEC_ROOT__/"
+_EXEC_ROOT_TEMPLATE = "__EXEC_ROOT__/"
+_OUTPUT_BASE_TEMPLATE = "__OUTPUT_BASE__/"
def _crate_id(crate_info):
"""Returns a unique stable identifier for a crate
@@ -149,24 +166,20 @@
# Switch on external/ to determine if crates are in the workspace or remote.
# TODO: Some folks may want to override this for vendored dependencies.
- root_path = info.crate.root.path
- root_dirname = info.crate.root.dirname
- if root_path.startswith("external/"):
- crate["is_workspace_member"] = False
- crate["root_module"] = _exec_root_tmpl + root_path
- crate_root = _exec_root_tmpl + root_dirname
- else:
- crate["is_workspace_member"] = True
- crate["root_module"] = root_path
- crate_root = root_dirname
+ is_external = info.crate.root.path.startswith("external/")
+ is_generated = not info.crate.root.is_source
+ path_prefix = _EXEC_ROOT_TEMPLATE if is_external or is_generated else ""
+ crate["is_workspace_member"] = not is_external
+ crate["root_module"] = path_prefix + info.crate.root.path
+ crate_root = path_prefix + info.crate.root.dirname
if info.build_info != None:
out_dir_path = info.build_info.out_dir.path
- crate["env"].update({"OUT_DIR": _exec_root_tmpl + out_dir_path})
+ crate["env"].update({"OUT_DIR": _EXEC_ROOT_TEMPLATE + out_dir_path})
crate["source"] = {
# We have to tell rust-analyzer about our out_dir since it's not under the crate root.
"exclude_dirs": [],
- "include_dirs": [crate_root, _exec_root_tmpl + out_dir_path],
+ "include_dirs": [crate_root, _EXEC_ROOT_TEMPLATE + out_dir_path],
}
# TODO: The only imagined use case is an env var holding a filename in the workspace passed to a
@@ -188,21 +201,42 @@
crate["cfg"] = info.cfgs
crate["target"] = find_toolchain(ctx).target_triple
if info.proc_macro_dylib_path != None:
- crate["proc_macro_dylib_path"] = _exec_root_tmpl + info.proc_macro_dylib_path
+ crate["proc_macro_dylib_path"] = _EXEC_ROOT_TEMPLATE + info.proc_macro_dylib_path
return crate
-def _rust_analyzer_detect_sysroot_impl(ctx):
- rust_toolchain = find_toolchain(ctx)
+def _rust_analyzer_toolchain_impl(ctx):
+ toolchain = platform_common.ToolchainInfo(
+ rustc_srcs = ctx.attr.rustc_srcs,
+ )
- if not rust_toolchain.rustc_srcs:
+ return [toolchain]
+
+rust_analyzer_toolchain = rule(
+ implementation = _rust_analyzer_toolchain_impl,
+ doc = "A toolchain for [rust-analyzer](https://rust-analyzer.github.io/).",
+ attrs = {
+ "rustc_srcs": attr.label(
+ doc = "The source code of rustc.",
+ mandatory = True,
+ ),
+ },
+ incompatible_use_toolchain_transition = True,
+)
+
+def _rust_analyzer_detect_sysroot_impl(ctx):
+ rust_analyzer_toolchain = ctx.toolchains[Label("@rules_rust//rust/rust_analyzer:toolchain_type")]
+
+ if not rust_analyzer_toolchain.rustc_srcs:
fail(
- "Current Rust toolchain doesn't contain rustc sources in `rustc_srcs` attribute.",
- "These are needed by rust analyzer.",
- "If you are using the default Rust toolchain, add `rust_repositories(include_rustc_srcs = True, ...).` to your WORKSPACE file.",
+ "Current Rust-Analyzer toolchain doesn't contain rustc sources in `rustc_srcs` attribute.",
+ "These are needed by rust-analyzer. If you are using the default Rust toolchain, add `rust_repositories(include_rustc_srcs = True, ...).` to your WORKSPACE file.",
)
- sysroot_src = rust_toolchain.rustc_srcs.label.package + "/library"
- if rust_toolchain.rustc_srcs.label.workspace_root:
- sysroot_src = _exec_root_tmpl + rust_toolchain.rustc_srcs.label.workspace_root + "/" + sysroot_src
+
+ rustc_srcs = rust_analyzer_toolchain.rustc_srcs
+
+ sysroot_src = rustc_srcs.label.package + "/library"
+ if rustc_srcs.label.workspace_root:
+ sysroot_src = _OUTPUT_BASE_TEMPLATE + rustc_srcs.label.workspace_root + "/" + sysroot_src
sysroot_src_file = ctx.actions.declare_file(ctx.label.name + ".rust_analyzer_sysroot_src")
ctx.actions.write(
@@ -214,27 +248,12 @@
rust_analyzer_detect_sysroot = rule(
implementation = _rust_analyzer_detect_sysroot_impl,
- toolchains = ["@rules_rust//rust:toolchain"],
+ toolchains = [
+ "@rules_rust//rust:toolchain_type",
+ "@rules_rust//rust/rust_analyzer:toolchain_type",
+ ],
incompatible_use_toolchain_transition = True,
doc = dedent("""\
Detect the sysroot and store in a file for use by the gen_rust_project tool.
"""),
)
-
-def _rust_analyzer_impl(_ctx):
- pass
-
-rust_analyzer = rule(
- attrs = {
- "targets": attr.label_list(
- aspects = [rust_analyzer_aspect],
- doc = "List of all targets to be included in the index",
- ),
- },
- implementation = _rust_analyzer_impl,
- toolchains = [str(Label("//rust:toolchain"))],
- incompatible_use_toolchain_transition = True,
- doc = dedent("""\
- Deprecated: gen_rust_project can now create a rust-project.json without a rust_analyzer rule.
- """),
-)
diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl
index 596c10d..490bfa1 100644
--- a/rust/private/rustc.bzl
+++ b/rust/private/rustc.bzl
@@ -27,7 +27,8 @@
"expand_dict_value_locations",
"expand_list_element_locations",
"find_cc_toolchain",
- "get_lib_name",
+ "get_lib_name_default",
+ "get_lib_name_for_windows",
"get_preferred_artifact",
"is_exec_configuration",
"make_static_lib_symlink",
@@ -61,6 +62,34 @@
fields = {"extra_exec_rustc_flags": "List[string] Extra flags to pass to rustc in exec configuration"},
)
+IsProcMacroDepInfo = provider(
+ doc = "Records if this is a transitive dependency of a proc-macro.",
+ fields = {"is_proc_macro_dep": "Boolean"},
+)
+
+def _is_proc_macro_dep_impl(ctx):
+ return IsProcMacroDepInfo(is_proc_macro_dep = ctx.build_setting_value)
+
+is_proc_macro_dep = rule(
+ doc = "Records if this is a transitive dependency of a proc-macro.",
+ implementation = _is_proc_macro_dep_impl,
+ build_setting = config.bool(flag = True),
+)
+
+IsProcMacroDepEnabledInfo = provider(
+ doc = "Enables the feature to record if a library is a transitive dependency of a proc-macro.",
+ fields = {"enabled": "Boolean"},
+)
+
+def _is_proc_macro_dep_enabled_impl(ctx):
+ return IsProcMacroDepEnabledInfo(enabled = ctx.build_setting_value)
+
+is_proc_macro_dep_enabled = rule(
+ doc = "Enables the feature to record if a library is a transitive dependency of a proc-macro.",
+ implementation = _is_proc_macro_dep_enabled_impl,
+ build_setting = config.bool(flag = True),
+)
+
def _get_rustc_env(attr, toolchain, crate_name):
"""Gathers rustc environment variables
@@ -78,7 +107,7 @@
patch, pre = patch.split("-", 1)
else:
pre = ""
- return {
+ result = {
"CARGO_CFG_TARGET_ARCH": toolchain.target_arch,
"CARGO_CFG_TARGET_OS": toolchain.os,
"CARGO_CRATE_NAME": crate_name,
@@ -92,6 +121,12 @@
"CARGO_PKG_VERSION_PATCH": patch,
"CARGO_PKG_VERSION_PRE": pre,
}
+ if hasattr(attr, "_is_proc_macro_dep_enabled") and attr._is_proc_macro_dep_enabled[IsProcMacroDepEnabledInfo].enabled:
+ is_proc_macro_dep = "0"
+ if hasattr(attr, "_is_proc_macro_dep") and attr._is_proc_macro_dep[IsProcMacroDepInfo].is_proc_macro_dep:
+ is_proc_macro_dep = "1"
+ result["BAZEL_RULES_RUST_IS_PROC_MACRO_DEP"] = is_proc_macro_dep
+ return result
def get_compilation_mode_opts(ctx, toolchain):
"""Gathers rustc flags for the current compilation mode (opt/debug)
@@ -119,10 +154,25 @@
has_grep_includes)
def _should_use_pic(cc_toolchain, feature_configuration, crate_type):
- if crate_type in ("cdylib" or "dylib"):
+ """Whether or not [PIC][pic] should be enabled
+
+ [pic]: https://en.wikipedia.org/wiki/Position-independent_code
+
+ Args:
+ cc_toolchain (CcToolchainInfo): The current `cc_toolchain`.
+ feature_configuration (FeatureConfiguration): Feature configuration to be queried.
+ crate_type (str): A Rust target's crate type.
+
+ Returns:
+ bool: Whether or not [PIC][pic] should be enabled.
+ """
+ if crate_type in ("cdylib", "dylib"):
return cc_toolchain.needs_pic_for_dynamic_libraries(feature_configuration = feature_configuration)
return False
+def _is_proc_macro(crate_info):
+ return "proc-macro" in (crate_info.type, crate_info.wrapped_crate_type)
+
def collect_deps(
deps,
proc_macro_deps,
@@ -151,6 +201,7 @@
build_info = None
linkstamps = []
transitive_crate_outputs = []
+ transitive_metadata_outputs = []
aliases = {k.label: v for k, v in aliases.items()}
for dep in depset(transitive = [deps, proc_macro_deps]).to_list():
@@ -173,16 +224,42 @@
dep = crate_info,
))
- transitive_crates.append(depset([crate_info], transitive = [dep_info.transitive_crates]))
+ transitive_crates.append(
+ depset(
+ [crate_info],
+ transitive = [] if _is_proc_macro(crate_info) else [dep_info.transitive_crates],
+ ),
+ )
+
+ # If this dependency produces metadata, add it to the metadata outputs.
+ # If it doesn't (for example a custom library that exports crate_info),
+ # we depend on crate_info.output.
+ depend_on = crate_info.metadata
+ if not crate_info.metadata:
+ depend_on = crate_info.output
+
+ # If this dependency is a proc_macro, it still can be used for lib crates
+ # that produce metadata.
+ # In that case, we don't depend on its metadata dependencies.
+ transitive_metadata_outputs.append(
+ depset(
+ [depend_on],
+ transitive = [] if _is_proc_macro(crate_info) else [dep_info.transitive_metadata_outputs],
+ ),
+ )
+
transitive_crate_outputs.append(
depset(
[crate_info.output],
- transitive = [dep_info.transitive_crate_outputs],
+ transitive = [] if _is_proc_macro(crate_info) else [dep_info.transitive_crate_outputs],
),
)
- transitive_noncrates.append(dep_info.transitive_noncrates)
+
+ if "proc-macro" not in [crate_info.type, crate_info.wrapped_crate_type]:
+ transitive_noncrates.append(dep_info.transitive_noncrates)
+ transitive_link_search_paths.append(dep_info.link_search_path_files)
+
transitive_build_infos.append(dep_info.transitive_build_infos)
- transitive_link_search_paths.append(dep_info.link_search_path_files)
elif cc_info:
# This dependency is a cc_library
@@ -209,6 +286,7 @@
order = "topological", # dylib link flag ordering matters.
),
transitive_crate_outputs = depset(transitive = transitive_crate_outputs),
+ transitive_metadata_outputs = depset(transitive = transitive_metadata_outputs),
transitive_build_infos = depset(transitive = transitive_build_infos),
link_search_path_files = depset(transitive = transitive_link_search_paths),
dep_env = build_info.dep_env if build_info else None,
@@ -348,7 +426,7 @@
# Take the absolute value of hash() since it could be negative.
path_hash = abs(hash(lib.path))
- lib_name = get_lib_name(lib)
+ lib_name = get_lib_name_for_windows(lib) if toolchain.os.startswith("windows") else get_lib_name_default(lib)
prefix = "lib"
extension = ".a"
@@ -411,7 +489,7 @@
if _is_dylib(lib):
continue
artifact = get_preferred_artifact(lib, use_pic)
- name = get_lib_name(artifact)
+ name = get_lib_name_for_windows(artifact) if toolchain.os.startswith("windows") else get_lib_name_default(artifact)
# On Linux-like platforms, normally library base names start with
# `lib`, following the pattern `lib[name].(a|lo)` and we pass
@@ -445,6 +523,28 @@
visited_libs[name] = artifact
return ambiguous_libs
+def _depend_on_metadata(crate_info, force_depend_on_objects):
+ """Determines if we can depend on metadata for this crate.
+
+ By default (when pipelining is disabled or when the crate type needs to link against
+ objects) we depend on the set of object files (.rlib).
+ When pipelining is enabled and the crate type supports depending on metadata,
+ we depend on metadata files only (.rmeta).
+ In some rare cases, even if both of those conditions are true, we still want to
+ depend on objects. This is what force_depend_on_objects is.
+
+ Args:
+ crate_info (CrateInfo): The Crate to determine this for.
+ force_depend_on_objects (bool): if set we will not depend on metadata.
+
+ Returns:
+ Whether we can depend on metadata for this crate.
+ """
+ if force_depend_on_objects:
+ return False
+
+ return crate_info.type in ("rlib", "lib")
+
def collect_inputs(
ctx,
file,
@@ -456,7 +556,9 @@
crate_info,
dep_info,
build_info,
- stamp = False):
+ stamp = False,
+ force_depend_on_objects = False,
+ experimental_use_cc_common_link = False):
"""Gather's the inputs and required input information for a rustc action
Args:
@@ -472,6 +574,10 @@
build_info (BuildInfo): The target Crate's build settings.
stamp (bool, optional): Whether or not workspace status stamping is enabled. For more details see
https://docs.bazel.build/versions/main/user-manual.html#flag--stamp
+ force_depend_on_objects (bool, optional): Forces dependencies of this rule to be objects rather than
+ metadata, even for libraries. This is used in rustdoc tests.
+ experimental_use_cc_common_link (bool, optional): Whether rules_rust uses cc_common.link to link
+ rust binaries.
Returns:
tuple: A tuple: A tuple of the following items:
@@ -512,6 +618,10 @@
# change.
linkstamp_outs = []
+ transitive_crate_outputs = dep_info.transitive_crate_outputs
+ if _depend_on_metadata(crate_info, force_depend_on_objects):
+ transitive_crate_outputs = dep_info.transitive_metadata_outputs
+
nolinkstamp_compile_inputs = depset(
getattr(files, "data", []) +
([build_info.rustc_env, build_info.flags] if build_info else []) +
@@ -520,14 +630,16 @@
transitive = [
linker_depset,
crate_info.srcs,
- dep_info.transitive_crate_outputs,
+ transitive_crate_outputs,
depset(additional_transitive_inputs),
crate_info.compile_data,
toolchain.all_files,
],
)
- if crate_info.type in ("bin", "cdylib"):
+ # Register linkstamps when linking with rustc (when linking with
+ # cc_common.link linkstamps are handled by cc_common.link itself).
+ if not experimental_use_cc_common_link and crate_info.type in ("bin", "cdylib"):
# There is no other way to register an action for each member of a depset than
# flattening the depset as of 2021-10-12. Luckily, usually there is only one linkstamp
# in a build, and we only flatten the list on binary targets that perform transitive linking,
@@ -555,8 +667,8 @@
output_replacement = crate_info.output.path,
)
- # If stamping is enabled include the volatile status info file
- stamp_info = [ctx.version_file] if stamp else []
+ # If stamping is enabled include the volatile and stable status info file
+ stamp_info = [ctx.version_file, ctx.info_file] if stamp else []
compile_inputs = depset(
linkstamp_outs + stamp_info,
@@ -565,7 +677,9 @@
],
)
- build_env_files = getattr(files, "rustc_env_files", [])
+ # For backwards compatibility, we also check the value of the `rustc_env_files` attribute when
+ # `crate_info.rustc_env_files` is not populated.
+ build_env_files = crate_info.rustc_env_files if crate_info.rustc_env_files else getattr(files, "rustc_env_files", [])
compile_inputs, out_dir, build_env_file, build_flags_files = _process_build_scripts(build_info, dep_info, compile_inputs)
if build_env_file:
build_env_files = [f for f in build_env_files] + [build_env_file]
@@ -594,7 +708,10 @@
force_all_deps_direct = False,
force_link = False,
stamp = False,
- remap_path_prefix = "."):
+ remap_path_prefix = ".",
+ use_json_output = False,
+ build_metadata = False,
+ force_depend_on_objects = False):
"""Builds an Args object containing common rustc flags
Args:
@@ -621,6 +738,9 @@
stamp (bool, optional): Whether or not workspace status stamping is enabled. For more details see
https://docs.bazel.build/versions/main/user-manual.html#flag--stamp
remap_path_prefix (str, optional): A value used to remap `${pwd}` to. If set to a falsey value, no prefix will be set.
+ use_json_output (bool): Have rustc emit json and process_wrapper parse json messages to output rendered output.
+ build_metadata (bool): Generate CLI arguments for building *only* .rmeta files. This requires use_json_output.
+ force_depend_on_objects (bool): Force using `.rlib` object files instead of metadata (`.rmeta`) files even if they are available.
Returns:
tuple: A tuple of the following items
@@ -632,6 +752,9 @@
This is to be passed to the `arguments` parameter of actions
- (dict): Common rustc environment variables
"""
+ if build_metadata and not use_json_output:
+ fail("build_metadata requires parse_json_output")
+
output_dir = getattr(crate_info.output, "dirname", None)
linker_script = getattr(file, "linker_script", None)
@@ -662,6 +785,7 @@
# If stamping is enabled, enable the functionality in the process wrapper
if stamp:
process_wrapper_flags.add("--volatile-status-file", ctx.version_file)
+ process_wrapper_flags.add("--stable-status-file", ctx.info_file)
# Both ctx.label.workspace_root and ctx.label.package are relative paths
# and either can be empty strings. Avoid trailing/double slashes in the path.
@@ -700,15 +824,50 @@
rustc_flags.add(crate_info.root)
rustc_flags.add("--crate-name=" + crate_info.name)
rustc_flags.add("--crate-type=" + crate_info.type)
- if hasattr(attr, "_error_format"):
- rustc_flags.add("--error-format=" + attr._error_format[ErrorFormatInfo].error_format)
- # Mangle symbols to disambiguate crates with the same name
- extra_filename = "-" + output_hash if output_hash else ""
- rustc_flags.add("--codegen=metadata=" + extra_filename)
+ error_format = "human"
+ if hasattr(attr, "_error_format"):
+ error_format = attr._error_format[ErrorFormatInfo].error_format
+
+ if use_json_output:
+ # If --error-format was set to json, we just pass the output through
+ # Otherwise process_wrapper uses the "rendered" field.
+ process_wrapper_flags.add("--rustc-output-format", "json" if error_format == "json" else "rendered")
+
+ # Configure rustc json output by adding artifact notifications.
+ # These will always be filtered out by process_wrapper and will be use to terminate
+ # rustc when appropriate.
+ json = ["artifacts"]
+ if error_format == "short":
+ json.append("diagnostic-short")
+ elif error_format == "human" and toolchain.os != "windows":
+ # If the os is not windows, we can get colorized output.
+ json.append("diagnostic-rendered-ansi")
+
+ rustc_flags.add("--json=" + ",".join(json))
+
+ error_format = "json"
+
+ if build_metadata:
+ # Configure process_wrapper to terminate rustc when metadata are emitted
+ process_wrapper_flags.add("--rustc-quit-on-rmeta", "true")
+
+ rustc_flags.add("--error-format=" + error_format)
+
+ # Mangle symbols to disambiguate crates with the same name. This could
+ # happen only for non-final artifacts where we compute an output_hash,
+ # e.g., rust_library.
+ #
+ # For "final" artifacts and ones intended for distribution outside of
+ # Bazel, such as rust_binary, rust_static_library and rust_shared_library,
+ # where output_hash is None we don't need to add these flags.
+ if output_hash:
+ extra_filename = "-" + output_hash
+ rustc_flags.add("--codegen=metadata=" + extra_filename)
+ rustc_flags.add("--codegen=extra-filename=" + extra_filename)
+
if output_dir:
rustc_flags.add("--out-dir=" + output_dir)
- rustc_flags.add("--codegen=extra-filename=" + extra_filename)
compilation_mode = get_compilation_mode_opts(ctx, toolchain)
rustc_flags.add("--codegen=opt-level=" + compilation_mode.opt_level)
@@ -720,7 +879,9 @@
if emit:
rustc_flags.add("--emit=" + ",".join(emit_with_paths))
- rustc_flags.add("--color=always")
+ if error_format != "json":
+ # Color is not compatible with json output.
+ rustc_flags.add("--color=always")
rustc_flags.add("--target=" + toolchain.target_flag_value)
if hasattr(attr, "crate_features"):
rustc_flags.add_all(getattr(attr, "crate_features"), before_each = "--cfg", format_each = 'feature="%s"')
@@ -763,15 +924,19 @@
_add_native_link_flags(rustc_flags, dep_info, linkstamp_outs, ambiguous_libs, crate_info.type, toolchain, cc_toolchain, feature_configuration)
- # These always need to be added, even if not linking this crate.
- add_crate_link_flags(rustc_flags, dep_info, force_all_deps_direct)
+ use_metadata = _depend_on_metadata(crate_info, force_depend_on_objects)
- needs_extern_proc_macro_flag = "proc-macro" in [crate_info.type, crate_info.wrapped_crate_type] and \
- crate_info.edition != "2015"
+ # These always need to be added, even if not linking this crate.
+ add_crate_link_flags(rustc_flags, dep_info, force_all_deps_direct, use_metadata)
+
+ needs_extern_proc_macro_flag = _is_proc_macro(crate_info) and crate_info.edition != "2015"
if needs_extern_proc_macro_flag:
rustc_flags.add("--extern")
rustc_flags.add("proc_macro")
+ if toolchain.llvm_cov and ctx.configuration.coverage_enabled:
+ rustc_flags.add("--codegen=instrument-coverage")
+
# Make bin crate data deps available to tests.
for data in getattr(attr, "data", []):
if rust_common.crate_info in data:
@@ -781,6 +946,9 @@
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
env["CARGO_BIN_EXE_" + env_basename] = dep_crate_info.output.short_path
+ # Add environment variables from the Rust toolchain.
+ env.update(toolchain.env)
+
# Update environment with user provided variables.
env.update(expand_dict_value_locations(
ctx,
@@ -798,9 +966,15 @@
if hasattr(ctx.attr, "_extra_rustc_flags") and not is_exec_configuration(ctx):
rustc_flags.add_all(ctx.attr._extra_rustc_flags[ExtraRustcFlagsInfo].extra_rustc_flags)
+ if hasattr(ctx.attr, "_extra_rustc_flag") and not is_exec_configuration(ctx):
+ rustc_flags.add_all(ctx.attr._extra_rustc_flag[ExtraRustcFlagsInfo].extra_rustc_flags)
+
if hasattr(ctx.attr, "_extra_exec_rustc_flags") and is_exec_configuration(ctx):
rustc_flags.add_all(ctx.attr._extra_exec_rustc_flags[ExtraExecRustcFlagsInfo].extra_exec_rustc_flags)
+ if hasattr(ctx.attr, "_extra_exec_rustc_flag") and is_exec_configuration(ctx):
+ rustc_flags.add_all(ctx.attr._extra_exec_rustc_flag[ExtraExecRustcFlagsInfo].extra_exec_rustc_flags)
+
# Create a struct which keeps the arguments separate so each may be tuned or
# replaced where necessary
args = struct(
@@ -838,8 +1012,23 @@
- (DepInfo): The transitive dependencies of this crate.
- (DefaultInfo): The output file for this crate, and its runfiles.
"""
+ build_metadata = getattr(crate_info, "metadata", None)
+
cc_toolchain, feature_configuration = find_cc_toolchain(ctx)
+ # Determine whether to use cc_common.link:
+ # * either if experimental_use_cc_common_link is 1,
+ # * or if experimental_use_cc_common_link is -1 and
+ # the toolchain experimental_use_cc_common_link is true.
+ experimental_use_cc_common_link = False
+ if hasattr(ctx.attr, "experimental_use_cc_common_link"):
+ if ctx.attr.experimental_use_cc_common_link == 0:
+ experimental_use_cc_common_link = False
+ elif ctx.attr.experimental_use_cc_common_link == 1:
+ experimental_use_cc_common_link = True
+ elif ctx.attr.experimental_use_cc_common_link == -1:
+ experimental_use_cc_common_link = toolchain._experimental_use_cc_common_link
+
dep_info, build_info, linkstamps = collect_deps(
deps = crate_info.deps,
proc_macro_deps = crate_info.proc_macro_deps,
@@ -865,8 +1054,23 @@
dep_info = dep_info,
build_info = build_info,
stamp = stamp,
+ experimental_use_cc_common_link = experimental_use_cc_common_link,
)
+ # The types of rustc outputs to emit.
+ # If we build metadata, we need to keep the command line of the two invocations
+ # (rlib and rmeta) as similar as possible, otherwise rustc rejects the rmeta as
+ # a candidate.
+ # Because of that we need to add emit=metadata to both the rlib and rmeta invocation.
+ #
+ # When cc_common linking is enabled, emit a `.o` file, which is later
+ # passed to the cc_common.link action.
+ emit = ["dep-info", "link"]
+ if build_metadata:
+ emit.append("metadata")
+ if experimental_use_cc_common_link:
+ emit = ["obj"]
+
args, env_from_args = construct_arguments(
ctx = ctx,
attr = attr,
@@ -874,6 +1078,7 @@
toolchain = toolchain,
tool_path = toolchain.rustc.path,
cc_toolchain = cc_toolchain,
+ emit = emit,
feature_configuration = feature_configuration,
crate_info = crate_info,
dep_info = dep_info,
@@ -886,8 +1091,35 @@
build_flags_files = build_flags_files,
force_all_deps_direct = force_all_deps_direct,
stamp = stamp,
+ use_json_output = bool(build_metadata),
)
+ args_metadata = None
+ if build_metadata:
+ args_metadata, _ = construct_arguments(
+ ctx = ctx,
+ attr = attr,
+ file = ctx.file,
+ toolchain = toolchain,
+ tool_path = toolchain.rustc.path,
+ cc_toolchain = cc_toolchain,
+ emit = emit,
+ feature_configuration = feature_configuration,
+ crate_info = crate_info,
+ dep_info = dep_info,
+ linkstamp_outs = linkstamp_outs,
+ ambiguous_libs = ambiguous_libs,
+ output_hash = output_hash,
+ rust_flags = rust_flags,
+ out_dir = out_dir,
+ build_env_files = build_env_files,
+ build_flags_files = build_flags_files,
+ force_all_deps_direct = force_all_deps_direct,
+ stamp = stamp,
+ use_json_output = True,
+ build_metadata = True,
+ )
+
env = dict(ctx.configuration.default_shell_env)
env.update(env_from_args)
@@ -896,15 +1128,25 @@
else:
formatted_version = ""
+ # Declares the outputs of the rustc compile action.
+ # By default this is the binary output; if cc_common.link is used, this is
+ # the main `.o` file (`output_o` below).
outputs = [crate_info.output]
+ # The `.o` output file, only used for linking via cc_common.link.
+ output_o = None
+ if experimental_use_cc_common_link:
+ obj_ext = ".o"
+ output_o = ctx.actions.declare_file(crate_info.name + obj_ext, sibling = crate_info.output)
+ outputs = [output_o]
+
# For a cdylib that might be added as a dependency to a cc_* target on Windows, it is important to include the
# interface library that rustc generates in the output files.
interface_library = None
if toolchain.os == "windows" and crate_info.type == "cdylib":
# Rustc generates the import library with a `.dll.lib` extension rather than the usual `.lib` one that msvc
# expects (see https://github.com/rust-lang/rust/pull/29520 for more context).
- interface_library = ctx.actions.declare_file(crate_info.output.basename + ".lib")
+ interface_library = ctx.actions.declare_file(crate_info.output.basename + ".lib", sibling = crate_info.output)
outputs.append(interface_library)
# The action might generate extra output that we don't want to include in the `DefaultInfo` files.
@@ -916,14 +1158,13 @@
dsym_folder = None
if crate_info.type in ("cdylib", "bin") and not crate_info.is_test:
if toolchain.os == "windows":
- pdb_file = ctx.actions.declare_file(crate_info.output.basename[:-len(crate_info.output.extension)] + "pdb")
+ pdb_file = ctx.actions.declare_file(crate_info.output.basename[:-len(crate_info.output.extension)] + "pdb", sibling = crate_info.output)
action_outputs.append(pdb_file)
elif toolchain.os == "darwin":
- dsym_folder = ctx.actions.declare_directory(crate_info.output.basename + ".dSYM")
+ dsym_folder = ctx.actions.declare_directory(crate_info.output.basename + ".dSYM", sibling = crate_info.output)
action_outputs.append(dsym_folder)
- # This uses startswith as on windows the basename will be process_wrapper_fake.exe.
- if not ctx.executable._process_wrapper.basename.startswith("process_wrapper_fake"):
+ if ctx.executable._process_wrapper:
# Run as normal
ctx.actions.run(
executable = ctx.executable._process_wrapper,
@@ -939,10 +1180,25 @@
len(crate_info.srcs.to_list()),
),
)
+ if args_metadata:
+ ctx.actions.run(
+ executable = ctx.executable._process_wrapper,
+ inputs = compile_inputs,
+ outputs = [build_metadata],
+ env = env,
+ arguments = args_metadata.all,
+ mnemonic = "RustcMetadata",
+ progress_message = "Compiling Rust metadata {} {}{} ({} files)".format(
+ crate_info.type,
+ ctx.label.name,
+ formatted_version,
+ len(crate_info.srcs.to_list()),
+ ),
+ )
else:
# Run without process_wrapper
- if build_env_files or build_flags_files or stamp:
- fail("build_env_files, build_flags_files, stamp are not supported if use_process_wrapper is False")
+ if build_env_files or build_flags_files or stamp or build_metadata:
+ fail("build_env_files, build_flags_files, stamp, build_metadata are not supported when building without process_wrapper")
ctx.actions.run(
executable = toolchain.rustc,
inputs = compile_inputs,
@@ -958,8 +1214,58 @@
),
)
+ if experimental_use_cc_common_link:
+ # Wrap the main `.o` file into a compilation output suitable for
+ # cc_common.link. The main `.o` file is useful in both PIC and non-PIC
+ # modes.
+ compilation_outputs = cc_common.create_compilation_outputs(
+ objects = depset([output_o]),
+ pic_objects = depset([output_o]),
+ )
+
+ # Collect the linking contexts of the standard library and dependencies.
+ linking_contexts = [toolchain.libstd_and_allocator_ccinfo.linking_context, toolchain.stdlib_linkflags.linking_context]
+
+ for dep in crate_info.deps.to_list():
+ if dep.cc_info:
+ linking_contexts.append(dep.cc_info.linking_context)
+
+ # In the cc_common.link action we need to pass the name of the final
+ # binary (output) relative to the package of this target.
+ # We compute it by stripping the path to the package directory,
+ # which is a prefix of the path of `crate_info.output`.
+
+ # The path to the package dir, including a trailing "/".
+ package_dir = ctx.bin_dir.path + "/"
+ if ctx.label.workspace_root:
+ package_dir = package_dir + ctx.label.workspace_root + "/"
+ if ctx.label.package:
+ package_dir = package_dir + ctx.label.package + "/"
+
+ if not crate_info.output.path.startswith(package_dir):
+ fail("The package dir path {} should be a prefix of the crate_info.output.path {}", package_dir, crate_info.output.path)
+
+ output_relative_to_package = crate_info.output.path[len(package_dir):]
+
+ cc_common.link(
+ actions = ctx.actions,
+ feature_configuration = feature_configuration,
+ cc_toolchain = cc_toolchain,
+ linking_contexts = linking_contexts,
+ compilation_outputs = compilation_outputs,
+ name = output_relative_to_package,
+ grep_includes = ctx.file._grep_includes,
+ stamp = ctx.attr.stamp,
+ )
+
+ outputs = [crate_info.output]
+
+ coverage_runfiles = []
+ if toolchain.llvm_cov and ctx.configuration.coverage_enabled and crate_info.is_test:
+ coverage_runfiles = [toolchain.llvm_cov, toolchain.llvm_profdata]
+
runfiles = ctx.runfiles(
- files = getattr(ctx.files, "data", []),
+ files = getattr(ctx.files, "data", []) + coverage_runfiles,
collect_data = True,
)
@@ -968,15 +1274,29 @@
out_binary = getattr(attr, "out_binary", False)
providers = [
- crate_info,
- dep_info,
DefaultInfo(
# nb. This field is required for cc_library to depend on our output.
files = depset(outputs),
runfiles = runfiles,
executable = crate_info.output if crate_info.type == "bin" or crate_info.is_test or out_binary else None,
),
+ coverage_common.instrumented_files_info(
+ ctx,
+ dependency_attributes = ["deps", "crate"],
+ extensions = ["rs"],
+ source_attributes = ["srcs"],
+ ),
]
+
+ if crate_info.type in ["staticlib", "cdylib"]:
+ # These rules are not supposed to be depended on by other rust targets, and
+ # as such they shouldn't provide a CrateInfo. However, one may still want to
+ # write a rust_test for them, so we provide the CrateInfo wrapped in a provider
+ # that rust_test understands.
+ providers.extend([rust_common.test_crate_info(crate = crate_info), dep_info])
+ else:
+ providers.extend([crate_info, dep_info])
+
if toolchain.target_arch != "wasm32":
providers += establish_cc_info(ctx, attr, crate_info, toolchain, cc_toolchain, feature_configuration, interface_library)
if pdb_file:
@@ -989,6 +1309,21 @@
def _is_dylib(dep):
return not bool(dep.static_library or dep.pic_static_library)
+def _collect_nonstatic_linker_inputs(cc_info):
+ shared_linker_inputs = []
+ for linker_input in cc_info.linking_context.linker_inputs.to_list():
+ dylibs = [
+ lib
+ for lib in linker_input.libraries
+ if _is_dylib(lib)
+ ]
+ if dylibs:
+ shared_linker_inputs.append(cc_common.create_linker_input(
+ owner = linker_input.owner,
+ libraries = depset(dylibs),
+ ))
+ return shared_linker_inputs
+
def establish_cc_info(ctx, attr, crate_info, toolchain, cc_toolchain, feature_configuration, interface_library):
"""If the produced crate is suitable yield a CcInfo to allow for interop with cc rules
@@ -1067,9 +1402,20 @@
CcInfo(linking_context = linking_context),
toolchain.stdlib_linkflags,
]
+
for dep in getattr(attr, "deps", []):
if CcInfo in dep:
- cc_infos.append(dep[CcInfo])
+ # A Rust staticlib or shared library doesn't need to propagate linker inputs
+ # of its dependencies, except for shared libraries.
+ if crate_info.type in ["cdylib", "staticlib"]:
+ shared_linker_inputs = _collect_nonstatic_linker_inputs(dep[CcInfo])
+ if shared_linker_inputs:
+ linking_context = cc_common.create_linking_context(
+ linker_inputs = depset(shared_linker_inputs),
+ )
+ cc_infos.append(CcInfo(linking_context = linking_context))
+ else:
+ cc_infos.append(dep[CcInfo])
if crate_info.type in ("rlib", "lib") and toolchain.libstd_and_allocator_ccinfo:
# TODO: if we already have an rlib in our deps, we could skip this
@@ -1138,7 +1484,8 @@
"""
# Windows has no rpath equivalent, so always return an empty depset.
- if toolchain.os == "windows":
+ # Fuchsia assembles shared libraries during packaging.
+ if toolchain.os == "windows" or toolchain.os == "fuchsia":
return depset([])
dylibs = [
@@ -1180,7 +1527,7 @@
dirs[f.dirname] = None
return dirs.keys()
-def add_crate_link_flags(args, dep_info, force_all_deps_direct = False):
+def add_crate_link_flags(args, dep_info, force_all_deps_direct = False, use_metadata = False):
"""Adds link flags to an Args object reference
Args:
@@ -1188,22 +1535,19 @@
dep_info (DepInfo): The current target's dependency info
force_all_deps_direct (bool, optional): Whether to pass the transitive rlibs with --extern
to the commandline as opposed to -L.
+ use_metadata (bool, optional): Build command line arugments using metadata for crates that provide it.
"""
- if force_all_deps_direct:
- args.add_all(
- depset(
- transitive = [
- dep_info.direct_crates,
- dep_info.transitive_crates,
- ],
- ),
- uniquify = True,
- map_each = _crate_to_link_flag,
- )
- else:
- # nb. Direct crates are linked via --extern regardless of their crate_type
- args.add_all(dep_info.direct_crates, map_each = _crate_to_link_flag)
+ direct_crates = depset(
+ transitive = [
+ dep_info.direct_crates,
+ dep_info.transitive_crates,
+ ],
+ ) if force_all_deps_direct else dep_info.direct_crates
+
+ crate_to_link_flags = _crate_to_link_flag_metadata if use_metadata else _crate_to_link_flag
+ args.add_all(direct_crates, uniquify = True, map_each = crate_to_link_flags)
+
args.add_all(
dep_info.transitive_crates,
map_each = _get_crate_dirname,
@@ -1211,6 +1555,29 @@
format_each = "-Ldependency=%s",
)
+def _crate_to_link_flag_metadata(crate):
+ """A helper macro used by `add_crate_link_flags` for adding crate link flags to a Arg object
+
+ Args:
+ crate (CrateInfo|AliasableDepInfo): A CrateInfo or an AliasableDepInfo provider
+
+ Returns:
+ list: Link flags for the given provider
+ """
+
+ # This is AliasableDepInfo, we should use the alias as a crate name
+ if hasattr(crate, "dep"):
+ name = crate.name
+ crate_info = crate.dep
+ else:
+ name = crate.name
+ crate_info = crate
+
+ lib_or_meta = crate_info.metadata
+ if not crate_info.metadata:
+ lib_or_meta = crate_info.output
+ return ["--extern={}={}".format(name, lib_or_meta.path)]
+
def _crate_to_link_flag(crate):
"""A helper macro used by `add_crate_link_flags` for adding crate link flags to a Arg object
@@ -1241,13 +1608,48 @@
"""
return crate.output.dirname
-def _portable_link_flags(lib, use_pic, ambiguous_libs):
+def _portable_link_flags(lib, use_pic, ambiguous_libs, get_lib_name, for_windows):
artifact = get_preferred_artifact(lib, use_pic)
if ambiguous_libs and artifact.path in ambiguous_libs:
artifact = ambiguous_libs[artifact.path]
if lib.static_library or lib.pic_static_library:
+ # To ensure appropriate linker library argument order, in the presence
+ # of both native libraries that depend on rlibs and rlibs that depend
+ # on native libraries, we use an approach where we "sandwich" the
+ # rust libraries between two similar sections of all of native
+ # libraries:
+ # n1 n2 ... r1 r2 ... n1 n2 ...
+ # A B C
+ # This way any dependency from a native library to a rust library
+ # is resolved from A to B, and any dependency from a rust library to
+ # a native one is resolved from B to C.
+ # The question of resolving dependencies from a native library from A
+ # to any rust library is addressed in a different place, where we
+ # create symlinks to the rlibs, pretending they are native libraries,
+ # and adding references to these symlinks in the native section A.
+ # We rely in the behavior of -Clink-arg to put the linker args
+ # at the end of the linker invocation constructed by rustc.
+
+ # We skip adding `-Clink-arg=-l` for libstd and libtest from the standard library, as
+ # these two libraries are present both as an `.rlib` and a `.so` format.
+ # On linux, Rustc adds a -Bdynamic to the linker command line before the libraries specified
+ # with `-Clink-arg`, which leads to us linking against the `.so`s but not putting the
+ # corresponding value to the runtime library search paths, which results in a
+ # "cannot open shared object file: No such file or directory" error at exectuion time.
+ # We can fix this by adding a `-Clink-arg=-Bstatic` on linux, but we don't have that option for
+ # macos. The proper solution for this issue would be to remove `libtest-{hash}.so` and `libstd-{hash}.so`
+ # from the toolchain. However, it is not enough to change the toolchain's `rust_std_{...}` filegroups
+ # here: https://github.com/bazelbuild/rules_rust/blob/a9d5d894ad801002d007b858efd154e503796b9f/rust/private/repository_utils.bzl#L144
+ # because rustc manages to escape the sandbox and still finds them at linking time.
+ # We need to modify the repository rules to erase those files completely.
+ if "lib/rustlib" in artifact.path and (
+ artifact.basename.startswith("libtest-") or artifact.basename.startswith("libstd-") or
+ artifact.basename.startswith("test-") or artifact.basename.startswith("std-")
+ ):
+ return ["-lstatic=%s" % get_lib_name(artifact)]
return [
"-lstatic=%s" % get_lib_name(artifact),
+ "-Clink-arg=-l%s" % (get_lib_name(artifact) if not for_windows else artifact.basename),
]
elif _is_dylib(lib):
return [
@@ -1263,7 +1665,7 @@
if lib.alwayslink:
ret.extend(["-C", "link-arg=/WHOLEARCHIVE:%s" % get_preferred_artifact(lib, use_pic).path])
else:
- ret.extend(_portable_link_flags(lib, use_pic, ambiguous_libs))
+ ret.extend(_portable_link_flags(lib, use_pic, ambiguous_libs, get_lib_name_for_windows, for_windows = True))
return ret
def _make_link_flags_darwin(linker_input_and_use_pic_and_ambiguous_libs):
@@ -1276,7 +1678,7 @@
("link-arg=-Wl,-force_load,%s" % get_preferred_artifact(lib, use_pic).path),
])
else:
- ret.extend(_portable_link_flags(lib, use_pic, ambiguous_libs))
+ ret.extend(_portable_link_flags(lib, use_pic, ambiguous_libs, get_lib_name_default, for_windows = False))
return ret
def _make_link_flags_default(linker_input_and_use_pic_and_ambiguous_libs):
@@ -1293,7 +1695,7 @@
"link-arg=-Wl,--no-whole-archive",
])
else:
- ret.extend(_portable_link_flags(lib, use_pic, ambiguous_libs))
+ ret.extend(_portable_link_flags(lib, use_pic, ambiguous_libs, get_lib_name_default, for_windows = False))
return ret
def _libraries_dirnames(linker_input_and_use_pic_and_ambiguous_libs):
@@ -1314,7 +1716,6 @@
toolchain (rust_toolchain): The current `rust_toolchain`
cc_toolchain (CcToolchainInfo): The current `cc_toolchain`
feature_configuration (FeatureConfiguration): feature configuration to use with cc_toolchain
-
"""
if crate_type in ["lib", "rlib"]:
return
@@ -1323,10 +1724,13 @@
if toolchain.os == "windows":
make_link_flags = _make_link_flags_windows
+ get_lib_name = get_lib_name_for_windows
elif toolchain.os.startswith("mac") or toolchain.os.startswith("darwin"):
make_link_flags = _make_link_flags_darwin
+ get_lib_name = get_lib_name_default
else:
make_link_flags = _make_link_flags_default
+ get_lib_name = get_lib_name_default
# TODO(hlopko): Remove depset flattening by using lambdas once we are on >=Bazel 5.0
args_and_pic_and_ambiguous_libs = [(arg, use_pic, ambiguous_libs) for arg in dep_info.transitive_noncrates.to_list()]
@@ -1422,6 +1826,18 @@
build_setting = config.string_list(flag = True),
)
+def _extra_rustc_flag_impl(ctx):
+ return ExtraRustcFlagsInfo(extra_rustc_flags = [f for f in ctx.build_setting_value if f != ""])
+
+extra_rustc_flag = rule(
+ doc = (
+ "Add additional rustc_flag from the command line with `--@rules_rust//:extra_rustc_flag`. " +
+ "Multiple uses are accumulated and appended after the extra_rustc_flags."
+ ),
+ implementation = _extra_rustc_flag_impl,
+ build_setting = config.string(flag = True, allow_multiple = True),
+)
+
def _extra_exec_rustc_flags_impl(ctx):
return ExtraExecRustcFlagsInfo(extra_exec_rustc_flags = ctx.build_setting_value)
@@ -1434,3 +1850,15 @@
implementation = _extra_exec_rustc_flags_impl,
build_setting = config.string_list(flag = True),
)
+
+def _extra_exec_rustc_flag_impl(ctx):
+ return ExtraExecRustcFlagsInfo(extra_exec_rustc_flags = [f for f in ctx.build_setting_value if f != ""])
+
+extra_exec_rustc_flag = rule(
+ doc = (
+ "Add additional rustc_flags in the exec configuration from the command line with `--@rules_rust//:extra_exec_rustc_flag`. " +
+ "Multiple uses are accumulated and appended after the extra_exec_rustc_flags."
+ ),
+ implementation = _extra_exec_rustc_flag_impl,
+ build_setting = config.string(flag = True, allow_multiple = True),
+)
diff --git a/rust/private/rustdoc.bzl b/rust/private/rustdoc.bzl
index 86c2acd..ba3a12c 100644
--- a/rust/private/rustdoc.bzl
+++ b/rust/private/rustdoc.bzl
@@ -37,8 +37,10 @@
aliases = crate_info.aliases,
# This crate info should have no output
output = None,
+ metadata = None,
edition = crate_info.edition,
rustc_env = crate_info.rustc_env,
+ rustc_env_files = crate_info.rustc_env_files,
is_test = crate_info.is_test,
compile_data = crate_info.compile_data,
)
@@ -90,6 +92,8 @@
crate_info = crate_info,
dep_info = dep_info,
build_info = build_info,
+ # If this is a rustdoc test, we need to depend on rlibs rather than .rmeta.
+ force_depend_on_objects = is_test,
)
# Since this crate is not actually producing the output described by the
@@ -118,6 +122,7 @@
emit = [],
remap_path_prefix = None,
force_link = True,
+ force_depend_on_objects = is_test,
)
# Because rustdoc tests compile tests outside of the sandbox, the sysroot
@@ -281,9 +286,19 @@
doc = "CSS files to include via `<link>` in a rendered Markdown file.",
allow_files = [".css"],
),
+ "rustc_flags": attr.string_list(
+ doc = dedent("""\
+ List of compiler flags passed to `rustc`.
+
+ These strings are subject to Make variable expansion for predefined
+ source/output path variables like `$location`, `$execpath`, and
+ `$rootpath`. This expansion is useful if you wish to pass a generated
+ file of arguments to rustc: `@$(location //package:target)`.
+ """),
+ ),
"_cc_toolchain": attr.label(
doc = "In order to use find_cpp_toolchain, you must define the '_cc_toolchain' attribute on your rule or aspect.",
- default = "@bazel_tools//tools/cpp:current_cc_toolchain",
+ default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"),
),
"_dir_zipper": attr.label(
doc = "A tool that orchestrates the creation of zip archives for rustdoc outputs.",
@@ -311,7 +326,7 @@
"rust_doc_zip": "%{name}.zip",
},
toolchains = [
- str(Label("//rust:toolchain")),
+ str(Label("//rust:toolchain_type")),
"@bazel_tools//tools/cpp:toolchain_type",
],
incompatible_use_toolchain_transition = True,
diff --git a/rust/private/rustdoc_test.bzl b/rust/private/rustdoc_test.bzl
index 663c65f..525838e 100644
--- a/rust/private/rustdoc_test.bzl
+++ b/rust/private/rustdoc_test.bzl
@@ -15,10 +15,11 @@
"""Rules for performing `rustdoc --test` on Bazel built crates"""
load("//rust/private:common.bzl", "rust_common")
+load("//rust/private:providers.bzl", "CrateInfo")
load("//rust/private:rustdoc.bzl", "rustdoc_compile_action")
-load("//rust/private:utils.bzl", "dedent", "find_toolchain")
+load("//rust/private:utils.bzl", "dedent", "find_toolchain", "transform_deps")
-def _construct_writer_arguments(ctx, test_runner, action, crate_info):
+def _construct_writer_arguments(ctx, test_runner, opt_test_params, action, crate_info):
"""Construct arguments and environment variables specific to `rustdoc_test_writer`.
This is largely solving for the fact that tests run from a runfiles directory
@@ -28,6 +29,7 @@
Args:
ctx (ctx): The rule's context object.
test_runner (File): The test_runner output file declared by `rustdoc_test`.
+ opt_test_params (File): An output file we can optionally use to store params for `rustdoc`.
action (struct): Action arguments generated by `rustdoc_compile_action`.
crate_info (CrateInfo): The provider of the crate who's docs are being tested.
@@ -42,6 +44,9 @@
# Track the output path where the test writer should write the test
writer_args.add("--output={}".format(test_runner.path))
+ # Track where the test writer should move "spilled" Args to
+ writer_args.add("--optional_test_params={}".format(opt_test_params.path))
+
# Track what environment variables should be written to the test runner
writer_args.add("--action_env=DEVELOPER_DIR")
writer_args.add("--action_env=PATHEXT")
@@ -55,19 +60,29 @@
# files. To ensure rustdoc can find the appropriate dependencies, the
# file roots are identified and tracked for each dependency so it can be
# stripped from the test runner.
+
+ # Collect and dedupe all of the file roots in a list before appending
+ # them to args to prevent generating a large amount of identical args
+ roots = []
for dep in crate_info.deps.to_list():
dep_crate_info = getattr(dep, "crate_info", None)
dep_dep_info = getattr(dep, "dep_info", None)
if dep_crate_info:
root = dep_crate_info.output.root.path
- writer_args.add("--strip_substring={}/".format(root))
+ if not root in roots:
+ roots.append(root)
if dep_dep_info:
for direct_dep in dep_dep_info.direct_crates.to_list():
root = direct_dep.dep.output.root.path
- writer_args.add("--strip_substring={}/".format(root))
+ if not root in roots:
+ roots.append(root)
for transitive_dep in dep_dep_info.transitive_crates.to_list():
root = transitive_dep.output.root.path
- writer_args.add("--strip_substring={}/".format(root))
+ if not root in roots:
+ roots.append(root)
+
+ for root in roots:
+ writer_args.add("--strip_substring={}/".format(root))
# Indicate that the rustdoc_test args are over.
writer_args.add("--")
@@ -90,14 +105,38 @@
toolchain = find_toolchain(ctx)
- crate = ctx.attr.crate
- crate_info = crate[rust_common.crate_info]
+ crate = ctx.attr.crate[rust_common.crate_info]
+ deps = transform_deps(ctx.attr.deps)
+
+ crate_info = rust_common.create_crate_info(
+ name = crate.name,
+ type = crate.type,
+ root = crate.root,
+ srcs = crate.srcs,
+ deps = depset(deps, transitive = [crate.deps]),
+ proc_macro_deps = crate.proc_macro_deps,
+ aliases = {},
+ output = crate.output,
+ edition = crate.edition,
+ rustc_env = crate.rustc_env,
+ rustc_env_files = crate.rustc_env_files,
+ is_test = True,
+ compile_data = crate.compile_data,
+ wrapped_crate_type = crate.type,
+ owner = ctx.label,
+ )
if toolchain.os == "windows":
test_runner = ctx.actions.declare_file(ctx.label.name + ".rustdoc_test.bat")
else:
test_runner = ctx.actions.declare_file(ctx.label.name + ".rustdoc_test.sh")
+ # Bazel will auto-magically spill params to a file, if they are too many for a given OSes shell
+ # (e.g. Windows ~32k, Linux ~2M). The executable script (aka test_runner) that gets generated,
+ # is run from the runfiles, which is separate from the params_file Bazel generates. To handle
+ # this case, we declare our own params file, that the test_writer will populate, if necessary
+ opt_test_params = ctx.actions.declare_file(ctx.label.name + ".rustdoc_opt_params", sibling = test_runner)
+
# Add the current crate as an extern for the compile action
rustdoc_flags = [
"--extern",
@@ -118,6 +157,7 @@
writer_args, env = _construct_writer_arguments(
ctx = ctx,
test_runner = test_runner,
+ opt_test_params = opt_test_params,
action = action,
crate_info = crate_info,
)
@@ -127,18 +167,18 @@
ctx.actions.run(
mnemonic = "RustdocTestWriter",
- progress_message = "Generating Rustdoc test runner for {}".format(crate.label),
+ progress_message = "Generating Rustdoc test runner for {}".format(ctx.attr.crate.label),
executable = ctx.executable._test_writer,
inputs = action.inputs,
tools = tools,
arguments = [writer_args] + action.arguments,
env = action.env,
- outputs = [test_runner],
+ outputs = [test_runner, opt_test_params],
)
return [DefaultInfo(
files = depset([test_runner]),
- runfiles = ctx.runfiles(files = tools, transitive_files = action.inputs),
+ runfiles = ctx.runfiles(files = tools + [opt_test_params], transitive_files = action.inputs),
executable = test_runner,
)]
@@ -154,13 +194,22 @@
providers = [rust_common.crate_info],
mandatory = True,
),
+ "deps": attr.label_list(
+ doc = dedent("""\
+ List of other libraries to be linked to this library target.
+
+ These can be either other `rust_library` targets or `cc_library` targets if
+ linking a native library.
+ """),
+ providers = [CrateInfo, CcInfo],
+ ),
"_cc_toolchain": attr.label(
doc = (
"In order to use find_cc_toolchain, your rule has to depend " +
"on C++ toolchain. See @rules_cc//cc:find_cc_toolchain.bzl " +
"docs for details."
),
- default = "@bazel_tools//tools/cpp:current_cc_toolchain",
+ default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"),
),
"_process_wrapper": attr.label(
doc = "A process wrapper for running rustdoc on all platforms",
@@ -179,7 +228,7 @@
fragments = ["cpp"],
host_fragments = ["cpp"],
toolchains = [
- str(Label("//rust:toolchain")),
+ str(Label("//rust:toolchain_type")),
"@bazel_tools//tools/cpp:toolchain_type",
],
incompatible_use_toolchain_transition = True,
diff --git a/rust/private/rustfmt.bzl b/rust/private/rustfmt.bzl
index 9c59f48..db5ef43 100644
--- a/rust/private/rustfmt.bzl
+++ b/rust/private/rustfmt.bzl
@@ -20,9 +20,17 @@
if target.label.workspace_root.startswith("external"):
return []
- # Targets annotated with `norustfmt` will not be formatted
- if aspect_ctx and "norustfmt" in aspect_ctx.rule.attr.tags:
- return []
+ if aspect_ctx:
+ # Targets with specifc tags will not be formatted
+ ignore_tags = [
+ "no-format",
+ "no-rustfmt",
+ "norustfmt",
+ ]
+
+ for tag in ignore_tags:
+ if tag in aspect_ctx.rule.attr.tags:
+ return []
crate_info = target[rust_common.crate_info]
@@ -110,8 +118,8 @@
[cs]: https://rust-lang.github.io/rustfmt/
This aspect is executed on any target which provides the `CrateInfo` provider. However
-users may tag a target with `norustfmt` to have it skipped. Additionally, generated
-source files are also ignored by this aspect.
+users may tag a target with `no-rustfmt` or `no-format` to have it skipped. Additionally,
+generated source files are also ignored by this aspect.
""",
attrs = {
"_config": attr.label(
@@ -130,7 +138,7 @@
fragments = ["cpp"],
host_fragments = ["cpp"],
toolchains = [
- str(Label("//rust:toolchain")),
+ str(Label("//rust:toolchain_type")),
],
)
@@ -138,9 +146,10 @@
# The executable of a test target must be the output of an action in
# the rule implementation. This file is simply a symlink to the real
# rustfmt test runner.
+ is_windows = ctx.executable._runner.extension == ".exe"
runner = ctx.actions.declare_file("{}{}".format(
ctx.label.name,
- ctx.executable._runner.extension,
+ ".exe" if is_windows else "",
))
ctx.actions.symlink(
@@ -149,22 +158,33 @@
is_executable = True,
)
- manifests = [target[OutputGroupInfo].rustfmt_manifest for target in ctx.attr.targets]
+ manifests = depset(transitive = [target[OutputGroupInfo].rustfmt_manifest for target in ctx.attr.targets])
srcs = [depset(_find_rustfmtable_srcs(target)) for target in ctx.attr.targets]
runfiles = ctx.runfiles(
- transitive_files = depset(transitive = manifests + srcs),
+ transitive_files = depset(transitive = srcs + [manifests]),
)
runfiles = runfiles.merge(
ctx.attr._runner[DefaultInfo].default_runfiles,
)
- return [DefaultInfo(
- files = depset([runner]),
- runfiles = runfiles,
- executable = runner,
- )]
+ path_env_sep = ";" if is_windows else ":"
+
+ return [
+ DefaultInfo(
+ files = depset([runner]),
+ runfiles = runfiles,
+ executable = runner,
+ ),
+ testing.TestEnvironment({
+ "RUSTFMT_MANIFESTS": path_env_sep.join([
+ manifest.short_path
+ for manifest in sorted(manifests.to_list())
+ ]),
+ "RUST_BACKTRACE": "1",
+ }),
+ ]
rustfmt_test = rule(
implementation = _rustfmt_test_impl,
@@ -184,3 +204,22 @@
},
test = True,
)
+
+def _rustfmt_workspace_name_impl(ctx):
+ output = ctx.actions.declare_file(ctx.label.name)
+
+ ctx.actions.write(
+ output = output,
+ content = "RUSTFMT_WORKSPACE={}".format(
+ ctx.workspace_name,
+ ),
+ )
+
+ return [DefaultInfo(
+ files = depset([output]),
+ )]
+
+rustfmt_workspace_name = rule(
+ implementation = _rustfmt_workspace_name_impl,
+ doc = "A rule for detecting the workspace name for Rustfmt runfiles.",
+)
diff --git a/rust/private/toolchain_utils.bzl b/rust/private/toolchain_utils.bzl
index fcd4fb7..13c94ac 100644
--- a/rust/private/toolchain_utils.bzl
+++ b/rust/private/toolchain_utils.bzl
@@ -1,7 +1,7 @@
"""A module defining toolchain utilities"""
def _toolchain_files_impl(ctx):
- toolchain = ctx.toolchains[str(Label("//rust:toolchain"))]
+ toolchain = ctx.toolchains[str(Label("//rust:toolchain_type"))]
runfiles = None
if ctx.attr.tool == "cargo":
@@ -42,8 +42,6 @@
)
elif ctx.attr.tool == "rustc_lib":
files = toolchain.rustc_lib
- elif ctx.attr.tool == "rustc_srcs":
- files = toolchain.rustc_srcs.files
elif ctx.attr.tool == "rust_std" or ctx.attr.tool == "rust_stdlib" or ctx.attr.tool == "rust_lib":
files = toolchain.rust_std
else:
@@ -67,7 +65,6 @@
"rust_std",
"rust_stdlib",
"rustc_lib",
- "rustc_srcs",
"rustc",
"rustdoc",
"rustfmt",
@@ -76,7 +73,32 @@
),
},
toolchains = [
- str(Label("//rust:toolchain")),
+ str(Label("//rust:toolchain_type")),
+ ],
+ incompatible_use_toolchain_transition = True,
+)
+
+def _current_rust_toolchain_impl(ctx):
+ toolchain = ctx.toolchains[str(Label("@rules_rust//rust:toolchain_type"))]
+
+ return [
+ toolchain,
+ toolchain.make_variables,
+ DefaultInfo(
+ files = depset([
+ toolchain.rustc,
+ toolchain.rust_doc,
+ toolchain.rustfmt,
+ toolchain.cargo,
+ ]),
+ ),
+ ]
+
+current_rust_toolchain = rule(
+ doc = "A rule for exposing the current registered `rust_toolchain`.",
+ implementation = _current_rust_toolchain_impl,
+ toolchains = [
+ str(Label("@rules_rust//rust:toolchain_type")),
],
incompatible_use_toolchain_transition = True,
)
diff --git a/rust/private/transitions.bzl b/rust/private/transitions.bzl
index cd28efd..23336f4 100644
--- a/rust/private/transitions.bzl
+++ b/rust/private/transitions.bzl
@@ -46,63 +46,19 @@
outputs = ["@rules_rust//rust/settings:use_real_import_macro"],
)
-def _with_import_macro_bootstrapping_mode_impl(ctx):
- target = ctx.attr.target[0]
- return [target[rust_common.crate_info], target[rust_common.dep_info]]
+def _alias_with_import_macro_bootstrapping_mode_impl(ctx):
+ actual = ctx.attr.actual[0]
+ return [actual[rust_common.crate_info], actual[rust_common.dep_info]]
-with_import_macro_bootstrapping_mode = rule(
- implementation = _with_import_macro_bootstrapping_mode_impl,
+alias_with_import_macro_bootstrapping_mode = rule(
+ implementation = _alias_with_import_macro_bootstrapping_mode_impl,
+ doc = "Alias-like rule to build the `actual` with `use_real_import_macro` setting disabled. Not to be used outside of the import macro bootstrap.",
attrs = {
- "target": attr.label(
+ # Using `actual` so tooling such as rust analyzer aspect traverses the target.
+ "actual": attr.label(
+ doc = "The target this alias refers to.",
cfg = import_macro_dep_bootstrap_transition,
- allow_single_file = True,
mandatory = True,
- executable = False,
- ),
- "_allowlist_function_transition": attr.label(
- default = Label("//tools/allowlists/function_transition_allowlist"),
- ),
- },
-)
-
-def _without_process_wrapper_transition_impl(_settings, _attr):
- """This transition allows rust_* rules to invoke rustc without process_wrapper."""
- return {
- "//rust/settings:use_process_wrapper": False,
- }
-
-without_process_wrapper_transition = transition(
- implementation = _without_process_wrapper_transition_impl,
- inputs = [],
- outputs = ["//rust/settings:use_process_wrapper"],
-)
-
-def _without_process_wrapper_impl(ctx):
- executable = ctx.executable.target
- link_name = ctx.label.name
-
- # Append .exe if on windows
- if executable.extension:
- link_name = link_name + "." + executable.extension
- link = ctx.actions.declare_file(link_name)
- ctx.actions.symlink(
- output = link,
- target_file = executable,
- )
- return [
- DefaultInfo(
- executable = link,
- ),
- ]
-
-without_process_wrapper = rule(
- implementation = _without_process_wrapper_impl,
- attrs = {
- "target": attr.label(
- cfg = without_process_wrapper_transition,
- allow_single_file = True,
- mandatory = True,
- executable = True,
),
"_allowlist_function_transition": attr.label(
default = Label("//tools/allowlists/function_transition_allowlist"),
diff --git a/rust/private/utils.bzl b/rust/private/utils.bzl
index fa7318b..583dc6e 100644
--- a/rust/private/utils.bzl
+++ b/rust/private/utils.bzl
@@ -17,6 +17,14 @@
load("@bazel_tools//tools/cpp:toolchain_utils.bzl", find_rules_cc_toolchain = "find_cpp_toolchain")
load(":providers.bzl", "BuildInfo", "CrateInfo", "DepInfo", "DepVariantInfo")
+UNSUPPORTED_FEATURES = [
+ "thin_lto",
+ "module_maps",
+ "use_header_modules",
+ "fdo_instrument",
+ "fdo_optimize",
+]
+
def find_toolchain(ctx):
"""Finds the first rust toolchain that is configured.
@@ -26,7 +34,7 @@
Returns:
rust_toolchain: A Rust toolchain context.
"""
- return ctx.toolchains[Label("//rust:toolchain")]
+ return ctx.toolchains[Label("//rust:toolchain_type")]
def find_cc_toolchain(ctx):
"""Extracts a CcToolchain from the current target's context
@@ -43,7 +51,7 @@
ctx = ctx,
cc_toolchain = cc_toolchain,
requested_features = ctx.features,
- unsupported_features = ctx.disabled_features,
+ unsupported_features = UNSUPPORTED_FEATURES + ctx.disabled_features,
)
return cc_toolchain, feature_configuration
@@ -89,8 +97,8 @@
path_parts = path.split("/")
return [part for part in path_parts if part != "."]
-def get_lib_name(lib):
- """Returns the name of a library artifact, eg. libabc.a -> abc
+def get_lib_name_default(lib):
+ """Returns the name of a library artifact.
Args:
lib (File): A library file
@@ -122,6 +130,44 @@
else:
return libname
+# TODO: Could we remove this function in favor of a "windows" parameter in the
+# above function? It looks like currently lambdas cannot accept local parameters
+# so the following doesn't work:
+# args.add_all(
+# cc_toolchain.dynamic_runtime_lib(feature_configuration = feature_configuration),
+# map_each = lambda x: get_lib_name(x, for_windows = toolchain.os.startswith("windows)),
+# format_each = "-ldylib=%s",
+# )
+def get_lib_name_for_windows(lib):
+ """Returns the name of a library artifact for Windows builds.
+
+ Args:
+ lib (File): A library file
+
+ Returns:
+ str: The name of the library
+ """
+ # On macos and windows, dynamic/static libraries always end with the
+ # extension and potential versions will be before the extension, and should
+ # be part of the library name.
+ # On linux, the version usually comes after the extension.
+ # So regardless of the platform we want to find the extension and make
+ # everything left to it the library name.
+
+ # Search for the extension - starting from the right - by removing any
+ # trailing digit.
+ comps = lib.basename.split(".")
+ for comp in reversed(comps):
+ if comp.isdigit():
+ comps.pop()
+ else:
+ break
+
+ # The library name is now everything minus the extension.
+ libname = ".".join(comps[:-1])
+
+ return libname
+
def abs(value):
"""Returns the absolute value of a number.
@@ -162,8 +208,10 @@
File: Returns the first valid library type (only one is expected)
"""
if use_pic:
+ # Order consistent with https://github.com/bazelbuild/bazel/blob/815dfdabb7df31d4e99b6fc7616ff8e2f9385d98/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLinkingContext.java#L437.
return (
library_to_link.pic_static_library or
+ library_to_link.static_library or
library_to_link.interface_library or
library_to_link.dynamic_library
)
@@ -176,12 +224,12 @@
)
def _expand_location(ctx, env, data):
- """A trivial helper for `_expand_locations`
+ """A trivial helper for `expand_dict_value_locations` and `expand_list_element_locations`
Args:
ctx (ctx): The rule's context object
env (str): The value possibly containing location macros to expand.
- data (sequence of Targets): see `_expand_locations`
+ data (sequence of Targets): See one of the parent functions.
Returns:
string: The location-macro expanded version of the string.
@@ -189,8 +237,12 @@
for directive in ("$(execpath ", "$(location "):
if directive in env:
# build script runner will expand pwd to execroot for us
- env = env.replace(directive, "${pwd}/" + directive)
- return ctx.expand_location(env, data)
+ env = env.replace(directive, "$${pwd}/" + directive)
+ return ctx.expand_make_variables(
+ env,
+ ctx.expand_location(env, data),
+ {},
+ )
def expand_dict_value_locations(ctx, env, data):
"""Performs location-macro expansion on string values.
@@ -207,7 +259,9 @@
as compilation happens in a separate sandbox folder, so when it comes time
to read the file at runtime, the path is no longer valid.
- See [`expand_location`](https://docs.bazel.build/versions/main/skylark/lib/ctx.html#expand_location) for detailed documentation.
+ For detailed documentation, see:
+ - [`expand_location`](https://bazel.build/rules/lib/ctx#expand_location)
+ - [`expand_make_variables`](https://bazel.build/rules/lib/ctx#expand_make_variables)
Args:
ctx (ctx): The rule's context object
@@ -228,7 +282,9 @@
which process_wrapper and build_script_runner will expand at run time
to the absolute path.
- See [`expand_location`](https://docs.bazel.build/versions/main/skylark/lib/ctx.html#expand_location) for detailed documentation.
+ For detailed documentation, see:
+ - [`expand_location`](https://bazel.build/rules/lib/ctx#expand_location)
+ - [`expand_make_variables`](https://bazel.build/rules/lib/ctx#expand_make_variables)
Args:
ctx (ctx): The rule's context object
@@ -262,7 +318,9 @@
Returns:
str: The name of the crate for this target.
"""
- return name.replace("-", "_")
+ for illegal in ("-", "/"):
+ name = name.replace(illegal, "_")
+ return name
def _invalid_chars_in_crate_name(name):
"""Returns any invalid chars in the given crate name.
@@ -451,37 +509,37 @@
# the second element is an encoding of that char suitable for use in a crate
# name.
_encodings = (
- (":", "colon"),
- ("!", "bang"),
- ("%", "percent"),
- ("@", "at"),
+ (":", "x"),
+ ("!", "excl"),
+ ("%", "prc"),
+ ("@", "ao"),
("^", "caret"),
- ("`", "backtick"),
- (" ", "space"),
- ("\"", "quote"),
- ("#", "hash"),
- ("$", "dollar"),
- ("&", "ampersand"),
- ("'", "backslash"),
- ("(", "lparen"),
- (")", "rparen"),
- ("*", "star"),
- ("-", "dash"),
- ("+", "plus"),
- (",", "comma"),
- (";", "semicolon"),
- ("<", "langle"),
- ("=", "equal"),
- (">", "rangle"),
- ("?", "question"),
- ("[", "lbracket"),
- ("]", "rbracket"),
- ("{", "lbrace"),
- ("|", "pipe"),
- ("}", "rbrace"),
- ("~", "tilde"),
- ("/", "slash"),
- (".", "dot"),
+ ("`", "bt"),
+ (" ", "sp"),
+ ("\"", "dq"),
+ ("#", "octo"),
+ ("$", "dllr"),
+ ("&", "amp"),
+ ("'", "sq"),
+ ("(", "lp"),
+ (")", "rp"),
+ ("*", "astr"),
+ ("-", "d"),
+ ("+", "pl"),
+ (",", "cm"),
+ (";", "sm"),
+ ("<", "la"),
+ ("=", "eq"),
+ (">", "ra"),
+ ("?", "qm"),
+ ("[", "lbk"),
+ ("]", "rbk"),
+ ("{", "lbe"),
+ ("|", "pp"),
+ ("}", "rbe"),
+ ("~", "td"),
+ ("/", "y"),
+ (".", "pd"),
)
# For each of the above encodings, we generate two substitution rules: one that
@@ -489,15 +547,18 @@
# aren't clobbered by this translation, and one that does the encoding itself.
# We also include a rule that protects the clobbering-protection rules from
# getting clobbered.
-_substitutions = [("_quote", "_quotequote_")] + [
+_substitutions = [("_z", "_zz_")] + [
subst
for (pattern, replacement) in _encodings
for subst in (
- ("_{}_".format(replacement), "_quote{}_".format(replacement)),
+ ("_{}_".format(replacement), "_z{}_".format(replacement)),
(pattern, "_{}_".format(replacement)),
)
]
+# Expose the substitutions for testing only.
+substitutions_for_testing = _substitutions
+
def encode_label_as_crate_name(package, name):
"""Encodes the package and target names in a format suitable for a crate name.
@@ -508,8 +569,21 @@
Returns:
A string that encodes the package and target name, to be used as the crate's name.
"""
- full_name = package + ":" + name
- return _replace_all(full_name, _substitutions)
+ return _encode_raw_string(package + ":" + name)
+
+def _encode_raw_string(str):
+ """Encodes a string using the above encoding format.
+
+ Args:
+ str (string): The string to be encoded.
+
+ Returns:
+ An encoded version of the input string.
+ """
+ return _replace_all(str, _substitutions)
+
+# Expose the underlying encoding function for testing only.
+encode_raw_string_for_testing = _encode_raw_string
def decode_crate_name_as_label_for_testing(crate_name):
"""Decodes a crate_name that was encoded by encode_label_as_crate_name.
@@ -574,3 +648,25 @@
string = string[:pattern_start] + replacement + string[after_pattern:]
return string
+
+def can_build_metadata(toolchain, ctx, crate_type):
+ """Can we build metadata for this rust_library?
+
+ Args:
+ toolchain (toolchain): The rust toolchain
+ ctx (ctx): The rule's context object
+ crate_type (String): one of lib|rlib|dylib|staticlib|cdylib|proc-macro
+
+ Returns:
+ bool: whether we can build metadata for this rule.
+ """
+
+ # In order to enable pipelined compilation we require that:
+ # 1) The _pipelined_compilation flag is enabled,
+ # 2) the OS running the rule is something other than windows as we require sandboxing (for now),
+ # 3) process_wrapper is enabled (this is disabled when compiling process_wrapper itself),
+ # 4) the crate_type is rlib or lib.
+ return toolchain._pipelined_compilation and \
+ toolchain.os != "windows" and \
+ ctx.attr._process_wrapper and \
+ crate_type in ("rlib", "lib")