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/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),
+)