Stamp only binaries by default (#1452)

Currently we pass `bazel-out/volatile-status.txt` to all the `Rustc` actions, which is bad for remote caching with `--stamp`enabled.

This PR makes only `rust_binary` be affected by the `--stamp` flag by default. The rest of the rust rules have `stamp = 0` as a default. Should you want to force stamp you can set `stamp = 1`. If you want to make your target stamp behavior depend on the `--stamp` command line flag, use `stamp = -1`.

Additionally, this PR makes `rules_rust` also use the stable keys for stamping, by passing `bazel-out/stable-status.txt` to the relevant `Rustc` actions when stamping is enabled. This makes `rules_rust` adhere to the contract described here: https://docs.bazel.build/versions/main/user-manual.html#flag--workspace_status_command
diff --git a/crate_universe/BUILD.bazel b/crate_universe/BUILD.bazel
index 7ce1d78..203a4d2 100644
--- a/crate_universe/BUILD.bazel
+++ b/crate_universe/BUILD.bazel
@@ -67,6 +67,11 @@
     ),
     edition = "2018",
     proc_macro_deps = all_crate_deps(proc_macro = True),
+    # This target embeds additional, stamping related information (see
+    # https://docs.bazel.build/versions/main/user-manual.html#workspace_status
+    # for more information). Set stamp = -1 to indicate that it should respect
+    # the value of the --stamp comandline flag.
+    stamp = -1,
     version = VERSION,
     visibility = ["//visibility:public"],
     deps = all_crate_deps(normal = True),
diff --git a/docs/defs.md b/docs/defs.md
index 69189ee..7228d32 100644
--- a/docs/defs.md
+++ b/docs/defs.md
@@ -300,7 +300,7 @@
 | <a id="rust_library-rustc_env_files"></a>rustc_env_files |  Files containing additional environment variables to set for rustc.<br><br>These files should  contain a single variable per line, of format <code>NAME=value</code>, and newlines may be included in a value by ending a line with a trailing back-slash (<code>\\</code>).<br><br>The order that these files will be processed is unspecified, so multiple definitions of a particular variable are discouraged.<br><br>Note that the variables here are subject to [workspace status](https://docs.bazel.build/versions/main/user-manual.html#workspace_status) stamping should the <code>stamp</code> attribute be enabled. Stamp variables should be wrapped in brackets in order to be resolved. E.g. <code>NAME={WORKSPACE_STATUS_VARIABLE}</code>.   | <a href="https://bazel.build/docs/build-ref.html#labels">List of labels</a> | optional | [] |
 | <a id="rust_library-rustc_flags"></a>rustc_flags |  List of compiler flags passed to <code>rustc</code>.<br><br>These strings are subject to Make variable expansion for predefined source/output path variables like <code>$location</code>, <code>$execpath</code>, and <code>$rootpath</code>. This expansion is useful if you wish to pass a generated file of arguments to rustc: <code>@$(location //package:target)</code>.   | List of strings | optional | [] |
 | <a id="rust_library-srcs"></a>srcs |  List of Rust <code>.rs</code> source files used to build the library.<br><br>If <code>srcs</code> contains more than one file, then there must be a file either named <code>lib.rs</code>. Otherwise, <code>crate_root</code> must be set to the source file that is the root of the crate to be passed to rustc to build this crate.   | <a href="https://bazel.build/docs/build-ref.html#labels">List of labels</a> | optional | [] |
-| <a id="rust_library-stamp"></a>stamp |  Whether to encode build information into the <code>Rustc</code> action. Possible values:<br><br>- <code>stamp = 1</code>: Always stamp the build information into the <code>Rustc</code> 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.<br><br>- <code>stamp = 0</code>: Always replace build information by constant values. This gives good build result caching.<br><br>- <code>stamp = -1</code>: Embedding of build information is controlled by the             [--[no]stamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) flag.<br><br>Stamped targets are not rebuilt unless their dependencies change.<br><br>For example if a <code>rust_library</code> is stamped, and a <code>rust_binary</code> depends on that library, the stamped library won't be rebuilt when we change sources of the <code>rust_binary</code>. This is different from how [<code>cc_library.linkstamps</code>](https://docs.bazel.build/versions/main/be/c-cpp.html#cc_library.linkstamp) behaves.   | Integer | optional | -1 |
+| <a id="rust_library-stamp"></a>stamp |  Whether to encode build information into the <code>Rustc</code> action. Possible values:<br><br>- <code>stamp = 1</code>: Always stamp the build information into the <code>Rustc</code> 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.<br><br>- <code>stamp = 0</code>: Always replace build information by constant values. This gives good build result caching.<br><br>- <code>stamp = -1</code>: Embedding of build information is controlled by the             [--[no]stamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) flag.<br><br>Stamped targets are not rebuilt unless their dependencies change.<br><br>For example if a <code>rust_library</code> is stamped, and a <code>rust_binary</code> depends on that library, the stamped library won't be rebuilt when we change sources of the <code>rust_binary</code>. This is different from how [<code>cc_library.linkstamps</code>](https://docs.bazel.build/versions/main/be/c-cpp.html#cc_library.linkstamp) behaves.   | Integer | optional | 0 |
 | <a id="rust_library-version"></a>version |  A version to inject in the cargo environment variable.   | String | optional | "0.0.0" |
 
 
@@ -336,7 +336,7 @@
 | <a id="rust_proc_macro-rustc_env_files"></a>rustc_env_files |  Files containing additional environment variables to set for rustc.<br><br>These files should  contain a single variable per line, of format <code>NAME=value</code>, and newlines may be included in a value by ending a line with a trailing back-slash (<code>\\</code>).<br><br>The order that these files will be processed is unspecified, so multiple definitions of a particular variable are discouraged.<br><br>Note that the variables here are subject to [workspace status](https://docs.bazel.build/versions/main/user-manual.html#workspace_status) stamping should the <code>stamp</code> attribute be enabled. Stamp variables should be wrapped in brackets in order to be resolved. E.g. <code>NAME={WORKSPACE_STATUS_VARIABLE}</code>.   | <a href="https://bazel.build/docs/build-ref.html#labels">List of labels</a> | optional | [] |
 | <a id="rust_proc_macro-rustc_flags"></a>rustc_flags |  List of compiler flags passed to <code>rustc</code>.<br><br>These strings are subject to Make variable expansion for predefined source/output path variables like <code>$location</code>, <code>$execpath</code>, and <code>$rootpath</code>. This expansion is useful if you wish to pass a generated file of arguments to rustc: <code>@$(location //package:target)</code>.   | List of strings | optional | [] |
 | <a id="rust_proc_macro-srcs"></a>srcs |  List of Rust <code>.rs</code> source files used to build the library.<br><br>If <code>srcs</code> contains more than one file, then there must be a file either named <code>lib.rs</code>. Otherwise, <code>crate_root</code> must be set to the source file that is the root of the crate to be passed to rustc to build this crate.   | <a href="https://bazel.build/docs/build-ref.html#labels">List of labels</a> | optional | [] |
-| <a id="rust_proc_macro-stamp"></a>stamp |  Whether to encode build information into the <code>Rustc</code> action. Possible values:<br><br>- <code>stamp = 1</code>: Always stamp the build information into the <code>Rustc</code> 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.<br><br>- <code>stamp = 0</code>: Always replace build information by constant values. This gives good build result caching.<br><br>- <code>stamp = -1</code>: Embedding of build information is controlled by the             [--[no]stamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) flag.<br><br>Stamped targets are not rebuilt unless their dependencies change.<br><br>For example if a <code>rust_library</code> is stamped, and a <code>rust_binary</code> depends on that library, the stamped library won't be rebuilt when we change sources of the <code>rust_binary</code>. This is different from how [<code>cc_library.linkstamps</code>](https://docs.bazel.build/versions/main/be/c-cpp.html#cc_library.linkstamp) behaves.   | Integer | optional | -1 |
+| <a id="rust_proc_macro-stamp"></a>stamp |  Whether to encode build information into the <code>Rustc</code> action. Possible values:<br><br>- <code>stamp = 1</code>: Always stamp the build information into the <code>Rustc</code> 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.<br><br>- <code>stamp = 0</code>: Always replace build information by constant values. This gives good build result caching.<br><br>- <code>stamp = -1</code>: Embedding of build information is controlled by the             [--[no]stamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) flag.<br><br>Stamped targets are not rebuilt unless their dependencies change.<br><br>For example if a <code>rust_library</code> is stamped, and a <code>rust_binary</code> depends on that library, the stamped library won't be rebuilt when we change sources of the <code>rust_binary</code>. This is different from how [<code>cc_library.linkstamps</code>](https://docs.bazel.build/versions/main/be/c-cpp.html#cc_library.linkstamp) behaves.   | Integer | optional | 0 |
 | <a id="rust_proc_macro-version"></a>version |  A version to inject in the cargo environment variable.   | String | optional | "0.0.0" |
 
 
@@ -380,7 +380,7 @@
 | <a id="rust_shared_library-rustc_env_files"></a>rustc_env_files |  Files containing additional environment variables to set for rustc.<br><br>These files should  contain a single variable per line, of format <code>NAME=value</code>, and newlines may be included in a value by ending a line with a trailing back-slash (<code>\\</code>).<br><br>The order that these files will be processed is unspecified, so multiple definitions of a particular variable are discouraged.<br><br>Note that the variables here are subject to [workspace status](https://docs.bazel.build/versions/main/user-manual.html#workspace_status) stamping should the <code>stamp</code> attribute be enabled. Stamp variables should be wrapped in brackets in order to be resolved. E.g. <code>NAME={WORKSPACE_STATUS_VARIABLE}</code>.   | <a href="https://bazel.build/docs/build-ref.html#labels">List of labels</a> | optional | [] |
 | <a id="rust_shared_library-rustc_flags"></a>rustc_flags |  List of compiler flags passed to <code>rustc</code>.<br><br>These strings are subject to Make variable expansion for predefined source/output path variables like <code>$location</code>, <code>$execpath</code>, and <code>$rootpath</code>. This expansion is useful if you wish to pass a generated file of arguments to rustc: <code>@$(location //package:target)</code>.   | List of strings | optional | [] |
 | <a id="rust_shared_library-srcs"></a>srcs |  List of Rust <code>.rs</code> source files used to build the library.<br><br>If <code>srcs</code> contains more than one file, then there must be a file either named <code>lib.rs</code>. Otherwise, <code>crate_root</code> must be set to the source file that is the root of the crate to be passed to rustc to build this crate.   | <a href="https://bazel.build/docs/build-ref.html#labels">List of labels</a> | optional | [] |
-| <a id="rust_shared_library-stamp"></a>stamp |  Whether to encode build information into the <code>Rustc</code> action. Possible values:<br><br>- <code>stamp = 1</code>: Always stamp the build information into the <code>Rustc</code> 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.<br><br>- <code>stamp = 0</code>: Always replace build information by constant values. This gives good build result caching.<br><br>- <code>stamp = -1</code>: Embedding of build information is controlled by the             [--[no]stamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) flag.<br><br>Stamped targets are not rebuilt unless their dependencies change.<br><br>For example if a <code>rust_library</code> is stamped, and a <code>rust_binary</code> depends on that library, the stamped library won't be rebuilt when we change sources of the <code>rust_binary</code>. This is different from how [<code>cc_library.linkstamps</code>](https://docs.bazel.build/versions/main/be/c-cpp.html#cc_library.linkstamp) behaves.   | Integer | optional | -1 |
+| <a id="rust_shared_library-stamp"></a>stamp |  Whether to encode build information into the <code>Rustc</code> action. Possible values:<br><br>- <code>stamp = 1</code>: Always stamp the build information into the <code>Rustc</code> 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.<br><br>- <code>stamp = 0</code>: Always replace build information by constant values. This gives good build result caching.<br><br>- <code>stamp = -1</code>: Embedding of build information is controlled by the             [--[no]stamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) flag.<br><br>Stamped targets are not rebuilt unless their dependencies change.<br><br>For example if a <code>rust_library</code> is stamped, and a <code>rust_binary</code> depends on that library, the stamped library won't be rebuilt when we change sources of the <code>rust_binary</code>. This is different from how [<code>cc_library.linkstamps</code>](https://docs.bazel.build/versions/main/be/c-cpp.html#cc_library.linkstamp) behaves.   | Integer | optional | 0 |
 | <a id="rust_shared_library-version"></a>version |  A version to inject in the cargo environment variable.   | String | optional | "0.0.0" |
 
 
@@ -424,7 +424,7 @@
 | <a id="rust_static_library-rustc_env_files"></a>rustc_env_files |  Files containing additional environment variables to set for rustc.<br><br>These files should  contain a single variable per line, of format <code>NAME=value</code>, and newlines may be included in a value by ending a line with a trailing back-slash (<code>\\</code>).<br><br>The order that these files will be processed is unspecified, so multiple definitions of a particular variable are discouraged.<br><br>Note that the variables here are subject to [workspace status](https://docs.bazel.build/versions/main/user-manual.html#workspace_status) stamping should the <code>stamp</code> attribute be enabled. Stamp variables should be wrapped in brackets in order to be resolved. E.g. <code>NAME={WORKSPACE_STATUS_VARIABLE}</code>.   | <a href="https://bazel.build/docs/build-ref.html#labels">List of labels</a> | optional | [] |
 | <a id="rust_static_library-rustc_flags"></a>rustc_flags |  List of compiler flags passed to <code>rustc</code>.<br><br>These strings are subject to Make variable expansion for predefined source/output path variables like <code>$location</code>, <code>$execpath</code>, and <code>$rootpath</code>. This expansion is useful if you wish to pass a generated file of arguments to rustc: <code>@$(location //package:target)</code>.   | List of strings | optional | [] |
 | <a id="rust_static_library-srcs"></a>srcs |  List of Rust <code>.rs</code> source files used to build the library.<br><br>If <code>srcs</code> contains more than one file, then there must be a file either named <code>lib.rs</code>. Otherwise, <code>crate_root</code> must be set to the source file that is the root of the crate to be passed to rustc to build this crate.   | <a href="https://bazel.build/docs/build-ref.html#labels">List of labels</a> | optional | [] |
-| <a id="rust_static_library-stamp"></a>stamp |  Whether to encode build information into the <code>Rustc</code> action. Possible values:<br><br>- <code>stamp = 1</code>: Always stamp the build information into the <code>Rustc</code> 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.<br><br>- <code>stamp = 0</code>: Always replace build information by constant values. This gives good build result caching.<br><br>- <code>stamp = -1</code>: Embedding of build information is controlled by the             [--[no]stamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) flag.<br><br>Stamped targets are not rebuilt unless their dependencies change.<br><br>For example if a <code>rust_library</code> is stamped, and a <code>rust_binary</code> depends on that library, the stamped library won't be rebuilt when we change sources of the <code>rust_binary</code>. This is different from how [<code>cc_library.linkstamps</code>](https://docs.bazel.build/versions/main/be/c-cpp.html#cc_library.linkstamp) behaves.   | Integer | optional | -1 |
+| <a id="rust_static_library-stamp"></a>stamp |  Whether to encode build information into the <code>Rustc</code> action. Possible values:<br><br>- <code>stamp = 1</code>: Always stamp the build information into the <code>Rustc</code> 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.<br><br>- <code>stamp = 0</code>: Always replace build information by constant values. This gives good build result caching.<br><br>- <code>stamp = -1</code>: Embedding of build information is controlled by the             [--[no]stamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) flag.<br><br>Stamped targets are not rebuilt unless their dependencies change.<br><br>For example if a <code>rust_library</code> is stamped, and a <code>rust_binary</code> depends on that library, the stamped library won't be rebuilt when we change sources of the <code>rust_binary</code>. This is different from how [<code>cc_library.linkstamps</code>](https://docs.bazel.build/versions/main/be/c-cpp.html#cc_library.linkstamp) behaves.   | Integer | optional | 0 |
 | <a id="rust_static_library-version"></a>version |  A version to inject in the cargo environment variable.   | String | optional | "0.0.0" |
 
 
@@ -577,7 +577,7 @@
 | <a id="rust_test-rustc_env_files"></a>rustc_env_files |  Files containing additional environment variables to set for rustc.<br><br>These files should  contain a single variable per line, of format <code>NAME=value</code>, and newlines may be included in a value by ending a line with a trailing back-slash (<code>\\</code>).<br><br>The order that these files will be processed is unspecified, so multiple definitions of a particular variable are discouraged.<br><br>Note that the variables here are subject to [workspace status](https://docs.bazel.build/versions/main/user-manual.html#workspace_status) stamping should the <code>stamp</code> attribute be enabled. Stamp variables should be wrapped in brackets in order to be resolved. E.g. <code>NAME={WORKSPACE_STATUS_VARIABLE}</code>.   | <a href="https://bazel.build/docs/build-ref.html#labels">List of labels</a> | optional | [] |
 | <a id="rust_test-rustc_flags"></a>rustc_flags |  List of compiler flags passed to <code>rustc</code>.<br><br>These strings are subject to Make variable expansion for predefined source/output path variables like <code>$location</code>, <code>$execpath</code>, and <code>$rootpath</code>. This expansion is useful if you wish to pass a generated file of arguments to rustc: <code>@$(location //package:target)</code>.   | List of strings | optional | [] |
 | <a id="rust_test-srcs"></a>srcs |  List of Rust <code>.rs</code> source files used to build the library.<br><br>If <code>srcs</code> contains more than one file, then there must be a file either named <code>lib.rs</code>. Otherwise, <code>crate_root</code> must be set to the source file that is the root of the crate to be passed to rustc to build this crate.   | <a href="https://bazel.build/docs/build-ref.html#labels">List of labels</a> | optional | [] |
-| <a id="rust_test-stamp"></a>stamp |  Whether to encode build information into the <code>Rustc</code> action. Possible values:<br><br>- <code>stamp = 1</code>: Always stamp the build information into the <code>Rustc</code> 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.<br><br>- <code>stamp = 0</code>: Always replace build information by constant values. This gives good build result caching.<br><br>- <code>stamp = -1</code>: Embedding of build information is controlled by the             [--[no]stamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) flag.<br><br>Stamped targets are not rebuilt unless their dependencies change.<br><br>For example if a <code>rust_library</code> is stamped, and a <code>rust_binary</code> depends on that library, the stamped library won't be rebuilt when we change sources of the <code>rust_binary</code>. This is different from how [<code>cc_library.linkstamps</code>](https://docs.bazel.build/versions/main/be/c-cpp.html#cc_library.linkstamp) behaves.   | Integer | optional | -1 |
+| <a id="rust_test-stamp"></a>stamp |  Whether to encode build information into the <code>Rustc</code> action. Possible values:<br><br>- <code>stamp = 1</code>: Always stamp the build information into the <code>Rustc</code> 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.<br><br>- <code>stamp = 0</code>: Always replace build information by constant values. This gives good build result caching.<br><br>- <code>stamp = -1</code>: Embedding of build information is controlled by the             [--[no]stamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) flag.<br><br>Stamped targets are not rebuilt unless their dependencies change.<br><br>For example if a <code>rust_library</code> is stamped, and a <code>rust_binary</code> depends on that library, the stamped library won't be rebuilt when we change sources of the <code>rust_binary</code>. This is different from how [<code>cc_library.linkstamps</code>](https://docs.bazel.build/versions/main/be/c-cpp.html#cc_library.linkstamp) behaves.   | Integer | optional | 0 |
 | <a id="rust_test-use_libtest_harness"></a>use_libtest_harness |  Whether to use <code>libtest</code>. For targets using this flag, individual tests can be run by using the [--test_arg](https://docs.bazel.build/versions/4.0.0/command-line-reference.html#flag--test_arg) flag. E.g. <code>bazel test //src:rust_test --test_arg=foo::test::test_fn</code>.   | Boolean | optional | True |
 | <a id="rust_test-version"></a>version |  A version to inject in the cargo environment variable.   | String | optional | "0.0.0" |
 
diff --git a/docs/flatten.md b/docs/flatten.md
index 822c2dd..1663e77 100644
--- a/docs/flatten.md
+++ b/docs/flatten.md
@@ -677,7 +677,7 @@
 | <a id="rust_library-rustc_env_files"></a>rustc_env_files |  Files containing additional environment variables to set for rustc.<br><br>These files should  contain a single variable per line, of format <code>NAME=value</code>, and newlines may be included in a value by ending a line with a trailing back-slash (<code>\\</code>).<br><br>The order that these files will be processed is unspecified, so multiple definitions of a particular variable are discouraged.<br><br>Note that the variables here are subject to [workspace status](https://docs.bazel.build/versions/main/user-manual.html#workspace_status) stamping should the <code>stamp</code> attribute be enabled. Stamp variables should be wrapped in brackets in order to be resolved. E.g. <code>NAME={WORKSPACE_STATUS_VARIABLE}</code>.   | <a href="https://bazel.build/docs/build-ref.html#labels">List of labels</a> | optional | [] |
 | <a id="rust_library-rustc_flags"></a>rustc_flags |  List of compiler flags passed to <code>rustc</code>.<br><br>These strings are subject to Make variable expansion for predefined source/output path variables like <code>$location</code>, <code>$execpath</code>, and <code>$rootpath</code>. This expansion is useful if you wish to pass a generated file of arguments to rustc: <code>@$(location //package:target)</code>.   | List of strings | optional | [] |
 | <a id="rust_library-srcs"></a>srcs |  List of Rust <code>.rs</code> source files used to build the library.<br><br>If <code>srcs</code> contains more than one file, then there must be a file either named <code>lib.rs</code>. Otherwise, <code>crate_root</code> must be set to the source file that is the root of the crate to be passed to rustc to build this crate.   | <a href="https://bazel.build/docs/build-ref.html#labels">List of labels</a> | optional | [] |
-| <a id="rust_library-stamp"></a>stamp |  Whether to encode build information into the <code>Rustc</code> action. Possible values:<br><br>- <code>stamp = 1</code>: Always stamp the build information into the <code>Rustc</code> 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.<br><br>- <code>stamp = 0</code>: Always replace build information by constant values. This gives good build result caching.<br><br>- <code>stamp = -1</code>: Embedding of build information is controlled by the             [--[no]stamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) flag.<br><br>Stamped targets are not rebuilt unless their dependencies change.<br><br>For example if a <code>rust_library</code> is stamped, and a <code>rust_binary</code> depends on that library, the stamped library won't be rebuilt when we change sources of the <code>rust_binary</code>. This is different from how [<code>cc_library.linkstamps</code>](https://docs.bazel.build/versions/main/be/c-cpp.html#cc_library.linkstamp) behaves.   | Integer | optional | -1 |
+| <a id="rust_library-stamp"></a>stamp |  Whether to encode build information into the <code>Rustc</code> action. Possible values:<br><br>- <code>stamp = 1</code>: Always stamp the build information into the <code>Rustc</code> 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.<br><br>- <code>stamp = 0</code>: Always replace build information by constant values. This gives good build result caching.<br><br>- <code>stamp = -1</code>: Embedding of build information is controlled by the             [--[no]stamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) flag.<br><br>Stamped targets are not rebuilt unless their dependencies change.<br><br>For example if a <code>rust_library</code> is stamped, and a <code>rust_binary</code> depends on that library, the stamped library won't be rebuilt when we change sources of the <code>rust_binary</code>. This is different from how [<code>cc_library.linkstamps</code>](https://docs.bazel.build/versions/main/be/c-cpp.html#cc_library.linkstamp) behaves.   | Integer | optional | 0 |
 | <a id="rust_library-version"></a>version |  A version to inject in the cargo environment variable.   | String | optional | "0.0.0" |
 
 
@@ -713,7 +713,7 @@
 | <a id="rust_proc_macro-rustc_env_files"></a>rustc_env_files |  Files containing additional environment variables to set for rustc.<br><br>These files should  contain a single variable per line, of format <code>NAME=value</code>, and newlines may be included in a value by ending a line with a trailing back-slash (<code>\\</code>).<br><br>The order that these files will be processed is unspecified, so multiple definitions of a particular variable are discouraged.<br><br>Note that the variables here are subject to [workspace status](https://docs.bazel.build/versions/main/user-manual.html#workspace_status) stamping should the <code>stamp</code> attribute be enabled. Stamp variables should be wrapped in brackets in order to be resolved. E.g. <code>NAME={WORKSPACE_STATUS_VARIABLE}</code>.   | <a href="https://bazel.build/docs/build-ref.html#labels">List of labels</a> | optional | [] |
 | <a id="rust_proc_macro-rustc_flags"></a>rustc_flags |  List of compiler flags passed to <code>rustc</code>.<br><br>These strings are subject to Make variable expansion for predefined source/output path variables like <code>$location</code>, <code>$execpath</code>, and <code>$rootpath</code>. This expansion is useful if you wish to pass a generated file of arguments to rustc: <code>@$(location //package:target)</code>.   | List of strings | optional | [] |
 | <a id="rust_proc_macro-srcs"></a>srcs |  List of Rust <code>.rs</code> source files used to build the library.<br><br>If <code>srcs</code> contains more than one file, then there must be a file either named <code>lib.rs</code>. Otherwise, <code>crate_root</code> must be set to the source file that is the root of the crate to be passed to rustc to build this crate.   | <a href="https://bazel.build/docs/build-ref.html#labels">List of labels</a> | optional | [] |
-| <a id="rust_proc_macro-stamp"></a>stamp |  Whether to encode build information into the <code>Rustc</code> action. Possible values:<br><br>- <code>stamp = 1</code>: Always stamp the build information into the <code>Rustc</code> 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.<br><br>- <code>stamp = 0</code>: Always replace build information by constant values. This gives good build result caching.<br><br>- <code>stamp = -1</code>: Embedding of build information is controlled by the             [--[no]stamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) flag.<br><br>Stamped targets are not rebuilt unless their dependencies change.<br><br>For example if a <code>rust_library</code> is stamped, and a <code>rust_binary</code> depends on that library, the stamped library won't be rebuilt when we change sources of the <code>rust_binary</code>. This is different from how [<code>cc_library.linkstamps</code>](https://docs.bazel.build/versions/main/be/c-cpp.html#cc_library.linkstamp) behaves.   | Integer | optional | -1 |
+| <a id="rust_proc_macro-stamp"></a>stamp |  Whether to encode build information into the <code>Rustc</code> action. Possible values:<br><br>- <code>stamp = 1</code>: Always stamp the build information into the <code>Rustc</code> 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.<br><br>- <code>stamp = 0</code>: Always replace build information by constant values. This gives good build result caching.<br><br>- <code>stamp = -1</code>: Embedding of build information is controlled by the             [--[no]stamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) flag.<br><br>Stamped targets are not rebuilt unless their dependencies change.<br><br>For example if a <code>rust_library</code> is stamped, and a <code>rust_binary</code> depends on that library, the stamped library won't be rebuilt when we change sources of the <code>rust_binary</code>. This is different from how [<code>cc_library.linkstamps</code>](https://docs.bazel.build/versions/main/be/c-cpp.html#cc_library.linkstamp) behaves.   | Integer | optional | 0 |
 | <a id="rust_proc_macro-version"></a>version |  A version to inject in the cargo environment variable.   | String | optional | "0.0.0" |
 
 
@@ -857,7 +857,7 @@
 | <a id="rust_shared_library-rustc_env_files"></a>rustc_env_files |  Files containing additional environment variables to set for rustc.<br><br>These files should  contain a single variable per line, of format <code>NAME=value</code>, and newlines may be included in a value by ending a line with a trailing back-slash (<code>\\</code>).<br><br>The order that these files will be processed is unspecified, so multiple definitions of a particular variable are discouraged.<br><br>Note that the variables here are subject to [workspace status](https://docs.bazel.build/versions/main/user-manual.html#workspace_status) stamping should the <code>stamp</code> attribute be enabled. Stamp variables should be wrapped in brackets in order to be resolved. E.g. <code>NAME={WORKSPACE_STATUS_VARIABLE}</code>.   | <a href="https://bazel.build/docs/build-ref.html#labels">List of labels</a> | optional | [] |
 | <a id="rust_shared_library-rustc_flags"></a>rustc_flags |  List of compiler flags passed to <code>rustc</code>.<br><br>These strings are subject to Make variable expansion for predefined source/output path variables like <code>$location</code>, <code>$execpath</code>, and <code>$rootpath</code>. This expansion is useful if you wish to pass a generated file of arguments to rustc: <code>@$(location //package:target)</code>.   | List of strings | optional | [] |
 | <a id="rust_shared_library-srcs"></a>srcs |  List of Rust <code>.rs</code> source files used to build the library.<br><br>If <code>srcs</code> contains more than one file, then there must be a file either named <code>lib.rs</code>. Otherwise, <code>crate_root</code> must be set to the source file that is the root of the crate to be passed to rustc to build this crate.   | <a href="https://bazel.build/docs/build-ref.html#labels">List of labels</a> | optional | [] |
-| <a id="rust_shared_library-stamp"></a>stamp |  Whether to encode build information into the <code>Rustc</code> action. Possible values:<br><br>- <code>stamp = 1</code>: Always stamp the build information into the <code>Rustc</code> 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.<br><br>- <code>stamp = 0</code>: Always replace build information by constant values. This gives good build result caching.<br><br>- <code>stamp = -1</code>: Embedding of build information is controlled by the             [--[no]stamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) flag.<br><br>Stamped targets are not rebuilt unless their dependencies change.<br><br>For example if a <code>rust_library</code> is stamped, and a <code>rust_binary</code> depends on that library, the stamped library won't be rebuilt when we change sources of the <code>rust_binary</code>. This is different from how [<code>cc_library.linkstamps</code>](https://docs.bazel.build/versions/main/be/c-cpp.html#cc_library.linkstamp) behaves.   | Integer | optional | -1 |
+| <a id="rust_shared_library-stamp"></a>stamp |  Whether to encode build information into the <code>Rustc</code> action. Possible values:<br><br>- <code>stamp = 1</code>: Always stamp the build information into the <code>Rustc</code> 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.<br><br>- <code>stamp = 0</code>: Always replace build information by constant values. This gives good build result caching.<br><br>- <code>stamp = -1</code>: Embedding of build information is controlled by the             [--[no]stamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) flag.<br><br>Stamped targets are not rebuilt unless their dependencies change.<br><br>For example if a <code>rust_library</code> is stamped, and a <code>rust_binary</code> depends on that library, the stamped library won't be rebuilt when we change sources of the <code>rust_binary</code>. This is different from how [<code>cc_library.linkstamps</code>](https://docs.bazel.build/versions/main/be/c-cpp.html#cc_library.linkstamp) behaves.   | Integer | optional | 0 |
 | <a id="rust_shared_library-version"></a>version |  A version to inject in the cargo environment variable.   | String | optional | "0.0.0" |
 
 
@@ -901,7 +901,7 @@
 | <a id="rust_static_library-rustc_env_files"></a>rustc_env_files |  Files containing additional environment variables to set for rustc.<br><br>These files should  contain a single variable per line, of format <code>NAME=value</code>, and newlines may be included in a value by ending a line with a trailing back-slash (<code>\\</code>).<br><br>The order that these files will be processed is unspecified, so multiple definitions of a particular variable are discouraged.<br><br>Note that the variables here are subject to [workspace status](https://docs.bazel.build/versions/main/user-manual.html#workspace_status) stamping should the <code>stamp</code> attribute be enabled. Stamp variables should be wrapped in brackets in order to be resolved. E.g. <code>NAME={WORKSPACE_STATUS_VARIABLE}</code>.   | <a href="https://bazel.build/docs/build-ref.html#labels">List of labels</a> | optional | [] |
 | <a id="rust_static_library-rustc_flags"></a>rustc_flags |  List of compiler flags passed to <code>rustc</code>.<br><br>These strings are subject to Make variable expansion for predefined source/output path variables like <code>$location</code>, <code>$execpath</code>, and <code>$rootpath</code>. This expansion is useful if you wish to pass a generated file of arguments to rustc: <code>@$(location //package:target)</code>.   | List of strings | optional | [] |
 | <a id="rust_static_library-srcs"></a>srcs |  List of Rust <code>.rs</code> source files used to build the library.<br><br>If <code>srcs</code> contains more than one file, then there must be a file either named <code>lib.rs</code>. Otherwise, <code>crate_root</code> must be set to the source file that is the root of the crate to be passed to rustc to build this crate.   | <a href="https://bazel.build/docs/build-ref.html#labels">List of labels</a> | optional | [] |
-| <a id="rust_static_library-stamp"></a>stamp |  Whether to encode build information into the <code>Rustc</code> action. Possible values:<br><br>- <code>stamp = 1</code>: Always stamp the build information into the <code>Rustc</code> 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.<br><br>- <code>stamp = 0</code>: Always replace build information by constant values. This gives good build result caching.<br><br>- <code>stamp = -1</code>: Embedding of build information is controlled by the             [--[no]stamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) flag.<br><br>Stamped targets are not rebuilt unless their dependencies change.<br><br>For example if a <code>rust_library</code> is stamped, and a <code>rust_binary</code> depends on that library, the stamped library won't be rebuilt when we change sources of the <code>rust_binary</code>. This is different from how [<code>cc_library.linkstamps</code>](https://docs.bazel.build/versions/main/be/c-cpp.html#cc_library.linkstamp) behaves.   | Integer | optional | -1 |
+| <a id="rust_static_library-stamp"></a>stamp |  Whether to encode build information into the <code>Rustc</code> action. Possible values:<br><br>- <code>stamp = 1</code>: Always stamp the build information into the <code>Rustc</code> 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.<br><br>- <code>stamp = 0</code>: Always replace build information by constant values. This gives good build result caching.<br><br>- <code>stamp = -1</code>: Embedding of build information is controlled by the             [--[no]stamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) flag.<br><br>Stamped targets are not rebuilt unless their dependencies change.<br><br>For example if a <code>rust_library</code> is stamped, and a <code>rust_binary</code> depends on that library, the stamped library won't be rebuilt when we change sources of the <code>rust_binary</code>. This is different from how [<code>cc_library.linkstamps</code>](https://docs.bazel.build/versions/main/be/c-cpp.html#cc_library.linkstamp) behaves.   | Integer | optional | 0 |
 | <a id="rust_static_library-version"></a>version |  A version to inject in the cargo environment variable.   | String | optional | "0.0.0" |
 
 
@@ -1073,7 +1073,7 @@
 | <a id="rust_test-rustc_env_files"></a>rustc_env_files |  Files containing additional environment variables to set for rustc.<br><br>These files should  contain a single variable per line, of format <code>NAME=value</code>, and newlines may be included in a value by ending a line with a trailing back-slash (<code>\\</code>).<br><br>The order that these files will be processed is unspecified, so multiple definitions of a particular variable are discouraged.<br><br>Note that the variables here are subject to [workspace status](https://docs.bazel.build/versions/main/user-manual.html#workspace_status) stamping should the <code>stamp</code> attribute be enabled. Stamp variables should be wrapped in brackets in order to be resolved. E.g. <code>NAME={WORKSPACE_STATUS_VARIABLE}</code>.   | <a href="https://bazel.build/docs/build-ref.html#labels">List of labels</a> | optional | [] |
 | <a id="rust_test-rustc_flags"></a>rustc_flags |  List of compiler flags passed to <code>rustc</code>.<br><br>These strings are subject to Make variable expansion for predefined source/output path variables like <code>$location</code>, <code>$execpath</code>, and <code>$rootpath</code>. This expansion is useful if you wish to pass a generated file of arguments to rustc: <code>@$(location //package:target)</code>.   | List of strings | optional | [] |
 | <a id="rust_test-srcs"></a>srcs |  List of Rust <code>.rs</code> source files used to build the library.<br><br>If <code>srcs</code> contains more than one file, then there must be a file either named <code>lib.rs</code>. Otherwise, <code>crate_root</code> must be set to the source file that is the root of the crate to be passed to rustc to build this crate.   | <a href="https://bazel.build/docs/build-ref.html#labels">List of labels</a> | optional | [] |
-| <a id="rust_test-stamp"></a>stamp |  Whether to encode build information into the <code>Rustc</code> action. Possible values:<br><br>- <code>stamp = 1</code>: Always stamp the build information into the <code>Rustc</code> 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.<br><br>- <code>stamp = 0</code>: Always replace build information by constant values. This gives good build result caching.<br><br>- <code>stamp = -1</code>: Embedding of build information is controlled by the             [--[no]stamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) flag.<br><br>Stamped targets are not rebuilt unless their dependencies change.<br><br>For example if a <code>rust_library</code> is stamped, and a <code>rust_binary</code> depends on that library, the stamped library won't be rebuilt when we change sources of the <code>rust_binary</code>. This is different from how [<code>cc_library.linkstamps</code>](https://docs.bazel.build/versions/main/be/c-cpp.html#cc_library.linkstamp) behaves.   | Integer | optional | -1 |
+| <a id="rust_test-stamp"></a>stamp |  Whether to encode build information into the <code>Rustc</code> action. Possible values:<br><br>- <code>stamp = 1</code>: Always stamp the build information into the <code>Rustc</code> 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.<br><br>- <code>stamp = 0</code>: Always replace build information by constant values. This gives good build result caching.<br><br>- <code>stamp = -1</code>: Embedding of build information is controlled by the             [--[no]stamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) flag.<br><br>Stamped targets are not rebuilt unless their dependencies change.<br><br>For example if a <code>rust_library</code> is stamped, and a <code>rust_binary</code> depends on that library, the stamped library won't be rebuilt when we change sources of the <code>rust_binary</code>. This is different from how [<code>cc_library.linkstamps</code>](https://docs.bazel.build/versions/main/be/c-cpp.html#cc_library.linkstamp) behaves.   | Integer | optional | 0 |
 | <a id="rust_test-use_libtest_harness"></a>use_libtest_harness |  Whether to use <code>libtest</code>. For targets using this flag, individual tests can be run by using the [--test_arg](https://docs.bazel.build/versions/4.0.0/command-line-reference.html#flag--test_arg) flag. E.g. <code>bazel test //src:rust_test --test_arg=foo::test::test_fn</code>.   | Boolean | optional | True |
 | <a id="rust_test-version"></a>version |  A version to inject in the cargo environment variable.   | String | optional | "0.0.0" |
 
diff --git a/rust/private/rust.bzl b/rust/private/rust.bzl
index 5b02292..a51d718 100644
--- a/rust/private/rust.bzl
+++ b/rust/private/rust.bzl
@@ -494,6 +494,32 @@
 
     return providers
 
+def _stamp_attribute(default_value):
+    return 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 = default_value,
+        values = [1, 0, -1],
+    )
+
 _common_attrs = {
     "aliases": attr.label_keyed_string_dict(
         doc = dedent("""\
@@ -625,30 +651,7 @@
         """),
         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",
@@ -941,6 +944,7 @@
         ),
         default = False,
     ),
+    "stamp": _stamp_attribute(default_value = -1),
     "_grep_includes": attr.label(
         allow_single_file = True,
         cfg = "exec",
diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl
index fbd8dd0..21fb46c 100644
--- a/rust/private/rustc.bzl
+++ b/rust/private/rustc.bzl
@@ -615,8 +615,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,
@@ -722,6 +722,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.
diff --git a/test/unit/stamp/stamp.rs b/test/unit/stamp/stamp.rs
index cb80052..ec98749 100644
--- a/test/unit/stamp/stamp.rs
+++ b/test/unit/stamp/stamp.rs
@@ -12,26 +12,55 @@
 mod test {
     use super::*;
 
-    #[cfg(feature = "force_stamp")]
+    #[cfg(any(
+        feature = "always_stamp",
+        feature = "consult_cmdline_value_is_true",
+        feature = "always_stamp_build_flag_true",
+        feature = "always_stamp_build_flag_false"
+    ))]
     #[test]
-    fn stamp_resolved() {
+    fn stamp_resolved_for_library() {
         assert!(!build_timestamp().contains("BUILD_TIMESTAMP"));
         assert!(build_timestamp().chars().all(char::is_numeric));
+
+        assert!(!build_user().contains("BUILD_USER"));
     }
 
-    #[cfg(feature = "skip_stamp")]
+    #[cfg(any(
+        feature = "always_stamp",
+        feature = "consult_cmdline_value_is_true",
+        feature = "always_stamp_build_flag_true",
+        feature = "always_stamp_build_flag_false"
+    ))]
     #[test]
-    fn stamp_not_resolved() {
+    fn stamp_resolved_for_test() {
+        assert!(!env!("BUILD_TIMESTAMP").contains("BUILD_TIMESTAMP"));
+        assert!(env!("BUILD_TIMESTAMP").chars().all(char::is_numeric));
+
+        assert!(!env!("BUILD_USER").contains("BUILD_USER"));
+    }
+
+    #[cfg(any(
+        feature = "never_stamp",
+        feature = "consult_cmdline_value_is_false",
+        feature = "never_stamp_build_flag_true",
+        feature = "never_stamp_build_flag_false"
+    ))]
+    #[test]
+    fn stamp_not_resolved_for_library() {
         assert!(build_timestamp().contains("BUILD_TIMESTAMP"));
+        assert!(build_user().contains("BUILD_USER"));
     }
 
+    #[cfg(any(
+        feature = "never_stamp",
+        feature = "consult_cmdline_value_is_false",
+        feature = "never_stamp_build_flag_true",
+        feature = "never_stamp_build_flag_false"
+    ))]
     #[test]
-    fn lib_volatile_stamp_matches() {
-        assert_eq!(build_timestamp(), env!("BUILD_TIMESTAMP"));
-    }
-
-    #[test]
-    fn lib_stable_stamp_not_stamped() {
-        assert_eq!(build_user(), "{BUILD_USER}");
+    fn stamp_not_resolved_for_test() {
+        assert!(env!("BUILD_TIMESTAMP").contains("BUILD_TIMESTAMP"));
+        assert!(env!("BUILD_USER").contains("BUILD_USER"));
     }
 }
diff --git a/test/unit/stamp/stamp_main.rs b/test/unit/stamp/stamp_main.rs
index 0e8b963..ae547c4 100644
--- a/test/unit/stamp/stamp_main.rs
+++ b/test/unit/stamp/stamp_main.rs
@@ -1,10 +1,47 @@
-#[cfg(feature = "force_stamp")]
-use force_stamp::build_timestamp;
+#[cfg(feature = "always_stamp")]
+use default_with_build_flag_on_lib::build_timestamp;
+#[cfg(feature = "always_stamp")]
+use default_with_build_flag_on_lib::build_user;
 
-#[cfg(feature = "skip_stamp")]
-use skip_stamp::build_timestamp;
+#[cfg(feature = "never_stamp")]
+use default_with_build_flag_off_lib::build_timestamp;
+#[cfg(feature = "never_stamp")]
+use default_with_build_flag_off_lib::build_user;
+
+#[cfg(feature = "always_stamp_build_flag_true")]
+use always_stamp_build_flag_true_lib::build_timestamp;
+#[cfg(feature = "always_stamp_build_flag_true")]
+use always_stamp_build_flag_true_lib::build_user;
+
+#[cfg(feature = "always_stamp_build_flag_false")]
+use always_stamp_build_flag_false_lib::build_timestamp;
+#[cfg(feature = "always_stamp_build_flag_false")]
+use always_stamp_build_flag_false_lib::build_user;
+
+#[cfg(feature = "never_stamp_build_flag_true")]
+use never_stamp_build_flag_true_lib::build_timestamp;
+#[cfg(feature = "never_stamp_build_flag_true")]
+use never_stamp_build_flag_true_lib::build_user;
+
+#[cfg(feature = "never_stamp_build_flag_false")]
+use never_stamp_build_flag_false_lib::build_timestamp;
+#[cfg(feature = "never_stamp_build_flag_false")]
+use never_stamp_build_flag_false_lib::build_user;
+
+#[cfg(feature = "consult_cmdline_value_is_true")]
+use consult_cmdline_value_is_true_lib::build_timestamp;
+#[cfg(feature = "consult_cmdline_value_is_true")]
+use consult_cmdline_value_is_true_lib::build_user;
+
+#[cfg(feature = "consult_cmdline_value_is_false")]
+use consult_cmdline_value_is_false_lib::build_timestamp;
+#[cfg(feature = "consult_cmdline_value_is_false")]
+use consult_cmdline_value_is_false_lib::build_user;
 
 fn main() {
     println!("bin stamp: {}", env!("BUILD_TIMESTAMP"));
     println!("lib stamp: {}", build_timestamp());
+
+    println!("bin stamp: {}", env!("BUILD_USER"));
+    println!("lib stamp: {}", build_user());
 }
diff --git a/test/unit/stamp/stamp_test.bzl b/test/unit/stamp/stamp_test.bzl
index a32468c..fc9c62b 100644
--- a/test/unit/stamp/stamp_test.bzl
+++ b/test/unit/stamp/stamp_test.bzl
@@ -1,7 +1,7 @@
 """Unittest to verify workspace status stamping is applied to environment files"""
 
 load("@bazel_skylib//lib:unittest.bzl", "analysistest")
-load("//rust:defs.bzl", "rust_binary", "rust_library", "rust_test")
+load("//rust:defs.bzl", "rust_binary", "rust_common", "rust_library", "rust_test")
 load(
     "//test/unit:common.bzl",
     "assert_action_mnemonic",
@@ -9,114 +9,281 @@
     "assert_argv_contains_not",
 )
 
-def _stamp_test_impl(ctx, force_stamp):
+_STAMP_ATTR_VALUES = (0, 1, -1)
+_BUILD_FLAG_VALUES = ("true", "false")
+
+def _assert_stamped(env, action):
+    assert_argv_contains(env, action, "--volatile-status-file")
+    assert_argv_contains(env, action, "bazel-out/volatile-status.txt")
+
+    assert_argv_contains(env, action, "--stable-status-file")
+    assert_argv_contains(env, action, "bazel-out/stable-status.txt")
+
+def _assert_not_stamped(env, action):
+    assert_argv_contains_not(env, action, "--volatile-status-file")
+    assert_argv_contains_not(env, action, "bazel-out/volatile-status.txt")
+
+    assert_argv_contains_not(env, action, "--stable-status-file")
+    assert_argv_contains_not(env, action, "bazel-out/stable-status.txt")
+
+def _stamp_build_flag_test_impl(ctx, flag_value):
     env = analysistest.begin(ctx)
     target = analysistest.target_under_test(env)
 
     action = target.actions[0]
     assert_action_mnemonic(env, action, "Rustc")
 
-    if force_stamp:
-        assert_argv_contains(env, action, "--volatile-status-file")
-        assert_argv_contains(env, action, "bazel-out/volatile-status.txt")
-    else:
-        assert_argv_contains_not(env, action, "--volatile-status-file")
-        assert_argv_contains_not(env, action, "bazel-out/volatile-status.txt")
+    is_test = target[rust_common.crate_info].is_test
+    is_bin = target[rust_common.crate_info].type == "bin"
 
-    # Note that use of stable status invalidates targets any time it's updated.
-    # This is undesirable behavior so it's intended to be excluded from the
-    # Rustc action. In general this should be fine as other rules can be used
-    # to produce template files for stamping in this action (ie. genrule).
-    assert_argv_contains_not(env, action, "bazel-out/stable-status.txt")
+    # bazel build --stamp should lead to stamped rust binaries, but not
+    # libraries and tests.
+    if flag_value:
+        if is_bin and not is_test:
+            _assert_stamped(env, action)
+        else:
+            _assert_not_stamped(env, action)
+    else:
+        _assert_not_stamped(env, action)
 
     return analysistest.end(env)
 
-def _force_stamp_test_impl(ctx):
-    return _stamp_test_impl(ctx, True)
+def _stamp_build_flag_is_true_impl(ctx):
+    return _stamp_build_flag_test_impl(ctx, True)
 
-def _skip_stamp_test_impl(ctx):
-    return _stamp_test_impl(ctx, False)
+def _stamp_build_flag_is_false_impl(ctx):
+    return _stamp_build_flag_test_impl(ctx, False)
 
-force_stamp_test = analysistest.make(_force_stamp_test_impl)
-skip_stamp_test = analysistest.make(_skip_stamp_test_impl)
+stamp_build_flag_is_true_test = analysistest.make(
+    _stamp_build_flag_is_true_impl,
+    config_settings = {
+        "//command_line_option:stamp": True,
+    },
+)
 
-_STAMP_VALUES = (0, 1)
+stamp_build_flag_is_false_test = analysistest.make(
+    _stamp_build_flag_is_false_impl,
+    config_settings = {
+        "//command_line_option:stamp": False,
+    },
+)
 
-def _define_test_targets():
-    for stamp_value in _STAMP_VALUES:
-        if stamp_value == 1:
-            name = "force_stamp"
-            features = ["force_stamp"]
+def _build_flag_tests():
+    tests = []
+    for stamp_value in _BUILD_FLAG_VALUES:
+        if stamp_value == "true":
+            name = "default_with_build_flag_on"
+            features = ["always_stamp"]
+            build_flag_stamp_test = stamp_build_flag_is_true_test
         else:
-            name = "skip_stamp"
-            features = ["skip_stamp"]
+            name = "default_with_build_flag_off"
+            features = ["never_stamp"]
+            build_flag_stamp_test = stamp_build_flag_is_false_test
 
         rust_library(
-            name = name,
+            name = "{}_lib".format(name),
             srcs = ["stamp.rs"],
+            rustc_env_files = ["stamp.env"],
             edition = "2018",
-            rustc_env_files = [":stamp.env"],
-            stamp = stamp_value,
-            crate_features = features,
-        )
-
-        rust_test(
-            name = "{}_unit_test".format(name),
-            crate = ":{}".format(name),
-            edition = "2018",
-            rustc_env_files = [":stamp.env"],
-            stamp = stamp_value,
-            crate_features = features,
+            # Building with --stamp should not affect rust libraries
+            crate_features = ["never_stamp"],
         )
 
         rust_binary(
             name = "{}_bin".format(name),
             srcs = ["stamp_main.rs"],
             edition = "2018",
-            deps = [":{}".format(name)],
-            rustc_env_files = [":stamp.env"],
-            stamp = stamp_value,
+            deps = ["{}_lib".format(name)],
+            rustc_env_files = ["stamp.env"],
             crate_features = features,
         )
 
+        rust_test(
+            name = "{}_test".format(name),
+            crate = "{}_lib".format(name),
+            edition = "2018",
+            rustc_env_files = ["stamp.env"],
+            # Building with --stamp should not affect tests
+            crate_features = ["never_stamp"],
+        )
+
+        build_flag_stamp_test(
+            name = "lib_{}_test".format(name),
+            target_under_test = "{}_lib".format(name),
+        )
+        build_flag_stamp_test(
+            name = "bin_{}_test".format(name),
+            target_under_test = "{}_bin".format(name),
+        )
+
+        build_flag_stamp_test(
+            name = "test_{}_test".format(name),
+            target_under_test = "{}_test".format(name),
+        )
+
+        tests.extend([
+            "lib_{}_test".format(name),
+            "bin_{}_test".format(name),
+            "test_{}_test".format(name),
+        ])
+    return tests
+
+def _attribute_stamp_test_impl(ctx, attribute_value, build_flag_value):
+    env = analysistest.begin(ctx)
+    target = analysistest.target_under_test(env)
+
+    action = target.actions[0]
+    assert_action_mnemonic(env, action, "Rustc")
+
+    if attribute_value == 1:
+        _assert_stamped(env, action)
+    elif attribute_value == 0:
+        _assert_not_stamped(env, action)
+    elif build_flag_value:
+        _assert_stamped(env, action)
+    else:
+        _assert_not_stamped(env, action)
+
+    return analysistest.end(env)
+
+def _always_stamp_build_flag_is_true_test_impl(ctx):
+    return _attribute_stamp_test_impl(ctx, attribute_value = 1, build_flag_value = True)
+
+def _always_stamp_build_flag_is_false_test_impl(ctx):
+    return _attribute_stamp_test_impl(ctx, attribute_value = 1, build_flag_value = False)
+
+def _never_stamp_build_flag_is_true_test_impl(ctx):
+    return _attribute_stamp_test_impl(ctx, attribute_value = 0, build_flag_value = True)
+
+def _never_stamp_build_flag_is_false_test_impl(ctx):
+    return _attribute_stamp_test_impl(ctx, attribute_value = 0, build_flag_value = False)
+
+def _consult_build_flag_value_is_true_test_impl(ctx):
+    return _attribute_stamp_test_impl(ctx, attribute_value = -1, build_flag_value = True)
+
+def _consult_build_flag_value_is_false_test_impl(ctx):
+    return _attribute_stamp_test_impl(ctx, attribute_value = -1, build_flag_value = False)
+
+always_stamp_test_build_flag_is_true_test = analysistest.make(
+    _always_stamp_build_flag_is_true_test_impl,
+    config_settings = {
+        "//command_line_option:stamp": True,
+    },
+)
+
+always_stamp_test_build_flag_is_false_test = analysistest.make(
+    _always_stamp_build_flag_is_false_test_impl,
+    config_settings = {
+        "//command_line_option:stamp": False,
+    },
+)
+
+never_stamp_test_build_flag_is_true_test = analysistest.make(
+    _never_stamp_build_flag_is_true_test_impl,
+    config_settings = {
+        "//command_line_option:stamp": True,
+    },
+)
+
+never_stamp_test_build_flag_is_false_test = analysistest.make(
+    _never_stamp_build_flag_is_false_test_impl,
+    config_settings = {
+        "//command_line_option:stamp": False,
+    },
+)
+
+consult_build_flag_value_is_true_test = analysistest.make(
+    _consult_build_flag_value_is_true_test_impl,
+    config_settings = {
+        "//command_line_option:stamp": True,
+    },
+)
+
+consult_build_flag_value_is_false_test = analysistest.make(
+    _consult_build_flag_value_is_false_test_impl,
+    config_settings = {
+        "//command_line_option:stamp": False,
+    },
+)
+
+def _stamp_attribute_tests():
+    tests = []
+
+    for stamp_value in _STAMP_ATTR_VALUES:
+        for flag_value in _BUILD_FLAG_VALUES:
+            if stamp_value == 1:
+                name = "always_stamp_build_flag_{}".format(flag_value)
+                features = ["always_stamp_build_flag_{}".format(flag_value)]
+                stamp_attr_test = always_stamp_test_build_flag_is_true_test if flag_value == "true" else always_stamp_test_build_flag_is_false_test
+            elif stamp_value == 0:
+                name = "never_stamp_build_flag_{}".format(flag_value)
+                features = ["never_stamp_build_flag_{}".format(flag_value)]
+                stamp_attr_test = never_stamp_test_build_flag_is_true_test if flag_value == "true" else never_stamp_test_build_flag_is_false_test
+            else:
+                name = "consult_cmdline_value_is_{}".format(flag_value)
+                features = ["consult_cmdline_value_is_{}".format(flag_value)]
+                stamp_attr_test = consult_build_flag_value_is_true_test if flag_value == "true" else consult_build_flag_value_is_false_test
+
+            rust_library(
+                name = "{}_lib".format(name),
+                srcs = ["stamp.rs"],
+                edition = "2018",
+                rustc_env_files = [":stamp.env"],
+                stamp = stamp_value,
+                crate_features = features,
+            )
+
+            rust_test(
+                name = "{}_unit_test".format(name),
+                crate = ":{}_lib".format(name),
+                edition = "2018",
+                rustc_env_files = [":stamp.env"],
+                stamp = stamp_value,
+                crate_features = features,
+                # We disable this test so that it doesn't try to run with bazel test //test/...
+                # The reason for this is because then it is sensitive to the --stamp value which
+                # we override in the unit test implementation.
+                tags = ["manual"],
+            )
+
+            rust_binary(
+                name = "{}_bin".format(name),
+                srcs = ["stamp_main.rs"],
+                edition = "2018",
+                deps = [":{}_lib".format(name)],
+                rustc_env_files = [":stamp.env"],
+                stamp = stamp_value,
+                crate_features = features,
+            )
+
+            stamp_attr_test(
+                name = "lib_{}_test".format(name),
+                target_under_test = "{}_lib".format(name),
+            )
+            stamp_attr_test(
+                name = "bin_{}_test".format(name),
+                target_under_test = "{}_bin".format(name),
+            )
+
+            stamp_attr_test(
+                name = "test_{}_test".format(name),
+                target_under_test = "{}_unit_test".format(name),
+            )
+
+            tests.extend([
+                "lib_{}_test".format(name),
+                "bin_{}_test".format(name),
+                "test_{}_test".format(name),
+            ])
+    return tests
+
 def stamp_test_suite(name):
     """Entry-point macro called from the BUILD file.
 
     Args:
         name (str): Name of the macro.
     """
-    _define_test_targets()
-
-    tests = []
-
-    for stamp_value in _STAMP_VALUES:
-        if stamp_value == 1:
-            test_name = "force_stamp"
-            stamp_test = force_stamp_test
-        else:
-            test_name = "skip_stamp"
-            stamp_test = skip_stamp_test
-
-        stamp_test(
-            name = "lib_{}_test".format(test_name),
-            target_under_test = Label("//test/unit/stamp:{}".format(test_name)),
-        )
-
-        stamp_test(
-            name = "test_{}_test".format(test_name),
-            target_under_test = Label("//test/unit/stamp:{}_unit_test".format(test_name)),
-        )
-
-        stamp_test(
-            name = "bin_{}_test".format(test_name),
-            target_under_test = Label("//test/unit/stamp:{}_bin".format(test_name)),
-        )
-
-        tests.extend([
-            "lib_{}_test".format(test_name),
-            "test_{}_test".format(test_name),
-            "bin_{}_test".format(test_name),
-        ])
+    tests = _build_flag_tests() + _stamp_attribute_tests()
 
     native.test_suite(
         name = name,
diff --git a/util/process_wrapper/options.rs b/util/process_wrapper/options.rs
index f44c70e..fdd60b4 100644
--- a/util/process_wrapper/options.rs
+++ b/util/process_wrapper/options.rs
@@ -51,6 +51,7 @@
     // Process argument list until -- is encountered.
     // Everything after is sent to the child process.
     let mut subst_mapping_raw = None;
+    let mut stable_status_file_raw = None;
     let mut volatile_status_file_raw = None;
     let mut env_file_raw = None;
     let mut arg_file_raw = None;
@@ -62,6 +63,7 @@
     let mut rustc_output_format_raw = None;
     let mut flags = Flags::new();
     flags.define_repeated_flag("--subst", "", &mut subst_mapping_raw);
+    flags.define_flag("--stable-status-file", "", &mut stable_status_file_raw);
     flags.define_flag("--volatile-status-file", "", &mut volatile_status_file_raw);
     flags.define_repeated_flag(
         "--env-file",
@@ -134,9 +136,10 @@
             Ok((key.to_owned(), v))
         })
         .collect::<Result<Vec<(String, String)>, OptionError>>()?;
-    let stamp_mappings =
+    let stable_stamp_mappings =
+        stable_status_file_raw.map_or_else(Vec::new, |s| read_stamp_status_to_array(s).unwrap());
+    let volatile_stamp_mappings =
         volatile_status_file_raw.map_or_else(Vec::new, |s| read_stamp_status_to_array(s).unwrap());
-
     let environment_file_block = env_from_files(env_file_raw.unwrap_or_default())?;
     let mut file_arguments = args_from_file(arg_file_raw.unwrap_or_default())?;
     // Process --copy-output
@@ -175,7 +178,12 @@
 
     // Prepare the environment variables, unifying those read from files with the ones
     // of the current process.
-    let vars = environment_block(environment_file_block, &stamp_mappings, &subst_mappings);
+    let vars = environment_block(
+        environment_file_block,
+        &stable_stamp_mappings,
+        &volatile_stamp_mappings,
+        &subst_mappings,
+    );
     // Append all the arguments fetched from files to those provided via command line.
     child_args.append(&mut file_arguments);
     let child_args = prepare_args(child_args, &subst_mappings);
@@ -240,7 +248,8 @@
 
 fn environment_block(
     environment_file_block: HashMap<String, String>,
-    stamp_mappings: &[(String, String)],
+    stable_stamp_mappings: &[(String, String)],
+    volatile_stamp_mappings: &[(String, String)],
     subst_mappings: &[(String, String)],
 ) -> HashMap<String, String> {
     // Taking all environment variables from the current process
@@ -250,7 +259,7 @@
     // This is simpler than needing to track duplicates and explicitly override
     // them.
     environment_variables.extend(environment_file_block.into_iter());
-    for (f, replace_with) in stamp_mappings {
+    for (f, replace_with) in &[stable_stamp_mappings, volatile_stamp_mappings].concat() {
         for value in environment_variables.values_mut() {
             let from = format!("{{{}}}", f);
             let new = value.replace(from.as_str(), replace_with);