Add a generic additional_linker_inputs attribute on cc_binary rules.

This can be used to provide additional inputs that can be referenced by $(location) in linkopts.

RELNOTES: Add a generic additional_linker_inputs attribute on cc_binary rules.
PiperOrigin-RevId: 247973418
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/Expander.java b/src/main/java/com/google/devtools/build/lib/analysis/Expander.java
index 0e40e1e..e48ed8f 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/Expander.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/Expander.java
@@ -34,10 +34,19 @@
 
   private final RuleContext ruleContext;
   private final TemplateContext templateContext;
+  @Nullable ImmutableMap<Label, ImmutableCollection<Artifact>> labelMap;
 
   Expander(RuleContext ruleContext, TemplateContext templateContext) {
+    this(ruleContext, templateContext, /* labelMap= */ null);
+  }
+
+  Expander(
+      RuleContext ruleContext,
+      TemplateContext templateContext,
+      @Nullable ImmutableMap<Label, ImmutableCollection<Artifact>> labelMap) {
     this.ruleContext = ruleContext;
     this.templateContext = templateContext;
+    this.labelMap = labelMap;
   }
 
   /**
@@ -46,8 +55,8 @@
    */
   private Expander withLocations(boolean execPaths, boolean allowData) {
     TemplateContext newTemplateContext =
-        new LocationTemplateContext(templateContext, ruleContext, null, execPaths, allowData);
-    return new Expander(ruleContext, newTemplateContext);
+        new LocationTemplateContext(templateContext, ruleContext, labelMap, execPaths, allowData);
+    return new Expander(ruleContext, newTemplateContext, labelMap);
   }
 
   /**
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java b/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
index 8b62449..ca8da90 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
@@ -1136,6 +1136,10 @@
     return new Expander(this, getConfigurationMakeVariableContext());
   }
 
+  public Expander getExpander(ImmutableMap<Label, ImmutableCollection<Artifact>> labelMap) {
+    return new Expander(this, getConfigurationMakeVariableContext(), labelMap);
+  }
+
   /**
    * Returns a cached context that maps Make variable names (string) to values (string) without any
    * extra {@link MakeVariableSupplier}.
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/cpp/BazelCppRuleClasses.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/cpp/BazelCppRuleClasses.java
index f2d0fba..25b837a 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/cpp/BazelCppRuleClasses.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/cpp/BazelCppRuleClasses.java
@@ -472,6 +472,18 @@
     @Override
     public RuleClass build(RuleClass.Builder builder, RuleDefinitionEnvironment env) {
       return builder
+          /*<!-- #BLAZE_RULE($cc_binary_base).ATTRIBUTE(additional_linker_inputs) -->
+           Pass these files to the C++ linker command.
+           <p>
+           For example, compiled Windows .res files can be provided here to be embedded in
+           the binary target.
+           </p>
+          <!-- #END_BLAZE_RULE.ATTRIBUTE -->*/
+          .add(
+              attr("additional_linker_inputs", LABEL_LIST)
+                  .orderIndependent()
+                  .direct_compile_time_input()
+                  .allowedFileTypes(FileTypeSet.ANY_FILE))
           /*<!-- #BLAZE_RULE($cc_binary_base).ATTRIBUTE(malloc) -->
           Override the default dependency on malloc.
           <p>
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcBinary.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcBinary.java
index 5a08694..69067a6 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcBinary.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcBinary.java
@@ -360,6 +360,7 @@
             .merge(precompiledFileObjects)
             .merge(compilationInfo.getCcCompilationOutputs())
             .build();
+    Iterable<Artifact> additionalLinkerInputs = common.getAdditionalLinkerInputs();
 
     // Allows the dynamic library generated for code of default dynamic mode targets to be linked
     // separately. The main use case for default dynamic mode is the cc_test rule. The same behavior
@@ -479,6 +480,7 @@
             common,
             precompiledFiles,
             ccCompilationOutputs,
+            additionalLinkerInputs,
             ccLinkingOutputs,
             ccCompilationContext,
             fake,
@@ -672,6 +674,7 @@
       CcCommon common,
       PrecompiledFiles precompiledFiles,
       CcCompilationOutputs ccCompilationOutputs,
+      Iterable<Artifact> additionalLinkerInputs,
       CcLinkingOutputs ccLinkingOutputs,
       CcCompilationContext ccCompilationContext,
       boolean fake,
@@ -708,7 +711,8 @@
                 ruleContext.getSymbolGenerator())
             .setGrepIncludes(CppHelper.getGrepIncludes(ruleContext))
             .setIsStampingEnabled(AnalysisUtils.isStampingEnabled(ruleContext))
-            .setTestOrTestOnlyTarget(ruleContext.isTestTarget() || ruleContext.isTestOnlyTarget());
+            .setTestOrTestOnlyTarget(ruleContext.isTestTarget() || ruleContext.isTestOnlyTarget())
+            .addNonCodeLinkerInputs(additionalLinkerInputs);
 
     CcInfo depsCcInfo = CcInfo.builder().setCcLinkingContext(depsCcLinkingContext).build();
 
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCommon.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCommon.java
index 1a9ad14..36ecb13 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCommon.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCommon.java
@@ -676,6 +676,14 @@
   }
 
   /**
+   * Returns all additional linker inputs specified in the |additional_linker_inputs| attribute of
+   * the rule.
+   */
+  List<Artifact> getAdditionalLinkerInputs() {
+    return ruleContext.getPrerequisiteArtifacts("additional_linker_inputs", Mode.TARGET).list();
+  }
+
+  /**
    * Replaces shared library artifact with mangled symlink and creates related
    * symlink action. For artifacts that should retain filename (e.g. libraries
    * with SONAME tag), link is created to the parent directory instead.
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppHelper.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppHelper.java
index d0c381d..4b8e27d 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppHelper.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppHelper.java
@@ -15,12 +15,14 @@
 package com.google.devtools.build.lib.rules.cpp;
 
 import static com.google.devtools.build.lib.packages.BuildType.LABEL;
+import static com.google.devtools.build.lib.packages.BuildType.LABEL_LIST;
 import static com.google.devtools.build.lib.packages.BuildType.NODEP_LABEL;
 import static java.nio.charset.StandardCharsets.UTF_8;
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Function;
 import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableCollection;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Iterables;
@@ -32,8 +34,10 @@
 import com.google.devtools.build.lib.actions.MiddlemanFactory;
 import com.google.devtools.build.lib.actions.ParamFileInfo;
 import com.google.devtools.build.lib.actions.ParameterFile;
+import com.google.devtools.build.lib.analysis.AliasProvider;
 import com.google.devtools.build.lib.analysis.AnalysisUtils;
 import com.google.devtools.build.lib.analysis.Expander;
+import com.google.devtools.build.lib.analysis.FileProvider;
 import com.google.devtools.build.lib.analysis.PlatformConfiguration;
 import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder;
 import com.google.devtools.build.lib.analysis.RuleContext;
@@ -165,7 +169,18 @@
   public static List<String> expandLinkopts(
       RuleContext ruleContext, String attrName, Iterable<String> values) {
     List<String> result = new ArrayList<>();
-    Expander expander = ruleContext.getExpander().withDataExecLocations();
+    ImmutableMap.Builder<Label, ImmutableCollection<Artifact>> builder = ImmutableMap.builder();
+
+    if (ruleContext.attributes().has("additional_linker_inputs", LABEL_LIST)) {
+      for (TransitiveInfoCollection current :
+          ruleContext.getPrerequisites("additional_linker_inputs", Mode.TARGET)) {
+        builder.put(
+            AliasProvider.getDependencyLabel(current),
+            ImmutableList.copyOf(current.getProvider(FileProvider.class).getFilesToBuild()));
+      }
+    }
+
+    Expander expander = ruleContext.getExpander(builder.build()).withDataExecLocations();
     for (String value : values) {
       expander.tokenizeAndExpandMakeVars(result, attrName, value);
     }