C++: Make cc_binary's linking go through CcLinkingHelper.

CcLinkingHelper has new functionality to support transitive linking. This is
not exposed to Starlark yet.

GitHub issue #4570

This was rolled back because in the refactoring I started passing headers to the linking action that were not passed before. These headers have mistakes and cannot be pre-processed, so when they were passed as inputs to the linking action, the compilation action was executed whereas before it wasn't.

Fixed and added test to make sure the behavior is the same as before.

RELNOTES:none
PiperOrigin-RevId: 214785687
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 860bccc..71d508c 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
@@ -64,6 +64,7 @@
 import com.google.devtools.build.lib.vfs.PathFragment;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 
 /**
  * A ConfiguredTarget for <code>cc_binary</code> rules.
@@ -283,7 +284,7 @@
                   ruleContext.getConfiguration())
               .fromCommon(common)
               .addDeps(ImmutableList.of(CppHelper.mallocForTarget(ruleContext)))
-              .enableInterfaceSharedObjects()
+              .emitInterfaceSharedObjects(true)
               .setAlwayslink(false);
       ccLinkingOutputs = linkingHelper.link(ccCompilationOutputs);
     }
@@ -294,58 +295,10 @@
             linkingMode != Link.LinkingMode.DYNAMIC,
             isLinkShared(ruleContext),
             common.getLinkopts());
-    CppLinkActionBuilder linkActionBuilder =
-        determineLinkerArguments(
-            ruleContext,
-            ccToolchain,
-            featureConfiguration,
-            fdoProvider,
-            common,
-            precompiledFiles,
-            ccCompilationOutputs,
-            ccLinkingOutputs,
-            ccCompilationContext.getTransitiveCompilationPrerequisites(),
-            fake,
-            binary,
-            linkParams,
-            linkCompileOutputSeparately,
-            semantics);
-    linkActionBuilder.setUseTestOnlyFlags(ruleContext.isTestTarget());
-    if (linkingMode == Link.LinkingMode.DYNAMIC) {
-      linkActionBuilder.setRuntimeInputs(
-          ArtifactCategory.DYNAMIC_LIBRARY,
-          ccToolchain.getDynamicRuntimeLinkMiddleman(featureConfiguration),
-          ccToolchain.getDynamicRuntimeLinkInputs(featureConfiguration));
-    } else {
-      linkActionBuilder.setRuntimeInputs(
-          ArtifactCategory.STATIC_LIBRARY,
-          ccToolchain.getStaticRuntimeLinkMiddleman(featureConfiguration),
-          ccToolchain.getStaticRuntimeLinkInputs(featureConfiguration));
-      if (!cppConfiguration.disableEmittingStaticLibgcc()) {
-        // Only force a static link of libgcc if static runtime linking is enabled (which
-        // can't be true if runtimeInputs is empty).
-        // TODO(bazel-team): Move this to CcToolchain.
-        if (!ccToolchain.getStaticRuntimeLinkInputs(featureConfiguration).isEmpty()) {
-          linkActionBuilder.addLinkopt("-static-libgcc");
-        }
-      }
-    }
-
-    linkActionBuilder.setLinkType(linkType);
-    linkActionBuilder.setLinkingMode(linkingMode);
-    linkActionBuilder.setFake(fake);
-
-    if (CppLinkAction.enableSymbolsCounts(
-        cppConfiguration, ccToolchain.supportsGoldLinker(), fake, linkType)) {
-      linkActionBuilder.setSymbolCountsOutput(ruleContext.getBinArtifact(
-          CppLinkAction.symbolCountsFileName(binaryPath)));
-    }
 
     Artifact generatedDefFile = null;
-    Artifact interfaceLibrary = null;
+    Artifact customDefFile = null;
     if (isLinkShared(ruleContext)) {
-      linkActionBuilder.setLibraryIdentifier(CcLinkingOutputs.libraryIdentifierOf(binary));
-
       if (featureConfiguration.isEnabled(CppRuleClasses.TARGETS_WINDOWS)) {
         ImmutableList.Builder<Artifact> objectFiles = ImmutableList.builder();
         objectFiles.addAll(ccCompilationOutputs.getObjectFiles(false));
@@ -362,62 +315,56 @@
                 ruleContext.getPrerequisiteArtifact("$def_parser", Mode.HOST),
                 objectFiles.build(),
                 binary.getFilename());
-
-        if (CppHelper.shouldUseGeneratedDefFile(ruleContext, featureConfiguration)) {
-          linkActionBuilder.setDefFile(generatedDefFile);
-        }
-
-        Artifact customDefFile = common.getWinDefFile();
-        if (customDefFile != null) {
-          linkActionBuilder.setDefFile(customDefFile);
-        }
-
-        // If we are using a toolchain supporting interface library and targeting Windows, we build
-        // the interface library with the link action and add it to `interface_output` output group.
-        if (CppHelper.useInterfaceSharedObjects(cppConfiguration, ccToolchain)) {
-          interfaceLibrary = CppHelper.getLinkedArtifact(
-              ruleContext,
-              ccToolchain,
-              ruleContext.getConfiguration(),
-              LinkTargetType.INTERFACE_DYNAMIC_LIBRARY);
-          linkActionBuilder.setInterfaceOutput(interfaceLibrary);
-        }
+        customDefFile = common.getWinDefFile();
       }
     }
 
-    // Store immutable context for use in other *_binary rules that are implemented by
-    // linking the interpreter (Java, Python, etc.) together with native deps.
-    CppLinkAction.Context linkContext = new CppLinkAction.Context(linkActionBuilder);
     boolean usePic = usePic(ruleContext, ccToolchain);
 
-    if (linkActionBuilder.hasLtoBitcodeInputs()
-        && featureConfiguration.isEnabled(CppRuleClasses.THIN_LTO)) {
-      linkActionBuilder.setLtoIndexing(true);
-      linkActionBuilder.setUsePicForLtoBackendActions(usePic);
-      CppLinkAction indexAction = linkActionBuilder.build();
-      if (indexAction != null) {
-        ruleContext.registerAction(indexAction);
-      }
-
-      linkActionBuilder.setLtoIndexing(false);
-    }
-
     // On Windows, if GENERATE_PDB_FILE feature is enabled
     // then a pdb file will be built along with the executable.
     Artifact pdbFile = null;
     if (featureConfiguration.isEnabled(CppRuleClasses.GENERATE_PDB_FILE)) {
       pdbFile = ruleContext.getRelatedArtifact(binary.getRootRelativePath(), ".pdb");
-      linkActionBuilder.addActionOutput(pdbFile);
     }
 
-    CppLinkAction linkAction = linkActionBuilder.build();
-    Iterable<LtoBackendArtifacts> ltoBackendArtifacts =
-        linkActionBuilder.getAllLtoBackendArtifacts();
-    ruleContext.registerAction(linkAction);
-    LibraryToLink outputLibrary = linkAction.getOutputLibrary();
+    CcLinkingOutputs ccLinkingOutputsBinary =
+        createTransitiveLinkingActions(
+            ruleContext,
+            ccToolchain,
+            featureConfiguration,
+            fdoProvider,
+            common,
+            precompiledFiles,
+            ccCompilationOutputs,
+            ccLinkingOutputs,
+            ccCompilationContext,
+            fake,
+            binary,
+            linkParams,
+            linkCompileOutputSeparately,
+            semantics,
+            linkingMode,
+            cppConfiguration,
+            linkType,
+            binaryPath,
+            pdbFile,
+            generatedDefFile,
+            customDefFile);
+
+    // Store immutable context for use in other *_binary rules that are implemented by
+    // linking the interpreter (Java, Python, etc.) together with native deps.
+    CppLinkAction.Context linkContext = ccLinkingOutputsBinary.getCppLinkActionContext();
+
+    LibraryToLink outputLibrary = null;
+    List<LibraryToLink> dynamicLibrariesForRuntime =
+        ccLinkingOutputsBinary.getDynamicLibrariesForRuntime();
+    if (!dynamicLibrariesForRuntime.isEmpty()) {
+      Preconditions.checkState(dynamicLibrariesForRuntime.size() == 1);
+      outputLibrary = dynamicLibrariesForRuntime.get(0);
+    }
     Iterable<Artifact> fakeLinkerInputs =
-        fake ? linkAction.getInputs() : ImmutableList.<Artifact>of();
-    Artifact executable = linkAction.getLinkOutput();
+        fake ? ccLinkingOutputsBinary.getLinkActionInputs() : ImmutableList.<Artifact>of();
     CcLinkingOutputs.Builder linkingOutputsBuilder = new CcLinkingOutputs.Builder();
     if (isLinkShared(ruleContext)) {
       linkingOutputsBuilder.addDynamicLibraryForLinking(outputLibrary);
@@ -432,13 +379,13 @@
       linkingOutputsBuilder.addDynamicLibraryForRuntime(symlinkLibrary);
     }
     CcLinkingOutputs linkingOutputs = linkingOutputsBuilder.build();
-    NestedSet<Artifact> filesToBuild = NestedSetBuilder.create(Order.STABLE_ORDER, executable);
+    NestedSet<Artifact> filesToBuild = NestedSetBuilder.create(Order.STABLE_ORDER, binary);
 
     // Create the stripped binary, but don't add it to filesToBuild; it's only built when requested.
     Artifact strippedFile = ruleContext.getImplicitOutputArtifact(
         CppRuleClasses.CC_BINARY_STRIPPED);
     CppHelper.createStripAction(
-        ruleContext, ccToolchain, cppConfiguration, executable, strippedFile, featureConfiguration);
+        ruleContext, ccToolchain, cppConfiguration, binary, strippedFile, featureConfiguration);
 
     DwoArtifactsCollector dwoArtifacts =
         collectTransitiveDwoArtifacts(
@@ -447,7 +394,7 @@
             linkingMode,
             ccToolchain.useFission(),
             usePic,
-            ltoBackendArtifacts);
+            ccLinkingOutputsBinary.getAllLtoArtifacts());
     Artifact dwpFile =
         ruleContext.getImplicitOutputArtifact(CppRuleClasses.CC_BINARY_DEBUG_PACKAGE);
     createDebugPackagerActions(ruleContext, ccToolchain, dwpFile, dwoArtifacts);
@@ -497,8 +444,7 @@
             fake,
             compilationHelper.getCompilationUnitSources(),
             linkCompileOutputSeparately);
-    RunfilesSupport runfilesSupport = RunfilesSupport.withExecutable(
-        ruleContext, runfiles, executable);
+    RunfilesSupport runfilesSupport = RunfilesSupport.withExecutable(ruleContext, runfiles, binary);
 
     RuleConfiguredTargetBuilder ruleBuilder = new RuleConfiguredTargetBuilder(ruleContext);
     addTransitiveInfoProviders(
@@ -530,67 +476,77 @@
       ruleBuilder.addOutputGroup("def_file", generatedDefFile);
     }
 
-    if (interfaceLibrary != null) {
-      ruleBuilder.addOutputGroup("interface_library", interfaceLibrary);
+    List<LibraryToLink> dynamicLibraryForLinking =
+        ccLinkingOutputsBinary.getDynamicLibrariesForLinking();
+    if (!dynamicLibraryForLinking.isEmpty()) {
+      Preconditions.checkState(dynamicLibraryForLinking.size() == 1);
+      ruleBuilder.addOutputGroup(
+          "interface_library", dynamicLibraryForLinking.get(0).getOriginalLibraryArtifact());
     }
 
     return ruleBuilder
         .addProvider(RunfilesProvider.class, RunfilesProvider.simple(runfiles))
         .addProvider(
             DebugPackageProvider.class,
-            new DebugPackageProvider(
-                ruleContext.getLabel(), strippedFile, executable, explicitDwpFile))
-        .setRunfilesSupport(runfilesSupport, executable)
+            new DebugPackageProvider(ruleContext.getLabel(), strippedFile, binary, explicitDwpFile))
+        .setRunfilesSupport(runfilesSupport, binary)
         .addProvider(CppLinkAction.Context.class, linkContext)
         .addSkylarkTransitiveInfo(CcSkylarkApiProvider.NAME, new CcSkylarkApiProvider())
         .build();
   }
 
-  /**
-   * Given 'temps', traverse this target and its dependencies and collect up all the object files,
-   * libraries, linker options, linkstamps attributes and linker scripts.
-   */
-  private static CppLinkActionBuilder determineLinkerArguments(
-      RuleContext context,
-      CcToolchainProvider toolchain,
+  public static CcLinkingOutputs createTransitiveLinkingActions(
+      RuleContext ruleContext,
+      CcToolchainProvider ccToolchain,
       FeatureConfiguration featureConfiguration,
       FdoProvider fdoProvider,
       CcCommon common,
       PrecompiledFiles precompiledFiles,
-      CcCompilationOutputs compilationOutputs,
-      CcLinkingOutputs linkingOutputs,
-      ImmutableSet<Artifact> compilationPrerequisites,
+      CcCompilationOutputs ccCompilationOutputs,
+      CcLinkingOutputs ccLinkingOutputs,
+      CcCompilationContext ccCompilationContext,
       boolean fake,
       Artifact binary,
       CcLinkParams linkParams,
       boolean linkCompileOutputSeparately,
-      CppSemantics cppSemantics)
+      CppSemantics cppSemantics,
+      LinkingMode linkingMode,
+      CppConfiguration cppConfiguration,
+      LinkTargetType linkType,
+      PathFragment binaryPath,
+      Artifact pdbFile,
+      Artifact generatedDefFile,
+      Artifact customDefFile)
       throws InterruptedException, RuleErrorException {
-    CppLinkActionBuilder builder =
-        new CppLinkActionBuilder(
-                context, binary, toolchain, fdoProvider, featureConfiguration, cppSemantics)
-            .setCrosstoolInputs(toolchain.getLink())
-            .addNonCodeInputs(compilationPrerequisites);
-
-    // Either link in the .o files generated for the sources of this target or link in the
-    // generated dynamic library they are compiled into.
-    if (linkCompileOutputSeparately) {
-      for (LibraryToLink library : linkingOutputs.getDynamicLibrariesForLinking()) {
-        builder.addLibrary(library);
-      }
-    } else {
-      boolean usePic = usePic(context, toolchain);
-      Iterable<Artifact> objectFiles = compilationOutputs.getObjectFiles(usePic);
-
-      if (fake) {
-        builder.addFakeObjectFiles(objectFiles);
-      } else {
-        builder.addObjectFiles(objectFiles);
-      }
+    CcCompilationOutputs.Builder ccCompilationOutputsBuilder =
+        new CcCompilationOutputs.Builder()
+            .addPicObjectFiles(ccCompilationOutputs.getObjectFiles(/* usePic= */ true))
+            .addObjectFiles(ccCompilationOutputs.getObjectFiles(/* usePic= */ false));
+    for (Map.Entry<Artifact, Artifact> entry :
+        ccCompilationOutputs.getLtoBitcodeFiles().entrySet()) {
+      ccCompilationOutputsBuilder.addLtoBitcodeFile(entry.getKey(), entry.getValue());
     }
+    CcCompilationOutputs ccCompilationOutputsWithOnlyObjects = ccCompilationOutputsBuilder.build();
+    CcLinkingHelper ccLinkingHelper =
+        new CcLinkingHelper(
+                ruleContext,
+                cppSemantics,
+                featureConfiguration,
+                ccToolchain,
+                fdoProvider,
+                ruleContext.getConfiguration())
+            .addNonCodeLinkerInputs(ccCompilationContext.getTransitiveCompilationPrerequisites())
+            .addNonCodeLinkerInputs(common.getLinkerScripts());
 
-    builder.addLtoBitcodeFiles(compilationOutputs.getLtoBitcodeFiles());
-    builder.addNonCodeInputs(common.getLinkerScripts());
+    CcLinkParams.Builder ccLinkParamsBuilder = CcLinkParams.builder();
+    ccLinkParamsBuilder.addTransitiveArgs(linkParams);
+
+    if (linkCompileOutputSeparately) {
+      for (LibraryToLink library : ccLinkingOutputs.getDynamicLibrariesForLinking()) {
+        ccLinkParamsBuilder.addLibrary(library);
+      }
+      ccCompilationOutputsWithOnlyObjects = new CcCompilationOutputs.Builder().build();
+    }
 
     // Determine the libraries to link in.
     // First libraries from srcs. Shared library artifacts here are substituted with mangled symlink
@@ -598,24 +554,62 @@
     // entries during linking process.
     for (Artifact library : precompiledFiles.getLibraries()) {
       if (Link.SHARED_LIBRARY_FILETYPES.matches(library.getFilename())) {
-        builder.addLibrary(LinkerInputs.solibLibraryToLink(
-            common.getDynamicLibrarySymlink(library, true), library,
-            CcLinkingOutputs.libraryIdentifierOf(library)));
+        ccLinkParamsBuilder.addLibrary(
+            LinkerInputs.solibLibraryToLink(
+                common.getDynamicLibrarySymlink(library, true),
+                library,
+                CcLinkingOutputs.libraryIdentifierOf(library)));
       } else if (Link.LINK_LIBRARY_FILETYPES.matches(library.getFilename())) {
-        builder.addLibrary(LinkerInputs.precompiledLibraryToLink(
-            library, ArtifactCategory.ALWAYSLINK_STATIC_LIBRARY));
+        ccLinkParamsBuilder.addLibrary(
+            LinkerInputs.precompiledLibraryToLink(
+                library, ArtifactCategory.ALWAYSLINK_STATIC_LIBRARY));
       } else if (Link.ARCHIVE_FILETYPES.matches(library.getFilename())) {
-        builder.addLibrary(LinkerInputs.precompiledLibraryToLink(
-            library, ArtifactCategory.STATIC_LIBRARY));
+        ccLinkParamsBuilder.addLibrary(
+            LinkerInputs.precompiledLibraryToLink(library, ArtifactCategory.STATIC_LIBRARY));
       } else {
         throw new IllegalStateException();
       }
     }
 
-    // Then the link params from the closure of deps.
-    builder.addLinkParams(linkParams, context);
+    CcLinkParams ccLinkParams = ccLinkParamsBuilder.build();
+    CcLinkingInfo.Builder ccLinkingInfo = CcLinkingInfo.Builder.create();
+    ccLinkingInfo.setStaticModeParamsForDynamicLibrary(ccLinkParams);
+    ccLinkingInfo.setStaticModeParamsForExecutable(ccLinkParams);
+    ccLinkingInfo.setDynamicModeParamsForDynamicLibrary(ccLinkParams);
+    ccLinkingInfo.setDynamicModeParamsForExecutable(ccLinkParams);
 
-    return builder;
+    ccLinkingHelper
+        .addCcLinkingInfos(ImmutableList.of(ccLinkingInfo.build()))
+        .setUseTestOnlyFlags(ruleContext.isTestTarget())
+        .setShouldCreateStaticLibraries(false)
+        .setLinkingMode(linkingMode)
+        .setDynamicLinkType(linkType)
+        .setDynamicLibrary(binary)
+        .setNeverLink(true)
+        .emitInterfaceSharedObjects(
+            isLinkShared(ruleContext)
+                && featureConfiguration.isEnabled(CppRuleClasses.TARGETS_WINDOWS)
+                && CppHelper.useInterfaceSharedObjects(cppConfiguration, ccToolchain))
+        .setPdbFile(pdbFile)
+        .setFake(fake);
+
+    if (customDefFile != null) {
+      ccLinkingHelper.setDefFile(customDefFile);
+    } else if (CppHelper.shouldUseGeneratedDefFile(ruleContext, featureConfiguration)) {
+      ccLinkingHelper.setDefFile(generatedDefFile);
+    }
+
+    if (linkingMode != Link.LinkingMode.DYNAMIC
+        && !cppConfiguration.disableEmittingStaticLibgcc()) {
+      // Only force a static link of libgcc if static runtime linking is enabled (which
+      // can't be true if runtimeInputs is empty).
+      // TODO(bazel-team): Move this to CcToolchain.
+      if (!ccToolchain.getStaticRuntimeLinkInputs(featureConfiguration).isEmpty()) {
+        ccLinkingHelper.addLinkopts(ImmutableList.of("-static-libgcc"));
+      }
+    }
+
+    return ccLinkingHelper.link(/* ccOutputs */ ccCompilationOutputsWithOnlyObjects);
   }
 
   /**
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationOutputs.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationOutputs.java
index 14d68fe..14d1873 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationOutputs.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationOutputs.java
@@ -189,7 +189,8 @@
 
     public Builder addObjectFiles(Iterable<Artifact> artifacts) {
       for (Artifact artifact : artifacts) {
-        Preconditions.checkArgument(Link.OBJECT_FILETYPES.matches(artifact.getFilename()));
+        Preconditions.checkArgument(
+            artifact.isTreeArtifact() || Link.OBJECT_FILETYPES.matches(artifact.getFilename()));
       }
       Iterables.addAll(objectFiles, artifacts);
       return this;
@@ -208,7 +209,8 @@
 
     public Builder addPicObjectFiles(Iterable<Artifact> artifacts) {
       for (Artifact artifact : artifacts) {
-        Preconditions.checkArgument(Link.OBJECT_FILETYPES.matches(artifact.getFilename()));
+        Preconditions.checkArgument(
+            artifact.isTreeArtifact() || Link.OBJECT_FILETYPES.matches(artifact.getFilename()));
       }
 
       Iterables.addAll(picObjectFiles, artifacts);
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLibrary.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLibrary.java
index ef3cb53..28d44cb 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLibrary.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLibrary.java
@@ -14,6 +14,7 @@
 
 package com.google.devtools.build.lib.rules.cpp;
 
+import static com.google.devtools.build.lib.packages.BuildType.LABEL;
 import static java.util.stream.Collectors.joining;
 
 import com.google.common.collect.ImmutableList;
@@ -35,6 +36,7 @@
 import com.google.devtools.build.lib.analysis.Runfiles;
 import com.google.devtools.build.lib.analysis.RunfilesProvider;
 import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
+import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException;
 import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget.Mode;
 import com.google.devtools.build.lib.analysis.test.InstrumentedFilesProvider;
 import com.google.devtools.build.lib.cmdline.Label;
@@ -153,7 +155,7 @@
                 ruleContext.getConfiguration())
             .fromCommon(common)
             .addLinkopts(common.getLinkopts())
-            .enableInterfaceSharedObjects()
+            .emitInterfaceSharedObjects(true)
             .setAlwayslink(alwaysLink)
             .setNeverLink(neverLink)
             .addLinkstamps(ruleContext.getPrerequisites("linkstamp", Mode.TARGET));
@@ -258,6 +260,37 @@
     CcLinkingOutputs ccLinkingOutputs = CcLinkingOutputs.EMPTY;
     if (ruleContext.getRule().getImplicitOutputsFunction() != ImplicitOutputsFunction.NONE
         || !ccCompilationOutputs.isEmpty()) {
+      if (featureConfiguration.isEnabled(CppRuleClasses.TARGETS_WINDOWS)) {
+        // If windows_export_all_symbols feature is enabled, bazel parses object files to generate
+        // DEF file and use it to export symbols. The generated DEF file won't be used if a custom
+        // DEF file is specified by win_def_file attribute.
+        if (CppHelper.shouldUseGeneratedDefFile(ruleContext, featureConfiguration)) {
+          try {
+            Artifact generatedDefFile =
+                CppHelper.createDefFileActions(
+                    ruleContext,
+                    ruleContext.getPrerequisiteArtifact("$def_parser", Mode.HOST),
+                    ccCompilationOutputs.getObjectFiles(false),
+                    ccToolchain
+                        .getFeatures()
+                        .getArtifactNameForCategory(
+                            ArtifactCategory.DYNAMIC_LIBRARY, ruleContext.getLabel().getName()));
+            linkingHelper.setDefFile(generatedDefFile);
+          } catch (InvalidConfigurationException e) {
+            ruleContext.throwWithRuleError(e.getMessage());
+            throw new IllegalStateException("Should not be reached");
+          }
+        }
+
+        // If user specifies a custom DEF file, then we use this one instead of the generated one.
+        Artifact customDefFile = null;
+        if (ruleContext.isAttrDefined("win_def_file", LABEL)) {
+          customDefFile = ruleContext.getPrerequisiteArtifact("win_def_file", Mode.TARGET);
+          if (customDefFile != null) {
+            linkingHelper.setDefFile(customDefFile);
+          }
+        }
+      }
       ccLinkingOutputs = linkingHelper.link(ccCompilationOutputs);
     }
 
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLinkingHelper.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLinkingHelper.java
index 1565021..0b4ee03 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLinkingHelper.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLinkingHelper.java
@@ -14,7 +14,6 @@
 
 package com.google.devtools.build.lib.rules.cpp;
 
-import static com.google.devtools.build.lib.packages.BuildType.LABEL;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
@@ -36,6 +35,7 @@
 import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.ExpansionException;
 import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.FeatureConfiguration;
 import com.google.devtools.build.lib.rules.cpp.CcToolchainVariables.VariablesExtension;
+import com.google.devtools.build.lib.rules.cpp.CppLinkAction.Context;
 import com.google.devtools.build.lib.rules.cpp.Link.LinkTargetType;
 import com.google.devtools.build.lib.rules.cpp.Link.LinkerOrArchiver;
 import com.google.devtools.build.lib.rules.cpp.Link.LinkingMode;
@@ -101,7 +101,8 @@
   private final List<Artifact> linkActionInputs = new ArrayList<>();
 
   @Nullable private Artifact dynamicLibrary;
-  private LinkTargetType linkType = LinkTargetType.STATIC_LIBRARY;
+  private LinkTargetType staticLinkType = LinkTargetType.STATIC_LIBRARY;
+  private LinkTargetType dynamicLinkType = LinkTargetType.NODEPS_DYNAMIC_LIBRARY;
   private boolean neverlink;
 
   private boolean checkDepsGenerateCpp = true;
@@ -110,6 +111,11 @@
   private boolean shouldCreateStaticLibraries = true;
   private boolean willOnlyBeLinkedIntoDynamicLibraries;
   private final List<VariablesExtension> variablesExtensions = new ArrayList<>();
+  private boolean useTestOnlyFlags;
+  private Artifact pdbFile;
+  private Artifact defFile;
+  private LinkingMode linkingMode = LinkingMode.DYNAMIC;
+  private boolean fake;
 
   private final FeatureConfiguration featureConfiguration;
   private final CcToolchainProvider ccToolchain;
@@ -151,7 +157,7 @@
   }
 
   /** Adds the corresponding non-code files as linker inputs. */
-  public void addNonCodeLinkerInputs(Iterable<Artifact> nonCodeLinkerInputs) {
+  public CcLinkingHelper addNonCodeLinkerInputs(Iterable<Artifact> nonCodeLinkerInputs) {
     for (Artifact nonCodeLinkerInput : nonCodeLinkerInputs) {
       String basename = nonCodeLinkerInput.getFilename();
       Preconditions.checkArgument(!Link.OBJECT_FILETYPES.matches(basename));
@@ -159,6 +165,7 @@
       Preconditions.checkArgument(!Link.SHARED_LIBRARY_FILETYPES.matches(basename));
       this.nonCodeLinkerInputs.add(nonCodeLinkerInput);
     }
+    return this;
   }
 
   /** Adds the given options as linker options to the link command. */
@@ -231,7 +238,7 @@
    * downstream code).
    */
   public CcLinkingHelper setAlwayslink(boolean alwayslink) {
-    linkType =
+    staticLinkType =
         alwayslink ? LinkTargetType.ALWAYS_LINK_STATIC_LIBRARY : LinkTargetType.STATIC_LIBRARY;
     return this;
   }
@@ -245,7 +252,7 @@
   public CcLinkingHelper setStaticLinkType(LinkTargetType linkType) {
     Preconditions.checkNotNull(linkType);
     Preconditions.checkState(linkType.linkerOrArchiver() == LinkerOrArchiver.ARCHIVER);
-    this.linkType = linkType;
+    this.staticLinkType = linkType;
     return this;
   }
 
@@ -285,8 +292,8 @@
    * linker generates a dynamic library, and only if the crosstool supports it. The default is not
    * to generate interface dynamic libraries.
    */
-  public CcLinkingHelper enableInterfaceSharedObjects() {
-    this.emitInterfaceSharedObjects = true;
+  public CcLinkingHelper emitInterfaceSharedObjects(boolean emitInterfaceSharedObjects) {
+    this.emitInterfaceSharedObjects = emitInterfaceSharedObjects;
     return this;
   }
 
@@ -338,7 +345,7 @@
     //
     // An additional pre-existing issue is that the header check tokens are dropped if we don't
     // generate any link actions, effectively disabling header checking in some cases.
-    if (linkType.linkerOrArchiver() == LinkerOrArchiver.ARCHIVER) {
+    if (staticLinkType.linkerOrArchiver() == LinkerOrArchiver.ARCHIVER) {
       // TODO(bazel-team): This can't create the link action for a cc_binary yet.
       ccLinkingOutputs = createCcLinkActions(ccOutputs);
     }
@@ -396,7 +403,6 @@
     return ccLinkingInfoBuilder.build();
   }
 
-
   /**
    * Constructs the C++ linker actions. It generally generates two actions, one for a static library
    * and one for a dynamic library. If PIC is required for shared libraries, but not for binaries,
@@ -405,17 +411,19 @@
    *
    * <p>For dynamic libraries, this method can additionally create an interface shared library that
    * can be used for linking, but doesn't contain any executable code. This increases the number of
-   * cache hits for link actions. Call {@link #enableInterfaceSharedObjects()} to enable this
+   * cache hits for link actions. Call {@link #emitInterfaceSharedObjects(boolean)} to enable this
    * behavior.
    *
    * @throws RuleErrorException
    */
   private CcLinkingOutputs createCcLinkActions(CcCompilationOutputs ccOutputs)
       throws RuleErrorException, InterruptedException {
-    // For now only handle static links. Note that the dynamic library link below ignores linkType.
+    // For now only handle static links. Note that the dynamic library link below ignores
+    // staticLinkType.
     // TODO(bazel-team): Either support non-static links or move this check to setStaticLinkType().
     Preconditions.checkState(
-        linkType.linkerOrArchiver() == LinkerOrArchiver.ARCHIVER, "can only handle static links");
+        staticLinkType.linkerOrArchiver() == LinkerOrArchiver.ARCHIVER,
+        "can only handle static links");
 
     CcLinkingOutputs.Builder result = new CcLinkingOutputs.Builder();
     AnalysisEnvironment env = ruleContext.getAnalysisEnvironment();
@@ -442,7 +450,10 @@
     }
 
     if (shouldCreateDynamicLibrary) {
-      createDynamicLibrary(result, env, usePicForDynamicLibs, libraryIdentifier, ccOutputs);
+      boolean usePic =
+          (!dynamicLinkType.isExecutable() && usePicForDynamicLibs)
+              || (dynamicLinkType.isExecutable() && usePicForBinaries);
+      createDynamicLibrary(result, env, usePic, libraryIdentifier, ccOutputs);
     }
 
     return result.build();
@@ -454,6 +465,36 @@
     return this;
   }
 
+  public CcLinkingHelper setUseTestOnlyFlags(boolean useTestOnlyFlags) {
+    this.useTestOnlyFlags = useTestOnlyFlags;
+    return this;
+  }
+
+  public CcLinkingHelper setLinkingMode(LinkingMode linkingMode) {
+    this.linkingMode = linkingMode;
+    return this;
+  }
+
+  public CcLinkingHelper setDynamicLinkType(LinkTargetType dynamicLinkType) {
+    this.dynamicLinkType = dynamicLinkType;
+    return this;
+  }
+
+  public CcLinkingHelper setFake(boolean fake) {
+    this.fake = fake;
+    return this;
+  }
+
+  public CcLinkingHelper setPdbFile(Artifact pdbFile) {
+    this.pdbFile = pdbFile;
+    return this;
+  }
+
+  public CcLinkingHelper setDefFile(Artifact defFile) {
+    this.defFile = defFile;
+    return this;
+  }
+
   private Pair<LibraryToLink, LibraryToLink> createNoPicAndPicStaticLibraries(
       AnalysisEnvironment env,
       boolean usePicForBinaries,
@@ -463,8 +504,9 @@
       throws RuleErrorException, InterruptedException {
     LibraryToLink staticLibrary = null;
     LibraryToLink picStaticLibrary = null;
-    // Create static library (.a). The linkType only reflects whether the library is alwayslink or
-    // not. The PIC-ness is determined by whether we need to use PIC or not. There are four cases
+    // Create static library (.a). The staticLinkType only reflects whether the library is
+    // alwayslink or not. The PIC-ness is determined by whether we need to use PIC or not. There
+    // are four cases:
     // for (usePicForDynamicLibs usePicForBinaries):
     //
     // (1) (false false) -> no pic code is when toolchain and cppOptions don't need pic code for
@@ -495,7 +537,7 @@
     if (createNoPicAction) {
       staticLibrary =
           registerActionForStaticLibrary(
-                  linkType, ccOutputs, /* usePic= */ false, libraryIdentifier, env)
+                  staticLinkType, ccOutputs, /* usePic= */ false, libraryIdentifier, env)
               .getOutputLibrary();
     }
 
@@ -503,10 +545,10 @@
       LinkTargetType linkTargetTypeUsedForNaming;
       if (!createNoPicAction) {
         // Only PIC library created, name does not match content.
-        linkTargetTypeUsedForNaming = linkType;
+        linkTargetTypeUsedForNaming = staticLinkType;
       } else {
         linkTargetTypeUsedForNaming =
-            (linkType == LinkTargetType.ALWAYS_LINK_STATIC_LIBRARY)
+            (staticLinkType == LinkTargetType.ALWAYS_LINK_STATIC_LIBRARY)
                 ? LinkTargetType.ALWAYS_LINK_PIC_STATIC_LIBRARY
                 : LinkTargetType.PIC_STATIC_LIBRARY;
       }
@@ -549,7 +591,7 @@
   private void createDynamicLibrary(
       CcLinkingOutputs.Builder result,
       AnalysisEnvironment env,
-      boolean usePicForDynamicLibs,
+      boolean usePic,
       String libraryIdentifier,
       CcCompilationOutputs ccOutputs)
       throws RuleErrorException, InterruptedException {
@@ -595,57 +637,79 @@
     CppLinkActionBuilder dynamicLinkActionBuilder =
         newLinkActionBuilder(soImpl)
             .setInterfaceOutput(soInterface)
-            .addObjectFiles(ccOutputs.getObjectFiles(usePicForDynamicLibs))
             .addNonCodeInputs(ccOutputs.getHeaderTokenFiles())
             .addLtoBitcodeFiles(ccOutputs.getLtoBitcodeFiles())
-            .setLinkType(LinkTargetType.NODEPS_DYNAMIC_LIBRARY)
-            .setLinkingMode(LinkingMode.DYNAMIC)
+            .setLinkType(dynamicLinkType)
+            .setLinkingMode(linkingMode)
+            .setFake(fake)
             .addActionInputs(linkActionInputs)
-            .setLibraryIdentifier(mainLibraryIdentifier)
             .addLinkopts(linkopts)
             .addLinkopts(sonameLinkopts)
-            .setRuntimeInputs(
-                ArtifactCategory.DYNAMIC_LIBRARY,
-                ccToolchain.getDynamicRuntimeLinkMiddleman(featureConfiguration),
-                ccToolchain.getDynamicRuntimeLinkInputs(featureConfiguration))
+            .addNonCodeInputs(nonCodeLinkerInputs)
             .addVariablesExtensions(variablesExtensions);
 
-    if (featureConfiguration.isEnabled(CppRuleClasses.TARGETS_WINDOWS)) {
-      // On Windows, we cannot build a shared library with symbols unresolved, so here we
-      // dynamically
-      // link to all it's dependencies.
-      CcLinkParams.Builder ccLinkParamsBuilder =
-          CcLinkParams.builder(/* linkingStatically= */ false, /* linkShared= */ true);
-      ccLinkParamsBuilder.addCcLibrary(ruleContext);
-      dynamicLinkActionBuilder.addLinkParams(ccLinkParamsBuilder.build(), ruleContext);
+    if (fake) {
+      dynamicLinkActionBuilder.addFakeObjectFiles(ccOutputs.getObjectFiles(usePic));
+    } else {
+      dynamicLinkActionBuilder.addObjectFiles(ccOutputs.getObjectFiles(usePic));
+    }
 
-      // If windows_export_all_symbols feature is enabled, bazel parses object files to generate
-      // DEF file and use it to export symbols. The generated DEF file won't be used if a custom
-      // DEF file is specified by win_def_file attribute.
-      if (CppHelper.shouldUseGeneratedDefFile(ruleContext, featureConfiguration)) {
-        Artifact generatedDefFile =
-            CppHelper.createDefFileActions(
-                ruleContext,
-                ruleContext.getPrerequisiteArtifact("$def_parser", Mode.HOST),
-                ccOutputs.getObjectFiles(false),
-                SolibSymlinkAction.getDynamicLibrarySoname(soImpl.getRootRelativePath(), true));
-        dynamicLinkActionBuilder.setDefFile(generatedDefFile);
-      }
+    if (!dynamicLinkType.isExecutable()) {
+      dynamicLinkActionBuilder.setLibraryIdentifier(mainLibraryIdentifier);
+    }
 
-      // If user specifies a custom DEF file, then we use this one instead of the generated one.
-      Artifact customDefFile = null;
-      if (ruleContext.isAttrDefined("win_def_file", LABEL)) {
-        customDefFile = ruleContext.getPrerequisiteArtifact("win_def_file", Mode.TARGET);
-      }
-      if (customDefFile != null) {
-        dynamicLinkActionBuilder.setDefFile(customDefFile);
+    if (linkingMode == LinkingMode.DYNAMIC) {
+      dynamicLinkActionBuilder.setRuntimeInputs(
+          ArtifactCategory.DYNAMIC_LIBRARY,
+          ccToolchain.getDynamicRuntimeLinkMiddleman(featureConfiguration),
+          ccToolchain.getDynamicRuntimeLinkInputs(featureConfiguration));
+    } else {
+      dynamicLinkActionBuilder.setRuntimeInputs(
+          ArtifactCategory.STATIC_LIBRARY,
+          ccToolchain.getStaticRuntimeLinkMiddleman(featureConfiguration),
+          ccToolchain.getStaticRuntimeLinkInputs(featureConfiguration));
+    }
+
+    if (CppLinkAction.enableSymbolsCounts(
+        cppConfiguration, ccToolchain.supportsGoldLinker(), fake, staticLinkType)) {
+      dynamicLinkActionBuilder.setSymbolCountsOutput(
+          ruleContext.getBinArtifact(
+              CppLinkAction.symbolCountsFileName(
+                  PathFragment.create(ruleContext.getTarget().getName()))));
+    }
+
+    if (featureConfiguration.isEnabled(CppRuleClasses.TARGETS_WINDOWS)
+        || dynamicLinkType != LinkTargetType.NODEPS_DYNAMIC_LIBRARY) {
+      if (dynamicLinkType != LinkTargetType.NODEPS_DYNAMIC_LIBRARY) {
+        for (CcLinkingInfo ccLinkingInfo : ccLinkingInfos) {
+          dynamicLinkActionBuilder.addLinkParams(
+              ccLinkingInfo.getCcLinkParams(
+                  linkingMode == LinkingMode.STATIC, dynamicLinkType.isDynamicLibrary()),
+              ruleContext);
+        }
+      } else {
+        // On Windows, we cannot build a shared library with symbols unresolved, so here we
+        // dynamically
+        // link to all it's dependencies.
+        CcLinkParams.Builder ccLinkParamsBuilder =
+            CcLinkParams.builder(/* linkingStatically= */ false, /* linkShared= */ true);
+        ccLinkParamsBuilder.addCcLibrary(ruleContext);
+        dynamicLinkActionBuilder.addLinkParams(ccLinkParamsBuilder.build(), ruleContext);
       }
     }
 
-    if (!ccOutputs.getLtoBitcodeFiles().isEmpty()
+    if (pdbFile != null) {
+      dynamicLinkActionBuilder.addActionOutput(pdbFile);
+    }
+
+    if (defFile != null) {
+      dynamicLinkActionBuilder.setDefFile(defFile);
+    }
+
+    if (dynamicLinkActionBuilder.hasLtoBitcodeInputs()
         && featureConfiguration.isEnabled(CppRuleClasses.THIN_LTO)) {
       dynamicLinkActionBuilder.setLtoIndexing(true);
-      dynamicLinkActionBuilder.setUsePicForLtoBackendActions(usePicForDynamicLibs);
+      dynamicLinkActionBuilder.setUsePicForLtoBackendActions(usePic);
       CppLinkAction indexAction = dynamicLinkActionBuilder.build();
       if (indexAction != null) {
         env.registerAction(indexAction);
@@ -654,7 +718,12 @@
       dynamicLinkActionBuilder.setLtoIndexing(false);
     }
 
+    if (dynamicLinkActionBuilder.getAllLtoBackendArtifacts() != null) {
+      result.addAllLtoArtifacts(dynamicLinkActionBuilder.getAllLtoBackendArtifacts());
+    }
     CppLinkAction dynamicLinkAction = dynamicLinkActionBuilder.build();
+    result.setCppLinkActionContext(new Context(dynamicLinkActionBuilder));
+    result.addLinkActionInputs(dynamicLinkAction.getInputs());
     env.registerAction(dynamicLinkAction);
 
     LibraryToLink dynamicLibrary = dynamicLinkAction.getOutputLibrary();
@@ -666,49 +735,52 @@
     //
     // When COPY_DYNAMIC_LIBRARIES_TO_BINARY is enabled, we don't need to create the special
     // solibDir, instead we use the original interface library and dynamic library.
-    if (neverlink
-        || featureConfiguration.isEnabled(CppRuleClasses.COPY_DYNAMIC_LIBRARIES_TO_BINARY)) {
-      result.addDynamicLibraryForLinking(
-          interfaceLibrary == null ? dynamicLibrary : interfaceLibrary);
-      result.addDynamicLibraryForRuntime(dynamicLibrary);
-    } else {
-      Artifact implLibraryLinkArtifact =
-          SolibSymlinkAction.getDynamicLibrarySymlink(
-              ruleContext,
-              ccToolchain.getSolibDirectory(),
-              dynamicLibrary.getArtifact(),
-              /* preserveName= */ false,
-              /* prefixConsumer= */ false,
-              ruleContext.getConfiguration());
-      LibraryToLink implLibraryLink =
-          LinkerInputs.solibLibraryToLink(
-              implLibraryLinkArtifact, dynamicLibrary.getArtifact(), libraryIdentifier);
-      result.addDynamicLibraryForRuntime(implLibraryLink);
-
-      LibraryToLink libraryLink;
-      if (interfaceLibrary == null) {
-        libraryLink = implLibraryLink;
+    if (dynamicLibrary != null) {
+      if (neverlink
+          || featureConfiguration.isEnabled(CppRuleClasses.COPY_DYNAMIC_LIBRARIES_TO_BINARY)) {
+        result.addDynamicLibraryForLinking(
+            interfaceLibrary == null ? dynamicLibrary : interfaceLibrary);
+        result.addDynamicLibraryForRuntime(dynamicLibrary);
       } else {
-        Artifact libraryLinkArtifact =
+        Artifact implLibraryLinkArtifact =
             SolibSymlinkAction.getDynamicLibrarySymlink(
                 ruleContext,
                 ccToolchain.getSolibDirectory(),
-                interfaceLibrary.getArtifact(),
+                dynamicLibrary.getArtifact(),
                 /* preserveName= */ false,
                 /* prefixConsumer= */ false,
                 ruleContext.getConfiguration());
-        libraryLink =
+        LibraryToLink implLibraryLink =
             LinkerInputs.solibLibraryToLink(
-                libraryLinkArtifact, interfaceLibrary.getArtifact(), libraryIdentifier);
+                implLibraryLinkArtifact, dynamicLibrary.getArtifact(), libraryIdentifier);
+        result.addDynamicLibraryForRuntime(implLibraryLink);
+
+        LibraryToLink libraryLink;
+        if (interfaceLibrary == null) {
+          libraryLink = implLibraryLink;
+        } else {
+          Artifact libraryLinkArtifact =
+              SolibSymlinkAction.getDynamicLibrarySymlink(
+                  ruleContext,
+                  ccToolchain.getSolibDirectory(),
+                  interfaceLibrary.getArtifact(),
+                  /* preserveName= */ false,
+                  /* prefixConsumer= */ false,
+                  ruleContext.getConfiguration());
+          libraryLink =
+              LinkerInputs.solibLibraryToLink(
+                  libraryLinkArtifact, interfaceLibrary.getArtifact(), libraryIdentifier);
+        }
+        result.addDynamicLibraryForLinking(libraryLink);
       }
-      result.addDynamicLibraryForLinking(libraryLink);
     }
   }
 
   private CppLinkActionBuilder newLinkActionBuilder(Artifact outputArtifact) {
     return new CppLinkActionBuilder(
             ruleContext, outputArtifact, ccToolchain, fdoProvider, featureConfiguration, semantics)
-        .setCrosstoolInputs(ccToolchain.getLink());
+        .setCrosstoolInputs(ccToolchain.getLink())
+        .setUseTestOnlyFlags(useTestOnlyFlags);
   }
 
   /**
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLinkingOutputs.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLinkingOutputs.java
index ca42099..bcf795f 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLinkingOutputs.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLinkingOutputs.java
@@ -56,15 +56,25 @@
    */
   private final ImmutableList<LibraryToLink> dynamicLibrariesForRuntime;
 
+  private final ImmutableList<LtoBackendArtifacts> allLtoArtifacts;
+  private final ImmutableList<Artifact> linkActionInputs;
+  private final CppLinkAction.Context cppLinkActionContext;
+
   private CcLinkingOutputs(
       ImmutableList<LibraryToLink> staticLibraries,
       ImmutableList<LibraryToLink> picStaticLibraries,
       ImmutableList<LibraryToLink> dynamicLibrariesForLinking,
-      ImmutableList<LibraryToLink> dynamicLibrariesForRuntime) {
+      ImmutableList<LibraryToLink> dynamicLibrariesForRuntime,
+      ImmutableList<LtoBackendArtifacts> allLtoArtifacts,
+      ImmutableList<Artifact> linkActionInputs,
+      CppLinkAction.Context cppLinkActionContext) {
     this.staticLibraries = staticLibraries;
     this.picStaticLibraries = picStaticLibraries;
     this.dynamicLibrariesForLinking = dynamicLibrariesForLinking;
     this.dynamicLibrariesForRuntime = dynamicLibrariesForRuntime;
+    this.allLtoArtifacts = allLtoArtifacts;
+    this.linkActionInputs = linkActionInputs;
+    this.cppLinkActionContext = cppLinkActionContext;
   }
 
   @Override
@@ -98,6 +108,18 @@
     return dynamicLibrariesForRuntime;
   }
 
+  public ImmutableList<LtoBackendArtifacts> getAllLtoArtifacts() {
+    return allLtoArtifacts;
+  }
+
+  public ImmutableList<Artifact> getLinkActionInputs() {
+    return linkActionInputs;
+  }
+
+  public CppLinkAction.Context getCppLinkActionContext() {
+    return cppLinkActionContext;
+  }
+
   public boolean isEmpty() {
     return staticLibraries.isEmpty()
         && picStaticLibraries.isEmpty()
@@ -250,13 +272,23 @@
     private final Set<LibraryToLink> picStaticLibraries = new LinkedHashSet<>();
     private final Set<LibraryToLink> dynamicLibrariesForLinking = new LinkedHashSet<>();
     private final Set<LibraryToLink> dynamicLibrariesForRuntime = new LinkedHashSet<>();
+    // TODO(plf): Return a list of debug artifacts instead of lto back end artifacts and in that
+    // same list return the .pdb file for Windows.
+    private final ImmutableList.Builder<LtoBackendArtifacts> allLtoArtifacts =
+        ImmutableList.builder();
+    private final ImmutableList.Builder<Artifact> linkActionInputs = ImmutableList.builder();
+    // TODO(plf): Try to remove this by refactoring native deps and the way they build the launcher.
+    private CppLinkAction.Context cppLinkActionContext;
 
     public CcLinkingOutputs build() {
       return new CcLinkingOutputs(
           ImmutableList.copyOf(staticLibraries),
           ImmutableList.copyOf(picStaticLibraries),
           ImmutableList.copyOf(dynamicLibrariesForLinking),
-          ImmutableList.copyOf(dynamicLibrariesForRuntime));
+          ImmutableList.copyOf(dynamicLibrariesForRuntime),
+          allLtoArtifacts.build(),
+          linkActionInputs.build(),
+          cppLinkActionContext);
     }
 
     public Builder merge(CcLinkingOutputs outputs) {
@@ -264,6 +296,8 @@
       picStaticLibraries.addAll(outputs.getPicStaticLibraries());
       dynamicLibrariesForLinking.addAll(outputs.getDynamicLibrariesForLinking());
       dynamicLibrariesForRuntime.addAll(outputs.getDynamicLibrariesForRuntime());
+      allLtoArtifacts.addAll(outputs.getAllLtoArtifacts());
+      linkActionInputs.addAll(outputs.getLinkActionInputs());
       return this;
     }
 
@@ -306,5 +340,21 @@
       Iterables.addAll(dynamicLibrariesForRuntime, libraries);
       return this;
     }
+
+    public Builder addAllLtoArtifacts(Iterable<LtoBackendArtifacts> allLtoArtifacts) {
+      this.allLtoArtifacts.addAll(allLtoArtifacts);
+      return this;
+    }
+
+    public Builder addLinkActionInputs(Iterable<Artifact> linkActionInputs) {
+      this.linkActionInputs.addAll(linkActionInputs);
+      return this;
+    }
+
+    public Builder setCppLinkActionContext(CppLinkAction.Context cppLinkActionContext) {
+      Preconditions.checkState(this.cppLinkActionContext == null);
+      this.cppLinkActionContext = cppLinkActionContext;
+      return this;
+    }
   }
 }
diff --git a/src/test/py/bazel/bazel_windows_cpp_test.py b/src/test/py/bazel/bazel_windows_cpp_test.py
index b4dc5a4..108bd3b 100644
--- a/src/test/py/bazel/bazel_windows_cpp_test.py
+++ b/src/test/py/bazel/bazel_windows_cpp_test.py
@@ -474,6 +474,7 @@
         'build', '//:lib', '-s', '--output_groups=dynamic_library',
         '--features=windows_export_all_symbols'
     ])
+    self.AssertExitCode(exit_code, 0, stderr)
 
     bazel_bin = self.getBazelInfo('bazel-bin')
     lib_if = os.path.join(bazel_bin, 'lib.if.lib')