Add extra_rustc_flags_for_crate_types. (#2431)

This allows extra rustc flags that will only apply to library targets to
be set by the toolchain. Cross language LTO needs '-C linker-plugin-lto'
on libraries, how passing this flag to rust_binary generated bloat.
Adding this attribute resolves this issue.

Change-Id: Iba725fab1b1941e9586ff97cd71ec3bc1dfc1523

---------

Co-authored-by: UebelAndre <github@uebelandre.com>
diff --git a/docs/flatten.md b/docs/flatten.md
index 7fa9bfc..24c6baa 100644
--- a/docs/flatten.md
+++ b/docs/flatten.md
@@ -1178,9 +1178,9 @@
 rust_toolchain(<a href="#rust_toolchain-name">name</a>, <a href="#rust_toolchain-allocator_library">allocator_library</a>, <a href="#rust_toolchain-binary_ext">binary_ext</a>, <a href="#rust_toolchain-cargo">cargo</a>, <a href="#rust_toolchain-clippy_driver">clippy_driver</a>, <a href="#rust_toolchain-debug_info">debug_info</a>,
                <a href="#rust_toolchain-default_edition">default_edition</a>, <a href="#rust_toolchain-dylib_ext">dylib_ext</a>, <a href="#rust_toolchain-env">env</a>, <a href="#rust_toolchain-exec_triple">exec_triple</a>, <a href="#rust_toolchain-experimental_link_std_dylib">experimental_link_std_dylib</a>,
                <a href="#rust_toolchain-experimental_use_cc_common_link">experimental_use_cc_common_link</a>, <a href="#rust_toolchain-extra_exec_rustc_flags">extra_exec_rustc_flags</a>, <a href="#rust_toolchain-extra_rustc_flags">extra_rustc_flags</a>,
-               <a href="#rust_toolchain-global_allocator_library">global_allocator_library</a>, <a href="#rust_toolchain-llvm_cov">llvm_cov</a>, <a href="#rust_toolchain-llvm_profdata">llvm_profdata</a>, <a href="#rust_toolchain-llvm_tools">llvm_tools</a>, <a href="#rust_toolchain-opt_level">opt_level</a>,
-               <a href="#rust_toolchain-per_crate_rustc_flags">per_crate_rustc_flags</a>, <a href="#rust_toolchain-rust_doc">rust_doc</a>, <a href="#rust_toolchain-rust_std">rust_std</a>, <a href="#rust_toolchain-rustc">rustc</a>, <a href="#rust_toolchain-rustc_lib">rustc_lib</a>, <a href="#rust_toolchain-rustfmt">rustfmt</a>, <a href="#rust_toolchain-staticlib_ext">staticlib_ext</a>,
-               <a href="#rust_toolchain-stdlib_linkflags">stdlib_linkflags</a>, <a href="#rust_toolchain-target_json">target_json</a>, <a href="#rust_toolchain-target_triple">target_triple</a>)
+               <a href="#rust_toolchain-extra_rustc_flags_for_crate_types">extra_rustc_flags_for_crate_types</a>, <a href="#rust_toolchain-global_allocator_library">global_allocator_library</a>, <a href="#rust_toolchain-llvm_cov">llvm_cov</a>, <a href="#rust_toolchain-llvm_profdata">llvm_profdata</a>,
+               <a href="#rust_toolchain-llvm_tools">llvm_tools</a>, <a href="#rust_toolchain-opt_level">opt_level</a>, <a href="#rust_toolchain-per_crate_rustc_flags">per_crate_rustc_flags</a>, <a href="#rust_toolchain-rust_doc">rust_doc</a>, <a href="#rust_toolchain-rust_std">rust_std</a>, <a href="#rust_toolchain-rustc">rustc</a>, <a href="#rust_toolchain-rustc_lib">rustc_lib</a>,
+               <a href="#rust_toolchain-rustfmt">rustfmt</a>, <a href="#rust_toolchain-staticlib_ext">staticlib_ext</a>, <a href="#rust_toolchain-stdlib_linkflags">stdlib_linkflags</a>, <a href="#rust_toolchain-target_json">target_json</a>, <a href="#rust_toolchain-target_triple">target_triple</a>)
 </pre>
 
 Declares a Rust toolchain for use.
@@ -1246,6 +1246,7 @@
 | <a id="rust_toolchain-experimental_use_cc_common_link"></a>experimental_use_cc_common_link |  Label to a boolean build setting that controls whether cc_common.link is used to link rust binaries.   | <a href="https://bazel.build/concepts/labels">Label</a> | optional | <code>//rust/settings:experimental_use_cc_common_link</code> |
 | <a id="rust_toolchain-extra_exec_rustc_flags"></a>extra_exec_rustc_flags |  Extra flags to pass to rustc in exec configuration   | List of strings | optional | <code>[]</code> |
 | <a id="rust_toolchain-extra_rustc_flags"></a>extra_rustc_flags |  Extra flags to pass to rustc in non-exec configuration   | List of strings | optional | <code>[]</code> |
+| <a id="rust_toolchain-extra_rustc_flags_for_crate_types"></a>extra_rustc_flags_for_crate_types |  Extra flags to pass to rustc based on crate type   | <a href="https://bazel.build/rules/lib/dict">Dictionary: String -> List of strings</a> | optional | <code>{}</code> |
 | <a id="rust_toolchain-global_allocator_library"></a>global_allocator_library |  Target that provides allocator functions for when a global allocator is present.   | <a href="https://bazel.build/concepts/labels">Label</a> | optional | <code>@rules_rust//ffi/cc/global_allocator_library</code> |
 | <a id="rust_toolchain-llvm_cov"></a>llvm_cov |  The location of the <code>llvm-cov</code> binary. Can be a direct source or a filegroup containing one item. If None, rust code is not instrumented for coverage.   | <a href="https://bazel.build/concepts/labels">Label</a> | optional | <code>None</code> |
 | <a id="rust_toolchain-llvm_profdata"></a>llvm_profdata |  The location of the <code>llvm-profdata</code> binary. Can be a direct source or a filegroup containing one item. If <code>llvm_cov</code> is None, this can be None as well and rust code is not instrumented for coverage.   | <a href="https://bazel.build/concepts/labels">Label</a> | optional | <code>None</code> |
diff --git a/docs/rust_repositories.md b/docs/rust_repositories.md
index 0175c53..443b07a 100644
--- a/docs/rust_repositories.md
+++ b/docs/rust_repositories.md
@@ -39,9 +39,9 @@
 rust_toolchain(<a href="#rust_toolchain-name">name</a>, <a href="#rust_toolchain-allocator_library">allocator_library</a>, <a href="#rust_toolchain-binary_ext">binary_ext</a>, <a href="#rust_toolchain-cargo">cargo</a>, <a href="#rust_toolchain-clippy_driver">clippy_driver</a>, <a href="#rust_toolchain-debug_info">debug_info</a>,
                <a href="#rust_toolchain-default_edition">default_edition</a>, <a href="#rust_toolchain-dylib_ext">dylib_ext</a>, <a href="#rust_toolchain-env">env</a>, <a href="#rust_toolchain-exec_triple">exec_triple</a>, <a href="#rust_toolchain-experimental_link_std_dylib">experimental_link_std_dylib</a>,
                <a href="#rust_toolchain-experimental_use_cc_common_link">experimental_use_cc_common_link</a>, <a href="#rust_toolchain-extra_exec_rustc_flags">extra_exec_rustc_flags</a>, <a href="#rust_toolchain-extra_rustc_flags">extra_rustc_flags</a>,
-               <a href="#rust_toolchain-global_allocator_library">global_allocator_library</a>, <a href="#rust_toolchain-llvm_cov">llvm_cov</a>, <a href="#rust_toolchain-llvm_profdata">llvm_profdata</a>, <a href="#rust_toolchain-llvm_tools">llvm_tools</a>, <a href="#rust_toolchain-opt_level">opt_level</a>,
-               <a href="#rust_toolchain-per_crate_rustc_flags">per_crate_rustc_flags</a>, <a href="#rust_toolchain-rust_doc">rust_doc</a>, <a href="#rust_toolchain-rust_std">rust_std</a>, <a href="#rust_toolchain-rustc">rustc</a>, <a href="#rust_toolchain-rustc_lib">rustc_lib</a>, <a href="#rust_toolchain-rustfmt">rustfmt</a>, <a href="#rust_toolchain-staticlib_ext">staticlib_ext</a>,
-               <a href="#rust_toolchain-stdlib_linkflags">stdlib_linkflags</a>, <a href="#rust_toolchain-target_json">target_json</a>, <a href="#rust_toolchain-target_triple">target_triple</a>)
+               <a href="#rust_toolchain-extra_rustc_flags_for_crate_types">extra_rustc_flags_for_crate_types</a>, <a href="#rust_toolchain-global_allocator_library">global_allocator_library</a>, <a href="#rust_toolchain-llvm_cov">llvm_cov</a>, <a href="#rust_toolchain-llvm_profdata">llvm_profdata</a>,
+               <a href="#rust_toolchain-llvm_tools">llvm_tools</a>, <a href="#rust_toolchain-opt_level">opt_level</a>, <a href="#rust_toolchain-per_crate_rustc_flags">per_crate_rustc_flags</a>, <a href="#rust_toolchain-rust_doc">rust_doc</a>, <a href="#rust_toolchain-rust_std">rust_std</a>, <a href="#rust_toolchain-rustc">rustc</a>, <a href="#rust_toolchain-rustc_lib">rustc_lib</a>,
+               <a href="#rust_toolchain-rustfmt">rustfmt</a>, <a href="#rust_toolchain-staticlib_ext">staticlib_ext</a>, <a href="#rust_toolchain-stdlib_linkflags">stdlib_linkflags</a>, <a href="#rust_toolchain-target_json">target_json</a>, <a href="#rust_toolchain-target_triple">target_triple</a>)
 </pre>
 
 Declares a Rust toolchain for use.
@@ -107,6 +107,7 @@
 | <a id="rust_toolchain-experimental_use_cc_common_link"></a>experimental_use_cc_common_link |  Label to a boolean build setting that controls whether cc_common.link is used to link rust binaries.   | <a href="https://bazel.build/concepts/labels">Label</a> | optional | <code>//rust/settings:experimental_use_cc_common_link</code> |
 | <a id="rust_toolchain-extra_exec_rustc_flags"></a>extra_exec_rustc_flags |  Extra flags to pass to rustc in exec configuration   | List of strings | optional | <code>[]</code> |
 | <a id="rust_toolchain-extra_rustc_flags"></a>extra_rustc_flags |  Extra flags to pass to rustc in non-exec configuration   | List of strings | optional | <code>[]</code> |
+| <a id="rust_toolchain-extra_rustc_flags_for_crate_types"></a>extra_rustc_flags_for_crate_types |  Extra flags to pass to rustc based on crate type   | <a href="https://bazel.build/rules/lib/dict">Dictionary: String -> List of strings</a> | optional | <code>{}</code> |
 | <a id="rust_toolchain-global_allocator_library"></a>global_allocator_library |  Target that provides allocator functions for when a global allocator is present.   | <a href="https://bazel.build/concepts/labels">Label</a> | optional | <code>@rules_rust//ffi/cc/global_allocator_library</code> |
 | <a id="rust_toolchain-llvm_cov"></a>llvm_cov |  The location of the <code>llvm-cov</code> binary. Can be a direct source or a filegroup containing one item. If None, rust code is not instrumented for coverage.   | <a href="https://bazel.build/concepts/labels">Label</a> | optional | <code>None</code> |
 | <a id="rust_toolchain-llvm_profdata"></a>llvm_profdata |  The location of the <code>llvm-profdata</code> binary. Can be a direct source or a filegroup containing one item. If <code>llvm_cov</code> is None, this can be None as well and rust code is not instrumented for coverage.   | <a href="https://bazel.build/concepts/labels">Label</a> | optional | <code>None</code> |
diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl
index e101223..f6f632d 100644
--- a/rust/private/rustc.bzl
+++ b/rust/private/rustc.bzl
@@ -1070,6 +1070,9 @@
     if toolchain._rename_first_party_crates:
         env["RULES_RUST_THIRD_PARTY_DIR"] = toolchain._third_party_dir
 
+    if crate_info.type in toolchain.extra_rustc_flags_for_crate_types.keys():
+        rustc_flags.add_all(toolchain.extra_rustc_flags_for_crate_types[crate_info.type])
+
     if is_exec_configuration(ctx):
         rustc_flags.add_all(toolchain.extra_exec_rustc_flags)
     else:
diff --git a/rust/toolchain.bzl b/rust/toolchain.bzl
index 1d268ec..36c42b7 100644
--- a/rust/toolchain.bzl
+++ b/rust/toolchain.bzl
@@ -650,6 +650,7 @@
         staticlib_ext = ctx.attr.staticlib_ext,
         stdlib_linkflags = stdlib_linkflags_cc_info,
         extra_rustc_flags = ctx.attr.extra_rustc_flags,
+        extra_rustc_flags_for_crate_types = ctx.attr.extra_rustc_flags_for_crate_types,
         extra_exec_rustc_flags = ctx.attr.extra_exec_rustc_flags,
         per_crate_rustc_flags = ctx.attr.per_crate_rustc_flags,
         sysroot = sysroot_path,
@@ -747,6 +748,9 @@
         "extra_rustc_flags": attr.string_list(
             doc = "Extra flags to pass to rustc in non-exec configuration",
         ),
+        "extra_rustc_flags_for_crate_types": attr.string_list_dict(
+            doc = "Extra flags to pass to rustc based on crate type",
+        ),
         "global_allocator_library": attr.label(
             doc = "Target that provides allocator functions for when a global allocator is present.",
             default = "@rules_rust//ffi/cc/global_allocator_library",
diff --git a/test/toolchain/main.rs b/test/toolchain/main.rs
new file mode 100644
index 0000000..f328e4d
--- /dev/null
+++ b/test/toolchain/main.rs
@@ -0,0 +1 @@
+fn main() {}
diff --git a/test/toolchain/toolchain_test.bzl b/test/toolchain/toolchain_test.bzl
index 5c74fae..b61a351 100644
--- a/test/toolchain/toolchain_test.bzl
+++ b/test/toolchain/toolchain_test.bzl
@@ -2,18 +2,20 @@
 
 load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts")
 load("@bazel_skylib//rules:write_file.bzl", "write_file")
-load("//rust:defs.bzl", "rust_library")
+load("//rust:defs.bzl", "rust_library", "rust_shared_library")
 load("//rust:toolchain.bzl", "rust_stdlib_filegroup", "rust_toolchain")
 
 EXEC_TOOLCHAIN_FLAG = "missing"
 TOOLCHAIN_FLAG = "before"
 CONFIG_FLAG = "after"
+CRATE_FLAGS = {"cdylib": ["cdylib_flag"], "rlib": ["rlib_flag"]}
 
-def _toolchain_adds_rustc_flags_impl(ctx):
+def _toolchain_adds_rustc_flags_impl(ctx, crate_type):
     """ Tests adding extra_rustc_flags on the toolchain, asserts that:
 
     - extra_rustc_flags added by the toolchain are applied BEFORE flags added by a config on the commandline
     - The exec flags from the toolchain don't go on the commandline for a non-exec target
+    - crate type rustc flags are added
     """
     env = analysistest.begin(ctx)
     target = analysistest.target_under_test(env)
@@ -32,6 +34,27 @@
 
     asserts.true(
         env,
+        action.argv[-3] == CRATE_FLAGS[crate_type][0],
+        "Unexpected rustc flags: {}\nShould have contained: {}".format(
+            action.argv,
+            CRATE_FLAGS["rlib"],
+        ),
+    )
+
+    for type in CRATE_FLAGS.keys():
+        if type == crate_type:
+            continue
+        asserts.false(
+            env,
+            CRATE_FLAGS[type][0] in action.argv,
+            "Unexpected rustc flags: {}\nShould not contain: {}".format(
+                action.argv,
+                CRATE_FLAGS[type],
+            ),
+        )
+
+    asserts.true(
+        env,
         EXEC_TOOLCHAIN_FLAG not in action.argv,
         "Found exec toolchain flag ({}) in rustc flags: {}".format(EXEC_TOOLCHAIN_FLAG, action.argv),
     )
@@ -48,8 +71,22 @@
 
     return analysistest.end(env)
 
-toolchain_adds_rustc_flags_test = analysistest.make(
-    _toolchain_adds_rustc_flags_impl,
+def _toolchain_adds_rustc_flags_lib_impl(ctx):
+    return _toolchain_adds_rustc_flags_impl(ctx, "rlib")
+
+def _toolchain_adds_rustc_flags_shared_lib_impl(ctx):
+    return _toolchain_adds_rustc_flags_impl(ctx, "cdylib")
+
+toolchain_adds_rustc_flags_lib_test = analysistest.make(
+    _toolchain_adds_rustc_flags_lib_impl,
+    config_settings = {
+        str(Label("//:extra_rustc_flags")): [CONFIG_FLAG],
+        str(Label("//rust/settings:experimental_toolchain_generated_sysroot")): True,
+    },
+)
+
+toolchain_adds_rustc_flags_shared_lib_test = analysistest.make(
+    _toolchain_adds_rustc_flags_shared_lib_impl,
     config_settings = {
         str(Label("//:extra_rustc_flags")): [CONFIG_FLAG],
         str(Label("//rust/settings:experimental_toolchain_generated_sysroot")): True,
@@ -105,6 +142,12 @@
         edition = "2021",
     )
 
+    rust_shared_library(
+        name = "shared_lib",
+        srcs = ["lib.rs"],
+        edition = "2021",
+    )
+
     native.filegroup(
         name = "stdlib_srcs",
         srcs = ["config.txt"],
@@ -139,6 +182,7 @@
         stdlib_linkflags = [],
         extra_rustc_flags = [TOOLCHAIN_FLAG],
         extra_exec_rustc_flags = [EXEC_TOOLCHAIN_FLAG],
+        extra_rustc_flags_for_crate_types = CRATE_FLAGS,
         visibility = ["//visibility:public"],
     )
 
@@ -153,6 +197,11 @@
         dep = ":lib",
     )
 
+    extra_toolchain_wrapper(
+        name = "shared_lib_with_extra_toolchain",
+        dep = ":shared_lib",
+    )
+
 def _rust_stdlib_filegroup_provides_runfiles_test_impl(ctx):
     env = analysistest.begin(ctx)
     target = analysistest.target_under_test(env)
@@ -166,13 +215,23 @@
 )
 
 def toolchain_test_suite(name):
+    """ Instantiates tests for rust toolchains.
+
+    Args:
+        name: a name for the test suite
+    """
     _define_targets()
 
-    toolchain_adds_rustc_flags_test(
-        name = "toolchain_adds_rustc_flags_test",
+    toolchain_adds_rustc_flags_lib_test(
+        name = "toolchain_adds_rustc_flags_lib_test",
         target_under_test = ":lib_with_extra_toolchain",
     )
 
+    toolchain_adds_rustc_flags_shared_lib_test(
+        name = "toolchain_adds_rustc_flags_shared_lib_test",
+        target_under_test = ":shared_lib_with_extra_toolchain",
+    )
+
     rust_stdlib_filegroup_provides_runfiles_test(
         name = "rust_stdlib_filegroup_provides_runfiles_test",
         target_under_test = ":std_libs",
@@ -181,7 +240,8 @@
     native.test_suite(
         name = name,
         tests = [
-            ":toolchain_adds_rustc_flags_test",
+            ":toolchain_adds_rustc_flags_lib_test",
+            ":toolchain_adds_rustc_flags_shared_lib_test",
             ":rust_stdlib_filegroup_provides_runfiles_test",
         ],
     )