Allow rust_binary targets to create a hermetic launcher.

BUG=b:291840720
TEST=portage/tools/run_tests.sh

Cq-Depend: 5626214
Change-Id: I14a3022a03d9862fdf9519fdde192f5ae3b94bcb
diff --git a/rust/private/rust.bzl b/rust/private/rust.bzl
index afe1f12..c842e18 100644
--- a/rust/private/rust.bzl
+++ b/rust/private/rust.bzl
@@ -14,6 +14,7 @@
 
 """Rust rule implementations"""
 
+load("@@//bazel/module_extensions/toolchains/hermetic_launcher:hermetic_launcher.bzl", "HERMETIC_ATTRS")
 load("@bazel_skylib//lib:paths.bzl", "paths")
 load("//rust/private:common.bzl", "COMMON_PROVIDERS", "rust_common")
 load("//rust/private:providers.bzl", "BuildInfo")
@@ -1061,7 +1062,7 @@
 rust_binary = rule(
     implementation = _rust_binary_impl,
     provides = COMMON_PROVIDERS,
-    attrs = dict(_common_attrs.items() + _rust_binary_attrs.items() + {
+    attrs = dict(_common_attrs.items() + _rust_binary_attrs.items() + HERMETIC_ATTRS.items() + {
         "platform": attr.label(
             doc = "Optional platform to transition the binary to.",
             default = None,
@@ -1069,6 +1070,7 @@
         "_allowlist_function_transition": attr.label(
             default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
         ),
+        "use_hermetic_launcher": attr.label(default = "@@//bazel/module_extensions/toolchains/rust:use_hermetic_launcher"),
     }.items()),
     executable = True,
     fragments = ["cpp"],
diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl
index 98806f1..b025668 100644
--- a/rust/private/rustc.bzl
+++ b/rust/private/rustc.bzl
@@ -14,6 +14,7 @@
 
 """Functionality for constructing actions that invoke the Rust compiler"""
 
+load("@@//bazel/module_extensions/toolchains/hermetic_launcher:hermetic_launcher.bzl", "hermetic_defaultinfo")
 load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo")
 load(
     "@bazel_tools//tools/build_defs/cc:action_names.bzl",
@@ -1143,6 +1144,18 @@
             - (DepInfo): The transitive dependencies of this crate.
             - (DefaultInfo): The output file for this crate, and its runfiles.
     """
+    use_hermetic_launcher = hasattr(ctx.attr, "use_hermetic_launcher") and \
+                            ctx.attr.use_hermetic_launcher[BuildSettingInfo].value and \
+                            crate_info_dict and \
+                            "output" in crate_info_dict
+
+    if use_hermetic_launcher:
+        hermetic_wrapper = crate_info_dict["output"]
+        crate_info_dict["output"] = ctx.actions.declare_file(
+            "_%s_real" % hermetic_wrapper.basename,
+            sibling = hermetic_wrapper,
+        )
+
     crate_info = rust_common.create_crate_info(**crate_info_dict)
 
     build_metadata = crate_info_dict.get("metadata", None)
@@ -1469,14 +1482,24 @@
             "metadata_files": coverage_runfiles + [executable] if executable else [],
         })
 
-    providers = [
-        DefaultInfo(
+    if use_hermetic_launcher:
+        default_info = hermetic_defaultinfo(
+            ctx,
             # nb. This field is required for cc_library to depend on our output.
             files = depset(outputs),
             runfiles = runfiles,
             executable = executable,
-        ),
-    ]
+            out = hermetic_wrapper,
+        )
+    else:
+        default_info = DefaultInfo(
+            # nb. This field is required for cc_library to depend on our output.
+            files = depset(outputs),
+            runfiles = runfiles,
+            executable = executable,
+        )
+
+    providers = [default_info]
 
     # When invoked by aspects (and when running `bazel coverage`), the
     # baseline_coverage.dat created here will conflict with the baseline_coverage.dat of the