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")