Add extra_rustc_flag and extra_exec_rustc_flag (#1413)

* Add extra_rustc_flag and extra_exec_rustc_flag

* Restore Label wrappers

* Update docs/BUILD.bazel and docs/symbols.bzl

* Regenerate documentation

* Revert "Regenerate documentation"

This reverts commit 37a39e19f032595e841e48429cd0c91524961146.

* Regenerate documentation
diff --git a/BUILD.bazel b/BUILD.bazel
index 260db43..ff49a9c 100644
--- a/BUILD.bazel
+++ b/BUILD.bazel
@@ -1,5 +1,5 @@
 load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
-load("//rust:defs.bzl", "capture_clippy_output", "clippy_flags", "error_format", "extra_exec_rustc_flags", "extra_rustc_flags")
+load("//rust:defs.bzl", "capture_clippy_output", "clippy_flags", "error_format", "extra_exec_rustc_flag", "extra_exec_rustc_flags", "extra_rustc_flag", "extra_rustc_flags")
 
 exports_files(["LICENSE"])
 
@@ -55,6 +55,12 @@
     visibility = ["//visibility:public"],
 )
 
+extra_rustc_flag(
+    name = "extra_rustc_flag",
+    build_setting_default = "",
+    visibility = ["//visibility:public"],
+)
+
 # This setting may be used to pass extra options to rustc from the command line
 # in exec configuration.
 # It applies across all targets whereas the rustc_flags option on targets applies only
@@ -65,6 +71,12 @@
     visibility = ["//visibility:public"],
 )
 
+extra_exec_rustc_flag(
+    name = "extra_exec_rustc_flag",
+    build_setting_default = "",
+    visibility = ["//visibility:public"],
+)
+
 # This setting is used by the clippy rules. See https://bazelbuild.github.io/rules_rust/rust_clippy.html
 label_flag(
     name = "clippy.toml",
diff --git a/docs/BUILD.bazel b/docs/BUILD.bazel
index 3b3c30b..9427b06 100644
--- a/docs/BUILD.bazel
+++ b/docs/BUILD.bazel
@@ -54,6 +54,7 @@
             "rust_test",
             "rust_test_suite",
             "error_format",
+            "extra_rustc_flag",
             "extra_rustc_flags",
             "capture_clippy_output",
         ],
diff --git a/docs/defs.md b/docs/defs.md
index b1c44bc..69189ee 100644
--- a/docs/defs.md
+++ b/docs/defs.md
@@ -9,6 +9,7 @@
 * [rust_test](#rust_test)
 * [rust_test_suite](#rust_test_suite)
 * [error_format](#error_format)
+* [extra_rustc_flag](#extra_rustc_flag)
 * [extra_rustc_flags](#extra_rustc_flags)
 * [capture_clippy_output](#capture_clippy_output)
 
@@ -48,6 +49,24 @@
 | <a id="error_format-name"></a>name |  A unique name for this target.   | <a href="https://bazel.build/docs/build-ref.html#name">Name</a> | required |  |
 
 
+<a id="extra_rustc_flag"></a>
+
+## extra_rustc_flag
+
+<pre>
+extra_rustc_flag(<a href="#extra_rustc_flag-name">name</a>)
+</pre>
+
+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.
+
+**ATTRIBUTES**
+
+
+| Name  | Description | Type | Mandatory | Default |
+| :------------- | :------------- | :------------- | :------------- | :------------- |
+| <a id="extra_rustc_flag-name"></a>name |  A unique name for this target.   | <a href="https://bazel.build/docs/build-ref.html#name">Name</a> | required |  |
+
+
 <a id="extra_rustc_flags"></a>
 
 ## extra_rustc_flags
diff --git a/docs/flatten.md b/docs/flatten.md
index 34b50b6..f524f26 100644
--- a/docs/flatten.md
+++ b/docs/flatten.md
@@ -8,6 +8,7 @@
 * [cargo_build_script](#cargo_build_script)
 * [cargo_env](#cargo_env)
 * [error_format](#error_format)
+* [extra_rustc_flag](#extra_rustc_flag)
 * [extra_rustc_flags](#extra_rustc_flags)
 * [fail_when_enabled](#fail_when_enabled)
 * [incompatible_flag](#incompatible_flag)
@@ -121,6 +122,24 @@
 | <a id="error_format-name"></a>name |  A unique name for this target.   | <a href="https://bazel.build/docs/build-ref.html#name">Name</a> | required |  |
 
 
+<a id="extra_rustc_flag"></a>
+
+## extra_rustc_flag
+
+<pre>
+extra_rustc_flag(<a href="#extra_rustc_flag-name">name</a>)
+</pre>
+
+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.
+
+**ATTRIBUTES**
+
+
+| Name  | Description | Type | Mandatory | Default |
+| :------------- | :------------- | :------------- | :------------- | :------------- |
+| <a id="extra_rustc_flag-name"></a>name |  A unique name for this target.   | <a href="https://bazel.build/docs/build-ref.html#name">Name</a> | required |  |
+
+
 <a id="extra_rustc_flags"></a>
 
 ## extra_rustc_flags
diff --git a/docs/symbols.bzl b/docs/symbols.bzl
index 1a3239d..07151d0 100644
--- a/docs/symbols.bzl
+++ b/docs/symbols.bzl
@@ -48,6 +48,7 @@
     "@rules_rust//rust:defs.bzl",
     _capture_clippy_output = "capture_clippy_output",
     _error_format = "error_format",
+    _extra_rustc_flag = "extra_rustc_flag",
     _extra_rustc_flags = "extra_rustc_flags",
     _rust_analyzer_aspect = "rust_analyzer_aspect",
     _rust_binary = "rust_binary",
@@ -158,6 +159,7 @@
 rustfmt_test = _rustfmt_test
 
 error_format = _error_format
+extra_rustc_flag = _extra_rustc_flag
 extra_rustc_flags = _extra_rustc_flags
 incompatible_flag = _incompatible_flag
 capture_clippy_output = _capture_clippy_output
diff --git a/rust/defs.bzl b/rust/defs.bzl
index 4dc6779..a0560b5 100644
--- a/rust/defs.bzl
+++ b/rust/defs.bzl
@@ -43,7 +43,9 @@
 load(
     "//rust/private:rustc.bzl",
     _error_format = "error_format",
+    _extra_exec_rustc_flag = "extra_exec_rustc_flag",
     _extra_exec_rustc_flags = "extra_exec_rustc_flags",
+    _extra_rustc_flag = "extra_rustc_flag",
     _extra_rustc_flags = "extra_rustc_flags",
 )
 load(
@@ -102,9 +104,15 @@
 error_format = _error_format
 # See @rules_rust//rust/private:rustc.bzl for a complete description.
 
+extra_rustc_flag = _extra_rustc_flag
+# See @rules_rust//rust/private:rustc.bzl for a complete description.
+
 extra_rustc_flags = _extra_rustc_flags
 # See @rules_rust//rust/private:rustc.bzl for a complete description.
 
+extra_exec_rustc_flag = _extra_exec_rustc_flag
+# See @rules_rust//rust/private:rustc.bzl for a complete description.
+
 extra_exec_rustc_flags = _extra_exec_rustc_flags
 # See @rules_rust//rust/private:rustc.bzl for a complete description.
 
diff --git a/rust/private/clippy.bzl b/rust/private/clippy.bzl
index e7953a7..ec94ae7 100644
--- a/rust/private/clippy.bzl
+++ b/rust/private/clippy.bzl
@@ -215,6 +215,7 @@
             doc = "The desired `--error-format` flags for clippy",
             default = "//:error_format",
         ),
+        "_extra_rustc_flag": attr.label(default = "//:extra_rustc_flag"),
         "_extra_rustc_flags": attr.label(default = "//:extra_rustc_flags"),
         "_process_wrapper": attr.label(
             doc = "A process wrapper for running clippy on all platforms",
diff --git a/rust/private/rust.bzl b/rust/private/rust.bzl
index 9af3a32..ce61713 100644
--- a/rust/private/rust.bzl
+++ b/rust/private/rust.bzl
@@ -669,9 +669,15 @@
     "_error_format": attr.label(
         default = Label("//:error_format"),
     ),
+    "_extra_exec_rustc_flag": attr.label(
+        default = Label("//:extra_exec_rustc_flag"),
+    ),
     "_extra_exec_rustc_flags": attr.label(
         default = Label("//:extra_exec_rustc_flags"),
     ),
+    "_extra_rustc_flag": attr.label(
+        default = Label("//:extra_rustc_flag"),
+    ),
     "_extra_rustc_flags": attr.label(
         default = Label("//:extra_rustc_flags"),
     ),
diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl
index 6cdbefe..7d2c1dc 100644
--- a/rust/private/rustc.bzl
+++ b/rust/private/rustc.bzl
@@ -837,9 +837,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(
@@ -1520,6 +1526,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)
 
@@ -1532,3 +1550,15 @@
     implementation = _extra_exec_rustc_flags_impl,
     build_setting = config.string_list(flag = True),
 )
+
+def _extra_exec_rustc_flag_impl(ctx):
+    return ExtraExecRustcFlagsInfo(extra_exec_rustc_flags = [f for f in ctx.build_setting_value if f != ""])
+
+extra_exec_rustc_flag = rule(
+    doc = (
+        "Add additional rustc_flags in the exec configuration from the command line with `--@rules_rust//:extra_exec_rustc_flag`. " +
+        "Multiple uses are accumulated and appended after the extra_exec_rustc_flags."
+    ),
+    implementation = _extra_exec_rustc_flag_impl,
+    build_setting = config.string(flag = True, allow_multiple = True),
+)
diff --git a/test/extra_exec_rustc_flags/BUILD.bazel b/test/extra_exec_rustc_flags/BUILD.bazel
index d3067b0..7a7b6ef 100644
--- a/test/extra_exec_rustc_flags/BUILD.bazel
+++ b/test/extra_exec_rustc_flags/BUILD.bazel
@@ -21,15 +21,29 @@
 with_extra_exec_rustc_flags_cfg(
     name = "lib_with_exec_flags_do_not_build_directly",
     srcs = ["lib_do_not_build_directly"],
+    extra_exec_rustc_flag = [],
     extra_exec_rustc_flags = ["--cfg=bazel_exec"],
     tags = ["manual"],
 )
 
+with_extra_exec_rustc_flags_cfg(
+    name = "lib_with_singular_exec_flags_do_not_build_directly",
+    srcs = ["lib_do_not_build_directly"],
+    extra_exec_rustc_flag = ["--cfg=bazel_exec"],
+    extra_exec_rustc_flags = [],
+    tags = ["manual"],
+)
+
 with_exec_cfg(
     name = "lib",
     srcs = ["lib_with_exec_flags_do_not_build_directly"],
 )
 
+with_exec_cfg(
+    name = "lib_singular",
+    srcs = ["lib_with_singular_exec_flags_do_not_build_directly"],
+)
+
 # Checks that extra_exec_rustc_flags are not passed in non-exec configurations.
 # lib_no_exec.rs is a sample source file that fails to build if
 # `--cfg=bazel_exec` is present. The targets below are built in non-exec configurations,
@@ -43,14 +57,24 @@
 with_extra_exec_rustc_flags_cfg(
     name = "lib_no_exec_with_extra_build_flags",
     srcs = ["lib_no_exec"],
+    extra_exec_rustc_flag = [],
     extra_exec_rustc_flags = ["--cfg=bazel_exec"],
 )
 
+with_extra_exec_rustc_flags_cfg(
+    name = "lib_no_exec_with_singular_extra_build_flags",
+    srcs = ["lib_no_exec"],
+    extra_exec_rustc_flag = ["--cfg=bazel_exec"],
+    extra_exec_rustc_flags = [],
+)
+
 build_test(
     name = "lib_build",
     targets = [
         ":lib",
+        ":lib_singular",
         ":lib_no_exec",
         ":lib_no_exec_with_extra_build_flags",
+        ":lib_no_exec_with_singular_extra_build_flags",
     ],
 )
diff --git a/test/extra_exec_rustc_flags/defs.bzl b/test/extra_exec_rustc_flags/defs.bzl
index 790cdc5..e12998d 100644
--- a/test/extra_exec_rustc_flags/defs.bzl
+++ b/test/extra_exec_rustc_flags/defs.bzl
@@ -2,13 +2,14 @@
 
 def _extra_exec_rustc_flags_transition_impl(_settings, attr):
     return {
+        "//:extra_exec_rustc_flag": attr.extra_exec_rustc_flag,
         "//:extra_exec_rustc_flags": attr.extra_exec_rustc_flags,
     }
 
 _extra_exec_rustc_flags_transition = transition(
     implementation = _extra_exec_rustc_flags_transition_impl,
     inputs = [],
-    outputs = ["//:extra_exec_rustc_flags"],
+    outputs = ["//:extra_exec_rustc_flags", "//:extra_exec_rustc_flag"],
 )
 
 def _with_extra_exec_rustc_flags_cfg_impl(ctx):
@@ -17,6 +18,9 @@
 with_extra_exec_rustc_flags_cfg = rule(
     implementation = _with_extra_exec_rustc_flags_cfg_impl,
     attrs = {
+        "extra_exec_rustc_flag": attr.string_list(
+            mandatory = True,
+        ),
         "extra_exec_rustc_flags": attr.string_list(
             mandatory = True,
         ),
diff --git a/test/unit/extra_rustc_flags/extra_rustc_flags_test.bzl b/test/unit/extra_rustc_flags/extra_rustc_flags_test.bzl
index 87eb3b5..8e24580 100644
--- a/test/unit/extra_rustc_flags/extra_rustc_flags_test.bzl
+++ b/test/unit/extra_rustc_flags/extra_rustc_flags_test.bzl
@@ -58,6 +58,20 @@
     },
 )
 
+extra_rustc_flag_present_test = analysistest.make(
+    _extra_rustc_flags_present_test,
+    attrs = {
+        "lib_exec": attr.label(
+            mandatory = True,
+            cfg = "exec",
+        ),
+    },
+    config_settings = {
+        "@//:extra_rustc_flag": [EXTRA_FLAG],
+        "@//:extra_rustc_flags": [],
+    },
+)
+
 def _define_test_targets():
     rust_library(
         name = "lib",
@@ -85,6 +99,12 @@
         lib_exec = ":lib",
     )
 
+    extra_rustc_flag_present_test(
+        name = "extra_rustc_flag_present_test",
+        target_under_test = ":lib",
+        lib_exec = ":lib",
+    )
+
     native.test_suite(
         name = name,
         tests = [