Move interface so building to action configs

This cl moves the conditional building of interface libraries from LinkCommandLine to action configs and features. It provides link_dynamic_library.sh to keep blaze backwards compatible. The script and related code can be deleted once all crosstools are updated.

RELNOTES: No.

--
MOS_MIGRATED_REVID=135799041
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchain.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchain.java
index ec766a7..5ff24aa 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchain.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchain.java
@@ -56,6 +56,7 @@
  * Implementation for the cc_toolchain rule.
  */
 public class CcToolchain implements RuleConfiguredTargetFactory {
+
   /**
    * This file (found under the sysroot) may be unconditionally included in every C/C++ compilation.
    */
@@ -198,6 +199,8 @@
           "FDO_DIR", cppConfiguration.getFdoInstrument().getPathString()));
     }
 
+    Artifact linkDynamicLibraryTool = getLinkDynamicLibraryTool(ruleContext);
+
     CcToolchainProvider provider =
         new CcToolchainProvider(
             cppConfiguration,
@@ -220,6 +223,7 @@
             getBuildVariables(ruleContext),
             getBuiltinIncludes(ruleContext),
             coverageEnvironment.build(),
+            linkDynamicLibraryTool,
             getEnvironment(ruleContext));
     RuleConfiguredTargetBuilder builder =
         new RuleConfiguredTargetBuilder(ruleContext)
@@ -250,6 +254,10 @@
     return builder.build();
   }
 
+  private Artifact getLinkDynamicLibraryTool(RuleContext ruleContext) {
+    return ruleContext.getPrerequisiteArtifact("$link_dynamic_library_tool", Mode.TARGET);
+  }
+
   private ImmutableList<Artifact> getBuiltinIncludes(RuleContext ruleContext) {
     ImmutableList.Builder<Artifact> result = ImmutableList.builder();
     for (Artifact artifact : inputsForLibc(ruleContext)) {
@@ -285,6 +293,7 @@
     return NestedSetBuilder.<Artifact>stableOrder()
         .addTransitive(link)
         .addTransitive(AnalysisUtils.getMiddlemanFor(ruleContext, ":libc_top"))
+        .add(getLinkDynamicLibraryTool(ruleContext))
         .add(
             ruleContext
                 .getAnalysisEnvironment()
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainFeatures.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainFeatures.java
index 799be8b..a5e3715 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainFeatures.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainFeatures.java
@@ -1149,9 +1149,12 @@
       return enabledFeatureNames.contains(feature);
     }
 
-    /**
-     * @return whether an action config for the blaze action with the given name is enabled.
-     */
+    /** @return true if tool_path in action_config points to a real tool, not a dummy placeholder */
+    public boolean hasConfiguredLinkerPathInActionConfig() {
+      return isEnabled("has_configured_linker_path");
+    }
+
+    /** @return whether an action config for the blaze action with the given name is enabled. */
     boolean actionIsConfigured(String actionName) {
       return enabledActionConfigActionNames.contains(actionName);
     }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainProvider.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainProvider.java
index 755da53..4145ebd 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainProvider.java
@@ -25,9 +25,7 @@
 import com.google.devtools.build.lib.util.Pair;
 import com.google.devtools.build.lib.util.Preconditions;
 import com.google.devtools.build.lib.vfs.PathFragment;
-
 import java.util.Map;
-
 import javax.annotation.Nullable;
 
 /**
@@ -35,9 +33,7 @@
  */
 @Immutable
 public final class CcToolchainProvider implements TransitiveInfoProvider {
-  /**
-   * An empty toolchain to be returned in the error case (instead of null).
-   */
+  /** An empty toolchain to be returned in the error case (instead of null). */
   public static final CcToolchainProvider EMPTY_TOOLCHAIN_IS_ERROR =
       new CcToolchainProvider(
           null,
@@ -60,6 +56,7 @@
           ImmutableMap.<String, String>of(),
           ImmutableList.<Artifact>of(),
           NestedSetBuilder.<Pair<String, String>>emptySet(Order.COMPILE_ORDER),
+          null,
           ImmutableMap.<String, String>of());
 
   @Nullable private final CppConfiguration cppConfiguration;
@@ -82,6 +79,7 @@
   private final ImmutableMap<String, String> buildVariables;
   private final ImmutableList<Artifact> builtinIncludeFiles;
   private final NestedSet<Pair<String, String>> coverageEnvironment;
+  @Nullable private final Artifact linkDynamicLibraryTool;
   private final ImmutableMap<String, String> environment;
 
   public CcToolchainProvider(
@@ -105,6 +103,7 @@
       Map<String, String> buildVariables,
       ImmutableList<Artifact> builtinIncludeFiles,
       NestedSet<Pair<String, String>> coverageEnvironment,
+      Artifact linkDynamicLibraryTool,
       ImmutableMap<String, String> environment) {
     this.cppConfiguration = cppConfiguration;
     this.crosstool = Preconditions.checkNotNull(crosstool);
@@ -126,6 +125,7 @@
     this.buildVariables = ImmutableMap.copyOf(buildVariables);
     this.builtinIncludeFiles = builtinIncludeFiles;
     this.coverageEnvironment = coverageEnvironment;
+    this.linkDynamicLibraryTool = linkDynamicLibraryTool;
     this.environment = environment;
   }
 
@@ -283,4 +283,12 @@
   public ImmutableMap<String, String> getEnvironment() {
     return environment;
   }
+
+  /**
+   * Returns the tool which should be used for linking dynamic libraries, or in case it's not
+   * specified by the crosstool this will be @tools_repository/tools/cpp:link_dynamic_library
+   */
+  public Artifact getLinkDynamicLibraryTool() {
+    return linkDynamicLibraryTool;
+  }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainRule.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainRule.java
index 9ee4481..786add8 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainRule.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainRule.java
@@ -75,12 +75,16 @@
         .add(attr("module_map", LABEL).legacyAllowAnyFileType().cfg(HOST))
         .add(attr("supports_param_files", BOOLEAN).value(true))
         .add(attr("supports_header_parsing", BOOLEAN).value(false))
+        .add(
+            attr("$link_dynamic_library_tool", LABEL)
+                .value(env.getToolsLabel("//tools/cpp:link_dynamic_library")))
         // TODO(bazel-team): Should be using the TARGET configuration.
         .add(attr(":libc_top", LABEL).cfg(HOST).value(LIBC_TOP))
-        .add(attr(":lipo_context_collector", LABEL)
-            .cfg(LipoTransition.LIPO_COLLECTOR)
-            .value(CppRuleClasses.LIPO_CONTEXT_COLLECTOR)
-            .skipPrereqValidatorCheck())
+        .add(
+            attr(":lipo_context_collector", LABEL)
+                .cfg(LipoTransition.LIPO_COLLECTOR)
+                .value(CppRuleClasses.LIPO_CONTEXT_COLLECTOR)
+                .skipPrereqValidatorCheck())
         .build();
   }
 
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppConfiguration.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppConfiguration.java
index 95f37e7..7231f64 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppConfiguration.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppConfiguration.java
@@ -51,6 +51,7 @@
 import com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig.CToolchain.ArtifactNamePattern;
 import com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig.LinkingModeFlags;
 import com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig.LipoMode;
+import com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig.ToolPath;
 import com.google.devtools.common.options.OptionsParsingException;
 import com.google.protobuf.TextFormat;
 import com.google.protobuf.TextFormat.ParseException;
@@ -693,7 +694,6 @@
   // feature configuration, and all crosstools have been converted.
   private CToolchain addLegacyFeatures(CToolchain toolchain) {
     CToolchain.Builder toolchainBuilder = CToolchain.newBuilder();
-    toolchainBuilder.mergeFrom(toolchain);
 
     Set<ArtifactCategory> definedCategories = new HashSet<>();
     for (ArtifactNamePattern pattern : toolchainBuilder.getArtifactNamePatternList()) {
@@ -719,292 +719,302 @@
       featuresBuilder.add(feature.getName());
     }
     Set<String> features = featuresBuilder.build();
-    if (features.contains(CppRuleClasses.NO_LEGACY_FEATURES)) {
-      // The toolchain requested to not get any legacy features enabled.
-      return toolchainBuilder.build();
-    }
+    if (!features.contains(CppRuleClasses.NO_LEGACY_FEATURES)) {
+      try {
+        if (!linkActionsAreConfigured(toolchain)) {
+          String linkerToolPath = "DUMMY_LINKER_TOOL";
+          for (ToolPath tool : toolchain.getToolPathList()) {
+            if (tool.getName().equals(Tool.GCC.getNamePart())) {
+              linkerToolPath =
+                  crosstoolTopPathFragment
+                      .getRelative(new PathFragment(tool.getPath()))
+                      .getPathString();
+            }
+          }
+          if (getTargetLibc().equals("macosx")) {
+            TextFormat.merge(
+                CppLinkActionConfigs.getCppLinkActionConfigs(
+                    CppLinkPlatform.MAC, features, linkerToolPath),
+                toolchainBuilder);
+          } else {
+            TextFormat.merge(
+                CppLinkActionConfigs.getCppLinkActionConfigs(
+                    CppLinkPlatform.LINUX, features, linkerToolPath),
+                toolchainBuilder);
+          }
+        }
 
-    try {
-      
-      if (!linkActionsAreConfigured(toolchain)) {
-        if (getTargetLibc().equals("macosx")) {
+        if (!features.contains("dependency_file")) {
+          // Gcc options:
+          //  -MD turns on .d file output as a side-effect (doesn't imply -E)
+          //  -MM[D] enables user includes only, not system includes
+          //  -MF <name> specifies the dotd file name
+          // Issues:
+          //  -M[M] alone subverts actual .o output (implies -E)
+          //  -M[M]D alone breaks some of the .d naming assumptions
+          // This combination gets user and system includes with specified name:
+          //  -MD -MF <name>
           TextFormat.merge(
-              CppLinkActionConfigs.getCppLinkActionConfigs(CppLinkPlatform.MAC), toolchainBuilder);
-        } else {
-          TextFormat.merge(
-              CppLinkActionConfigs.getCppLinkActionConfigs(CppLinkPlatform.LINUX),
+              ""
+                  + "feature {"
+                  + "  name: 'dependency_file'"
+                  + "  flag_set {"
+                  + "    action: 'assemble'"
+                  + "    action: 'preprocess-assemble'"
+                  + "    action: 'c-compile'"
+                  + "    action: 'c++-compile'"
+                  + "    action: 'c++-module-compile'"
+                  + "    action: 'objc-compile'"
+                  + "    action: 'objc++-compile'"
+                  + "    action: 'c++-header-preprocessing'"
+                  + "    action: 'c++-header-parsing'"
+                  + "    expand_if_all_available: 'dependency_file'"
+                  + "    flag_group {"
+                  + "      flag: '-MD'"
+                  + "      flag: '-MF'"
+                  + "      flag: '%{dependency_file}'"
+                  + "    }"
+                  + "  }"
+                  + "}",
               toolchainBuilder);
         }
-      }
-      if (!features.contains("dependency_file")) {
-        // Gcc options:
-        //  -MD turns on .d file output as a side-effect (doesn't imply -E)
-        //  -MM[D] enables user includes only, not system includes
-        //  -MF <name> specifies the dotd file name
-        // Issues:
-        //  -M[M] alone subverts actual .o output (implies -E)
-        //  -M[M]D alone breaks some of the .d naming assumptions
-        // This combination gets user and system includes with specified name:
-        //  -MD -MF <name>
-        TextFormat.merge(""
-            + "feature {"
-            + "  name: 'dependency_file'"
-            + "  flag_set {"
-            + "    action: 'assemble'"
-            + "    action: 'preprocess-assemble'"
-            + "    action: 'c-compile'"
-            + "    action: 'c++-compile'"
-            + "    action: 'c++-module-compile'"
-            + "    action: 'objc-compile'"
-            + "    action: 'objc++-compile'"
-            + "    action: 'c++-header-preprocessing'"
-            + "    action: 'c++-header-parsing'"
-            + "    expand_if_all_available: 'dependency_file'"
-            + "    flag_group {"
-            + "      flag: '-MD'"
-            + "      flag: '-MF'"
-            + "      flag: '%{dependency_file}'"
-            + "    }"
-            + "  }"
-            + "}",
-            toolchainBuilder);
-      }
 
-      if (!features.contains("random_seed")) {
-        // GCC and Clang give randomized names to symbols which are defined in
-        // an anonymous namespace but have external linkage.  To make
-        // computation of these deterministic, we want to override the
-        // default seed for the random number generator.  It's safe to use
-        // any value which differs for all translation units; we use the
-        // path to the object file.
-        TextFormat.merge(""
-            + "feature {"
-            + "  name: 'random_seed'"
-            + "  flag_set {"
-            + "    action: 'c++-compile'"
-            + "    action: 'c++-module-compile'"
-            + "    flag_group {"
-            + "      flag: '-frandom-seed=%{output_file}'"
-            + "    }"
-            + "  }"
-            + "}",
-            toolchainBuilder);
-      }
-
-      if (!features.contains("pic")) {
-        TextFormat.merge(""
-            + "feature {"
-            + "  name: 'pic'"
-            + "  flag_set {"
-            + "    action: 'c-compile'"
-            + "    action: 'c++-compile'"
-            + "    action: 'c++-module-compile'"
-            + "    action: 'preprocess-assemble'"
-            + "    expand_if_all_available: 'pic'"
-            + "    flag_group {"
-            + "      flag: '-fPIC'"
-            + "    }"
-            + "  }"
-            + "}",
-            toolchainBuilder);
-      }
-
-      if (!features.contains("per_object_debug_info")) {
-        TextFormat.merge(""
-            + "feature {"
-            + "  name: 'per_object_debug_info'"
-            + "  flag_set {"
-            + "    action: 'c-compile'"
-            + "    action: 'c++-compile'"
-            + "    action: 'assemble'"
-            + "    action: 'preprocess-assemble'"
-            + "    expand_if_all_available: 'per_object_debug_info_file'"
-            + "    flag_group {"
-            + "      flag: '-gsplit-dwarf'"
-            + "    }"
-            + "  }"
-            + "}",
-            toolchainBuilder);
-      }
-
-      if (!features.contains("preprocessor_defines")) {
-        TextFormat.merge(
-            ""
-                + "feature {"
-                + "  name: 'preprocessor_defines'"
-                + "  flag_set {"
-                + "    action: 'preprocess-assemble'"
-                + "    action: 'c-compile'"
-                + "    action: 'c++-compile'"
-                + "    action: 'c++-header-parsing'"
-                + "    action: 'c++-header-preprocessing'"
-                + "    action: 'c++-module-compile'"
-                + "    action: 'clif-match'"
-                + "    flag_group {"
-                + "      flag: '-D%{preprocessor_defines}'"
-                + "    }"
-                + "  }"
-                + "}",
-            toolchainBuilder);
-      }
-      if (!features.contains("include_paths")) {
-        TextFormat.merge(
-            ""
-                + "feature {"
-                + "  name: 'include_paths'"
-                + "  flag_set {"
-                + "    action: 'preprocess-assemble'"
-                + "    action: 'c-compile'"
-                + "    action: 'c++-compile'"
-                + "    action: 'c++-header-parsing'"
-                + "    action: 'c++-header-preprocessing'"
-                + "    action: 'c++-module-compile'"
-                + "    action: 'clif-match'"
-                + "    action: 'objc-compile'"
-                + "    action: 'objc++-compile'"
-                + "    flag_group {"
-                + "      flag: '-iquote'"
-                + "      flag: '%{quote_include_paths}'"
-                + "    }"
-                + "    flag_group {"
-                + "      flag: '-I%{include_paths}'"
-                + "    }"
-                + "    flag_group {"
-                + "      flag: '-isystem'"
-                + "      flag: '%{system_include_paths}'"
-                + "    }"
-                + "  }"
-                + "}",
-            toolchainBuilder);
-      }
-      if (!features.contains("fdo_instrument")) {
-        TextFormat.merge(
-            ""
-                + "feature {"
-                + "  name: 'fdo_instrument'"
-                + "  provides: 'profile'"
-                + "  flag_set {"
-                + "    action: 'c-compile'"
-                + "    action: 'c++-compile'"
-                + "    action: 'c++-link-interface-dynamic-library'"
-                + "    action: 'c++-link-dynamic-library'"
-                + "    action: 'c++-link-executable'"
-                + "    flag_group {"
-                + "      flag: '-fprofile-generate=%{fdo_instrument_path}'"
-                + "      flag: '-fno-data-sections'"
-                + "    }"
-                + "  }"
-                + "}",
-            toolchainBuilder);
-      }
-      if (!features.contains("fdo_optimize")) {
-        TextFormat.merge(
-            ""
-                + "feature {"
-                + "  name: 'fdo_optimize'"
-                + "  provides: 'profile'"
-                + "  flag_set {"
-                + "    action: 'c-compile'"
-                + "    action: 'c++-compile'"
-                + "    expand_if_all_available: 'fdo_profile_path'"
-                + "    flag_group {"
-                + "      flag: '-fprofile-use=%{fdo_profile_path}'"
-                + "      flag: '-Xclang-only=-Wno-profile-instr-unprofiled'"
-                + "      flag: '-Xclang-only=-Wno-profile-instr-out-of-date'"
-                + "      flag: '-fprofile-correction'"
-                + "    }"
-                + "  }"
-                + "}",
-            toolchainBuilder);
-      }
-      if (!features.contains("autofdo")) {
-        TextFormat.merge(
-            ""
-                + "feature {"
-                + "  name: 'autofdo'"
-                + "  provides: 'profile'"
-                + "  flag_set {"
-                + "    action: 'c-compile'"
-                + "    action: 'c++-compile'"
-                + "    expand_if_all_available: 'fdo_profile_path'"
-                + "    flag_group {"
-                + "      flag: '-fauto-profile=%{fdo_profile_path}'"
-                + "      flag: '-fprofile-correction'"
-                + "    }"
-                + "  }"
-                + "}",
-            toolchainBuilder);
-      }
-      if (!features.contains("lipo")) {
-        TextFormat.merge(
-            ""
-                + "feature {"
-                + "  name: 'lipo'"
-                + "  requires { feature: 'autofdo' }"
-                + "  requires { feature: 'fdo_optimize' }"
-                + "  requires { feature: 'fdo_instrument' }"
-                + "  flag_set {"
-                + "    action: 'c-compile'"
-                + "    action: 'c++-compile'"
-                + "    flag_group {"
-                + "      flag: '-fripa'"
-                + "    }"
-                + "  }"
-                + "}",
-            toolchainBuilder);
-      }
-      if (!features.contains("coverage")) {
-        String compileFlags;
-        String linkerFlags;
-        if (useLLVMCoverageMap) {
-          compileFlags =
-              "flag_group {"
-              + " flag: '-fprofile-instr-generate'"
-              + " flag: '-fcoverage-mapping'"
-              + "}";
-          linkerFlags =
-              "  flag_group {"
-              + "  flag: '-fprofile-instr-generate'"
-              + "}";
-        } else {
-          compileFlags =
-              "  expand_if_all_available: 'gcov_gcno_file'"
-              + "flag_group {"
-              + "  flag: '-fprofile-arcs'"
-              + "  flag: '-ftest-coverage'"
-              + "}";
-          linkerFlags =
-              "  flag_group {"
-              + "  flag: '-lgcov'"
-              + "}";
+        if (!features.contains("random_seed")) {
+          // GCC and Clang give randomized names to symbols which are defined in
+          // an anonymous namespace but have external linkage.  To make
+          // computation of these deterministic, we want to override the
+          // default seed for the random number generator.  It's safe to use
+          // any value which differs for all translation units; we use the
+          // path to the object file.
+          TextFormat.merge(
+              ""
+                  + "feature {"
+                  + "  name: 'random_seed'"
+                  + "  flag_set {"
+                  + "    action: 'c++-compile'"
+                  + "    action: 'c++-module-compile'"
+                  + "    flag_group {"
+                  + "      flag: '-frandom-seed=%{output_file}'"
+                  + "    }"
+                  + "  }"
+                  + "}",
+              toolchainBuilder);
         }
-        TextFormat.merge(
-            ""
-                + "feature {"
-                + "  name: 'coverage'"
-                + "  provides: 'profile'"
-                + "  flag_set {"
-                + "    action: 'preprocess-assemble'"
-                + "    action: 'c-compile'"
-                + "    action: 'c++-compile'"
-                + "    action: 'c++-header-parsing'"
-                + "    action: 'c++-header-preprocessing'"
-                + "    action: 'c++-module-compile'"
-                + compileFlags
-                + "  }"
-                + "  flag_set {"
-                + "    action: 'c++-link-interface-dynamic-library'"
-                + "    action: 'c++-link-dynamic-library'"
-                + "    action: 'c++-link-executable'"
-                + linkerFlags
-                + "  }"
-                + "}",
-            toolchainBuilder);
+
+        if (!features.contains("pic")) {
+          TextFormat.merge(
+              ""
+                  + "feature {"
+                  + "  name: 'pic'"
+                  + "  flag_set {"
+                  + "    action: 'c-compile'"
+                  + "    action: 'c++-compile'"
+                  + "    action: 'c++-module-compile'"
+                  + "    action: 'preprocess-assemble'"
+                  + "    expand_if_all_available: 'pic'"
+                  + "    flag_group {"
+                  + "      flag: '-fPIC'"
+                  + "    }"
+                  + "  }"
+                  + "}",
+              toolchainBuilder);
+        }
+
+        if (!features.contains("per_object_debug_info")) {
+          TextFormat.merge(
+              ""
+                  + "feature {"
+                  + "  name: 'per_object_debug_info'"
+                  + "  flag_set {"
+                  + "    action: 'c-compile'"
+                  + "    action: 'c++-compile'"
+                  + "    action: 'assemble'"
+                  + "    action: 'preprocess-assemble'"
+                  + "    expand_if_all_available: 'per_object_debug_info_file'"
+                  + "    flag_group {"
+                  + "      flag: '-gsplit-dwarf'"
+                  + "    }"
+                  + "  }"
+                  + "}",
+              toolchainBuilder);
+        }
+
+        if (!features.contains("preprocessor_defines")) {
+          TextFormat.merge(
+              ""
+                  + "feature {"
+                  + "  name: 'preprocessor_defines'"
+                  + "  flag_set {"
+                  + "    action: 'preprocess-assemble'"
+                  + "    action: 'c-compile'"
+                  + "    action: 'c++-compile'"
+                  + "    action: 'c++-header-parsing'"
+                  + "    action: 'c++-header-preprocessing'"
+                  + "    action: 'c++-module-compile'"
+                  + "    action: 'clif-match'"
+                  + "    flag_group {"
+                  + "      flag: '-D%{preprocessor_defines}'"
+                  + "    }"
+                  + "  }"
+                  + "}",
+              toolchainBuilder);
+        }
+        if (!features.contains("include_paths")) {
+          TextFormat.merge(
+              ""
+                  + "feature {"
+                  + "  name: 'include_paths'"
+                  + "  flag_set {"
+                  + "    action: 'preprocess-assemble'"
+                  + "    action: 'c-compile'"
+                  + "    action: 'c++-compile'"
+                  + "    action: 'c++-header-parsing'"
+                  + "    action: 'c++-header-preprocessing'"
+                  + "    action: 'c++-module-compile'"
+                  + "    action: 'clif-match'"
+                  + "    action: 'objc-compile'"
+                  + "    action: 'objc++-compile'"
+                  + "    flag_group {"
+                  + "      flag: '-iquote'"
+                  + "      flag: '%{quote_include_paths}'"
+                  + "    }"
+                  + "    flag_group {"
+                  + "      flag: '-I%{include_paths}'"
+                  + "    }"
+                  + "    flag_group {"
+                  + "      flag: '-isystem'"
+                  + "      flag: '%{system_include_paths}'"
+                  + "    }"
+                  + "  }"
+                  + "}",
+              toolchainBuilder);
+        }
+        if (!features.contains("fdo_instrument")) {
+          TextFormat.merge(
+              ""
+                  + "feature {"
+                  + "  name: 'fdo_instrument'"
+                  + "  provides: 'profile'"
+                  + "  flag_set {"
+                  + "    action: 'c-compile'"
+                  + "    action: 'c++-compile'"
+                  + "    action: 'c++-link-interface-dynamic-library'"
+                  + "    action: 'c++-link-dynamic-library'"
+                  + "    action: 'c++-link-executable'"
+                  + "    flag_group {"
+                  + "      flag: '-fprofile-generate=%{fdo_instrument_path}'"
+                  + "      flag: '-fno-data-sections'"
+                  + "    }"
+                  + "  }"
+                  + "}",
+              toolchainBuilder);
+        }
+        if (!features.contains("fdo_optimize")) {
+          TextFormat.merge(
+              ""
+                  + "feature {"
+                  + "  name: 'fdo_optimize'"
+                  + "  provides: 'profile'"
+                  + "  flag_set {"
+                  + "    action: 'c-compile'"
+                  + "    action: 'c++-compile'"
+                  + "    expand_if_all_available: 'fdo_profile_path'"
+                  + "    flag_group {"
+                  + "      flag: '-fprofile-use=%{fdo_profile_path}'"
+                  + "      flag: '-Xclang-only=-Wno-profile-instr-unprofiled'"
+                  + "      flag: '-Xclang-only=-Wno-profile-instr-out-of-date'"
+                  + "      flag: '-fprofile-correction'"
+                  + "    }"
+                  + "  }"
+                  + "}",
+              toolchainBuilder);
+        }
+        if (!features.contains("autofdo")) {
+          TextFormat.merge(
+              ""
+                  + "feature {"
+                  + "  name: 'autofdo'"
+                  + "  provides: 'profile'"
+                  + "  flag_set {"
+                  + "    action: 'c-compile'"
+                  + "    action: 'c++-compile'"
+                  + "    expand_if_all_available: 'fdo_profile_path'"
+                  + "    flag_group {"
+                  + "      flag: '-fauto-profile=%{fdo_profile_path}'"
+                  + "      flag: '-fprofile-correction'"
+                  + "    }"
+                  + "  }"
+                  + "}",
+              toolchainBuilder);
+        }
+        if (!features.contains("lipo")) {
+          TextFormat.merge(
+              ""
+                  + "feature {"
+                  + "  name: 'lipo'"
+                  + "  requires { feature: 'autofdo' }"
+                  + "  requires { feature: 'fdo_optimize' }"
+                  + "  requires { feature: 'fdo_instrument' }"
+                  + "  flag_set {"
+                  + "    action: 'c-compile'"
+                  + "    action: 'c++-compile'"
+                  + "    flag_group {"
+                  + "      flag: '-fripa'"
+                  + "    }"
+                  + "  }"
+                  + "}",
+              toolchainBuilder);
+        }
+        if (!features.contains("coverage")) {
+          String compileFlags;
+          String linkerFlags;
+          if (useLLVMCoverageMap) {
+            compileFlags =
+                "flag_group {"
+                    + " flag: '-fprofile-instr-generate'"
+                    + " flag: '-fcoverage-mapping'"
+                    + "}";
+            linkerFlags = "  flag_group {" + "  flag: '-fprofile-instr-generate'" + "}";
+          } else {
+            compileFlags =
+                "  expand_if_all_available: 'gcov_gcno_file'"
+                    + "flag_group {"
+                    + "  flag: '-fprofile-arcs'"
+                    + "  flag: '-ftest-coverage'"
+                    + "}";
+            linkerFlags = "  flag_group {" + "  flag: '-lgcov'" + "}";
+          }
+          TextFormat.merge(
+              ""
+                  + "feature {"
+                  + "  name: 'coverage'"
+                  + "  provides: 'profile'"
+                  + "  flag_set {"
+                  + "    action: 'preprocess-assemble'"
+                  + "    action: 'c-compile'"
+                  + "    action: 'c++-compile'"
+                  + "    action: 'c++-header-parsing'"
+                  + "    action: 'c++-header-preprocessing'"
+                  + "    action: 'c++-module-compile'"
+                  + compileFlags
+                  + "  }"
+                  + "  flag_set {"
+                  + "    action: 'c++-link-interface-dynamic-library'"
+                  + "    action: 'c++-link-dynamic-library'"
+                  + "    action: 'c++-link-executable'"
+                  + linkerFlags
+                  + "  }"
+                  + "}",
+              toolchainBuilder);
+        }
+      } catch (ParseException e) {
+        // Can only happen if we change the proto definition without changing our
+        // configuration above.
+        throw new RuntimeException(e);
       }
-    } catch (ParseException e) {
-      // Can only happen if we change the proto definition without changing our configuration above.
-      throw new RuntimeException(e);
     }
+
+    toolchainBuilder.mergeFrom(toolchain);
     return toolchainBuilder.build();
   }
 
@@ -1026,7 +1036,8 @@
   private static final String SYSROOT_START = "%sysroot%/";
   private static final String WORKSPACE_START = "%workspace%/";
   private static final String CROSSTOOL_START = "%crosstool_top%/";
-  private static final String PACKAGE_START = "%package(", PACKAGE_END = ")%";
+  private static final String PACKAGE_START = "%package(";
+  private static final String PACKAGE_END = ")%";
 
   /**
    * Resolve the given include directory.
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppLinkActionBuilder.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppLinkActionBuilder.java
index 178d068..d6b2c62 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppLinkActionBuilder.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppLinkActionBuilder.java
@@ -100,6 +100,18 @@
   /** A build variable for the execpath of the output of the linker. */
   public static final String OUTPUT_EXECPATH_VARIABLE = "output_execpath";
 
+  /** A build variable setting if interface library should be generated. */
+  public static final String GENERATE_INTERFACE_LIBRARY_VARIABLE = "generate_interface_library";
+
+  /** A build variable for the path to the interface library builder tool. */
+  public static final String INTERFACE_LIBRARY_BUILDER_VARIABLE = "interface_library_builder_path";
+
+  /** A build variable for the input for the interface library builder tool. */
+  public static final String INTERFACE_LIBRARY_INPUT_VARIABLE = "interface_library_input_path";
+
+  /** A build variable for the path where to generate interface library using the builder tool. */
+  public static final String INTERFACE_LIBRARY_OUTPUT_VARIABLE = "interface_library_output_path";
+
   /**
    * A build variable that is set to indicate a mostly static linking for which the linked binary
    * should be piped to /dev/null.
@@ -320,6 +332,10 @@
   public List<String> getLinkstampOptions() {
     return this.linkstampOptions;
   }
+
+  protected Artifact getInterfaceSoBuilder() {
+    return analysisEnvironment.getEmbeddedToolArtifact(CppRuleClasses.BUILD_INTERFACE_SO);
+  }
   
   /**
    * Returns command line options for this link action.
@@ -552,7 +568,9 @@
                 runtimeLinkerInputs,
                 null,
                 linkerParamsFile,
-                ltoOutputRootPrefix)
+                ltoOutputRootPrefix,
+                null,
+                null)
             : new CppLinkVariablesExtension(
                 configuration,
                 linkstampMap,
@@ -561,7 +579,9 @@
                 runtimeLinkerInputs,
                 output,
                 linkerParamsFile,
-                PathFragment.EMPTY_FRAGMENT);
+                PathFragment.EMPTY_FRAGMENT,
+                getInterfaceSoBuilder(),
+                interfaceOutput);
     variablesExtension.addVariables(buildVariablesBuilder);
     for (VariablesExtension extraVariablesExtension : variablesExtensions) {
       extraVariablesExtension.addVariables(buildVariablesBuilder);
@@ -612,6 +632,7 @@
             .setParamFile(paramFile)
             .setToolchain(toolchain)
             .setBuildVariables(buildVariables)
+            .setToolPath(getToolPath())
             .setFeatureConfiguration(featureConfiguration);
 
     // TODO(b/30228443): Refactor noWholeArchiveInputs into action_configs, and remove this.
@@ -622,9 +643,7 @@
     if (!isLTOIndexing) {
       linkCommandLineBuilder
           .setOutput(output)
-          .setInterfaceOutput(interfaceOutput)
           .setBuildInfoHeaderArtifacts(buildInfoHeaderArtifacts)
-          .setInterfaceSoBuilder(getInterfaceSoBuilder())
           .setLinkstamps(linkstampMap)
           .setLinkopts(ImmutableList.copyOf(linkopts))
           .addLinkstampCompileOptions(linkstampOptions);
@@ -639,6 +658,7 @@
     // Compute the set of inputs - we only need stable order here.
     NestedSetBuilder<Artifact> dependencyInputsBuilder = NestedSetBuilder.stableOrder();
     dependencyInputsBuilder.addTransitive(crosstoolInputs);
+    dependencyInputsBuilder.add(toolchain.getLinkDynamicLibraryTool());
     dependencyInputsBuilder.addAll(linkActionInputs);
     if (runtimeMiddleman != null) {
       dependencyInputsBuilder.add(runtimeMiddleman);
@@ -739,6 +759,26 @@
         executionRequirements.build());
   }
 
+  /**
+   * Returns the tool path from feature configuration, if the tool in the configuration is sane, or
+   * builtin tool, if configuration has a dummy value.
+   */
+  private String getToolPath() {
+    if (!featureConfiguration.actionIsConfigured(linkType.getActionName())) {
+      return null;
+    }
+    String toolPath =
+        featureConfiguration
+            .getToolForAction(linkType.getActionName())
+            .getToolPath(cppConfiguration.getCrosstoolTopPathFragment())
+            .getPathString();
+    if (linkType.equals(LinkTargetType.DYNAMIC_LIBRARY)
+        && !featureConfiguration.hasConfiguredLinkerPathInActionConfig()) {
+      toolPath = toolchain.getLinkDynamicLibraryTool().getExecPathString();
+    }
+    return toolPath;
+  }
+
   /** The default heuristic on whether we need to use whole-archive for the link. */
   private static boolean needWholeArchive(
       LinkStaticness staticness,
@@ -809,10 +849,6 @@
     return ruleContext.getActionOwner();
   }
 
-  protected Artifact getInterfaceSoBuilder() {
-    return analysisEnvironment.getEmbeddedToolArtifact(CppRuleClasses.BUILD_INTERFACE_SO);
-  }
-
   /** Set the crosstool inputs required for the action. */
   public CppLinkActionBuilder setCrosstoolInputs(NestedSet<Artifact> inputs) {
     this.crosstoolInputs = inputs;
@@ -1196,6 +1232,8 @@
     private final Iterable<LinkerInput> linkerInputs;
     private final ImmutableList<LinkerInput> runtimeLinkerInputs;
     private final Artifact outputArtifact;
+    private final Artifact interfaceLibraryBuilder;
+    private final Artifact interfaceLibraryOutput;
     private final Artifact linkerParamsFile;
     private final PathFragment ltoOutputRootPrefix;
 
@@ -1209,13 +1247,17 @@
         ImmutableList<LinkerInput> runtimeLinkerInputs,
         Artifact output,
         Artifact linkerParamsFile,
-        PathFragment ltoOutputRootPrefix) {
+        PathFragment ltoOutputRootPrefix,
+        Artifact interfaceLibraryBuilder,
+        Artifact interfaceLibraryOutput) {
       this.configuration = configuration;
       this.linkstampMap = linkstampMap;
       this.needWholeArchive = needWholeArchive;
       this.linkerInputs = linkerInputs;
       this.runtimeLinkerInputs = runtimeLinkerInputs;
       this.outputArtifact = output;
+      this.interfaceLibraryBuilder = interfaceLibraryBuilder;
+      this.interfaceLibraryOutput = interfaceLibraryOutput;
       this.linkerParamsFile = linkerParamsFile;
       this.ltoOutputRootPrefix = ltoOutputRootPrefix;
 
@@ -1285,9 +1327,8 @@
       }
 
       // output exec path
-      if (this.outputArtifact != null) {
-        buildVariables.addVariable(
-            OUTPUT_EXECPATH_VARIABLE, this.outputArtifact.getExecPathString());
+      if (outputArtifact != null) {
+        buildVariables.addVariable(OUTPUT_EXECPATH_VARIABLE, outputArtifact.getExecPathString());
       }
 
       if (!ltoOutputRootPrefix.equals(PathFragment.EMPTY_FRAGMENT)) {
@@ -1303,6 +1344,21 @@
                 + ";"
                 + configuration.getBinDirectory().getExecPath().getRelative(ltoOutputRootPrefix));
       }
+      boolean shouldGenerateInterfaceLibrary =
+          outputArtifact != null
+              && interfaceLibraryBuilder != null
+              && interfaceLibraryOutput != null;
+      buildVariables.addVariable(
+          GENERATE_INTERFACE_LIBRARY_VARIABLE, shouldGenerateInterfaceLibrary ? "yes" : "no");
+      buildVariables.addVariable(
+          INTERFACE_LIBRARY_BUILDER_VARIABLE,
+          shouldGenerateInterfaceLibrary ? interfaceLibraryBuilder.getExecPathString() : "ignored");
+      buildVariables.addVariable(
+          INTERFACE_LIBRARY_INPUT_VARIABLE,
+          shouldGenerateInterfaceLibrary ? outputArtifact.getExecPathString() : "ignored");
+      buildVariables.addVariable(
+          INTERFACE_LIBRARY_OUTPUT_VARIABLE,
+          shouldGenerateInterfaceLibrary ? interfaceLibraryOutput.getExecPathString() : "ignored");
 
       // Variables arising from the toolchain
       buildVariables
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppLinkActionConfigs.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppLinkActionConfigs.java
index a359350..dc7b250 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppLinkActionConfigs.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppLinkActionConfigs.java
@@ -15,6 +15,7 @@
 
 import com.google.common.base.Joiner;
 import com.google.common.collect.ImmutableList;
+import java.util.Set;
 
 /**
  * A helper class for creating action_configs for the c++ link action.
@@ -30,7 +31,25 @@
     MAC
   }
 
-  public static String getCppLinkActionConfigs(CppLinkPlatform platform) {
+  public static String getCppLinkActionConfigs(
+      CppLinkPlatform platform, Set<String> features, String cppLinkDynamicLibraryToolPath) {
+    String cppDynamicLibraryLinkerTool = "";
+    if (!features.contains("dynamic_library_linker_tool")) {
+      cppDynamicLibraryLinkerTool =
+          ""
+              + "feature {"
+              + "   name: 'dynamic_library_linker_tool'"
+              + "   flag_set {"
+              + "       action: 'c++-link-dynamic-library'"
+              + "       flag_group {"
+              + "           flag: '"
+              + cppLinkDynamicLibraryToolPath
+              + "'"
+              + "       }"
+              + "   }"
+              + "}";
+    }
+
     return Joiner.on("\n")
         .join(
             ImmutableList.of(
@@ -55,6 +74,8 @@
                 "   tool {",
                 "       tool_path: 'DUMMY_TOOL'",
                 "   }",
+                "   implies: 'build_interface_libraries'",
+                "   implies: 'dynamic_library_linker_tool'",
                 "   implies: 'symbol_counts'",
                 "   implies: 'shared_flag'",
                 "   implies: 'linkstamps'",
@@ -109,6 +130,22 @@
                 "   implies: 'global_whole_archive_close'",
                 "}",
                 "feature {",
+                "   name: 'build_interface_libraries'",
+                "   flag_set {",
+                "       expand_if_all_available: 'generate_interface_library'",
+                "       action: 'c++-link-dynamic-library'",
+                "       flag_group {",
+                "           flag: '%{generate_interface_library}'",
+                "           flag: '%{interface_library_builder_path}'",
+                "           flag: '%{interface_library_input_path}'",
+                "           flag: '%{interface_library_output_path}'",
+                "       }",
+                "   }",
+                "}",
+                // Order of feature declaration matters, cppDynamicLibraryLinkerTool has to follow
+                // right after build_interface_libraries.
+                cppDynamicLibraryLinkerTool,
+                "feature {",
                 "   name: 'symbol_counts'",
                 "   flag_set {",
                 "       expand_if_all_available: 'symbol_counts_output'",
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/LinkCommandLine.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/LinkCommandLine.java
index db3ab65..27e3e4d 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/LinkCommandLine.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/LinkCommandLine.java
@@ -53,14 +53,13 @@
 @Immutable
 public final class LinkCommandLine extends CommandLine {
   private final String actionName;
-  private final BuildConfiguration configuration;
+  private final String toolPath;
   private final CppConfiguration cppConfiguration;
   private final ActionOwner owner;
   private final CcToolchainFeatures.Variables variables;
   // The feature config can be null for tests.
   @Nullable private final FeatureConfiguration featureConfiguration;
   @Nullable private final Artifact output;
-  @Nullable private final Artifact interfaceOutput;
   private final ImmutableList<Artifact> buildInfoHeaderArtifacts;
   private final Iterable<? extends LinkerInput> linkerInputs;
   private final Iterable<? extends LinkerInput> runtimeInputs;
@@ -77,14 +76,13 @@
   private final List<String> noWholeArchiveFlags;
 
   @Nullable private final Artifact paramFile;
-  @Nullable private final Artifact interfaceSoBuilder;
 
   private LinkCommandLine(
       String actionName,
+      String toolPath,
       BuildConfiguration configuration,
       ActionOwner owner,
       Artifact output,
-      @Nullable Artifact interfaceOutput,
       ImmutableList<Artifact> buildInfoHeaderArtifacts,
       Iterable<? extends LinkerInput> linkerInputs,
       Iterable<? extends LinkerInput> runtimeInputs,
@@ -100,23 +98,17 @@
       boolean useTestOnlyFlags,
       boolean needWholeArchive,
       @Nullable Artifact paramFile,
-      Artifact interfaceSoBuilder,
       List<String> noWholeArchiveFlags,
       CcToolchainFeatures.Variables variables,
       @Nullable FeatureConfiguration featureConfiguration) {
 
     this.actionName = actionName;
-    this.configuration = Preconditions.checkNotNull(configuration);
+    this.toolPath = toolPath;
     this.cppConfiguration = configuration.getFragment(CppConfiguration.class);
     this.variables = variables;
     this.featureConfiguration = featureConfiguration;
     this.owner = Preconditions.checkNotNull(owner);
     this.output = output;
-    this.interfaceOutput = interfaceOutput;
-    if (interfaceOutput != null) {
-      Preconditions.checkNotNull(this.output);
-    }
-
     this.buildInfoHeaderArtifacts = Preconditions.checkNotNull(buildInfoHeaderArtifacts);
     this.linkerInputs = Preconditions.checkNotNull(linkerInputs);
     this.runtimeInputs = Preconditions.checkNotNull(runtimeInputs);
@@ -135,23 +127,6 @@
     this.useTestOnlyFlags = useTestOnlyFlags;
     this.paramFile = paramFile;
     this.noWholeArchiveFlags = noWholeArchiveFlags;
-
-    // For now, silently ignore interfaceSoBuilder if we don't build an interface dynamic library.
-    this.interfaceSoBuilder =
-        ((linkTargetType == LinkTargetType.DYNAMIC_LIBRARY) && (interfaceOutput != null))
-            ? Preconditions.checkNotNull(
-                interfaceSoBuilder, "cannot build interface dynamic library without builder")
-            : null;
-  }
-
-  /**
-   * Returns an interface shared object output artifact produced during linking. This only returns
-   * non-null if {@link #getLinkTargetType} is {@code DYNAMIC_LIBRARY} and an interface shared
-   * object was requested.
-   */
-  @Nullable
-  public Artifact getInterfaceOutput() {
-    return interfaceOutput;
   }
 
   @Nullable
@@ -410,17 +385,7 @@
         break;
 
       case DYNAMIC_LIBRARY:
-        if (interfaceOutput != null) {
-          argv.add(configuration.getShellExecutable().getPathString());
-          argv.add("-c");
-          argv.add(
-              "build_iface_so=\"$0\"; impl=\"$1\"; iface=\"$2\"; cmd=\"$3\"; shift 3; "
-                  + "\"$cmd\" \"$@\" && \"$build_iface_so\" \"$impl\" \"$iface\"");
-          argv.add(interfaceSoBuilder.getExecPathString());
-          argv.add(output.getExecPathString());
-          argv.add(interfaceOutput.getExecPathString());
-        }
-        argv.add(cppConfiguration.getCppExecutable().getPathString());
+        argv.add(toolPath);
         argv.addAll(featureConfiguration.getCommandLine(actionName, variables));
         argv.addAll(noWholeArchiveFlags);
         addToolchainFlags(argv);
@@ -443,11 +408,7 @@
         // TODO(b/30109612): make this pattern the case for all link variants.
       case OBJC_ARCHIVE:
       case OBJC_FULLY_LINKED_ARCHIVE:
-        argv.add(
-            featureConfiguration
-                .getToolForAction(actionName)
-                .getToolPath(cppConfiguration.getCrosstoolTopPathFragment())
-                .getPathString());
+        argv.add(toolPath);
         argv.addAll(featureConfiguration.getCommandLine(actionName, variables));
         break;
 
@@ -664,8 +625,8 @@
     private final ActionOwner owner;
     @Nullable private final RuleContext ruleContext;
 
+    @Nullable private String toolPath;
     @Nullable private Artifact output;
-    @Nullable private Artifact interfaceOutput;
     private ImmutableList<Artifact> buildInfoHeaderArtifacts = ImmutableList.of();
     private Iterable<? extends LinkerInput> linkerInputs = ImmutableList.of();
     private Iterable<? extends LinkerInput> runtimeInputs = ImmutableList.of();
@@ -680,7 +641,6 @@
     private boolean useTestOnlyFlags;
     private boolean needWholeArchive;
     @Nullable private Artifact paramFile;
-    @Nullable private Artifact interfaceSoBuilder;
     @Nullable private CcToolchainProvider toolchain;
     private Variables variables;
     private FeatureConfiguration featureConfiguration;
@@ -740,10 +700,10 @@
 
       return new LinkCommandLine(
           actionName,
+          toolPath,
           configuration,
           owner,
           output,
-          interfaceOutput,
           buildInfoHeaderArtifacts,
           linkerInputs,
           runtimeInputs,
@@ -759,7 +719,6 @@
           useTestOnlyFlags,
           needWholeArchive,
           paramFile,
-          interfaceSoBuilder,
           noWholeArchiveFlags,
           variables,
           featureConfiguration);
@@ -774,9 +733,13 @@
       return this;
     }
 
-    /**
-     * Sets the feature configuration for this link action.
-     */
+    /** Sets the tool path, with tool being the first thing on the command line */
+    public Builder setToolPath(String toolPath) {
+      this.toolPath = toolPath;
+      return this;
+    }
+
+    /** Sets the feature configuration for this link action. */
     public Builder setFeatureConfiguration(FeatureConfiguration featureConfiguration) {
       this.featureConfiguration = featureConfiguration;
       return this;
@@ -818,16 +781,6 @@
     }
 
     /**
-     * Sets the additional interface output artifact, which is only used for dynamic libraries. The
-     * {@link #build} method throws an exception if the target type is not {@link
-     * LinkTargetType#DYNAMIC_LIBRARY}.
-     */
-    public Builder setInterfaceOutput(Artifact interfaceOutput) {
-      this.interfaceOutput = interfaceOutput;
-      return this;
-    }
-
-    /**
      * Sets the linker options. These are passed to the linker in addition to the other linker
      * options like linker inputs, symbol count options, etc. The {@link #build} method throws an
      * exception if the linker options are non-empty for a static link (see {@link
@@ -850,16 +803,6 @@
     }
 
     /**
-     * Sets the binary that should be used to create the interface output for a dynamic library.
-     * This is ignored unless the target type is {@link LinkTargetType#DYNAMIC_LIBRARY} and an
-     * interface output artifact is specified.
-     */
-    public Builder setInterfaceSoBuilder(Artifact interfaceSoBuilder) {
-      this.interfaceSoBuilder = interfaceSoBuilder;
-      return this;
-    }
-
-    /**
      * Sets the linkstamps. Linkstamps are additional C++ source files that are compiled as part of
      * the link command. The {@link #build} method throws an exception if the linkstamps are
      * non-empty for a static link (see {@link LinkTargetType#staticness()}}).
diff --git a/src/test/java/com/google/devtools/build/lib/packages/util/BazelMockCcSupport.java b/src/test/java/com/google/devtools/build/lib/packages/util/BazelMockCcSupport.java
index 4ecc1c0..521d56a 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/util/BazelMockCcSupport.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/util/BazelMockCcSupport.java
@@ -32,7 +32,8 @@
       new Predicate<String>() {
         @Override
         public boolean apply(String label) {
-          return !label.startsWith("@blaze_tools//tools/cpp/stl");
+          return !label.startsWith("@blaze_tools//tools/cpp/stl")
+              && !label.startsWith("@blaze_tools//tools/cpp/link_dynamic_library");
         }
       };
 
@@ -75,6 +76,7 @@
   public void setup(MockToolsConfig config) throws IOException {
     config.create(
         "/bazel_tools_workspace/tools/cpp/BUILD",
+        "package(default_visibility=['//visibility:public'])",
         "cc_library(name = 'stl')",
         "cc_library(name = 'malloc')",
         "cc_toolchain_suite(",
@@ -119,11 +121,20 @@
         "    linker_files = ':empty',",
         "    module_map = 'crosstool.cppmap', supports_header_parsing = 1,",
         "    objcopy_files = ':empty', static_runtime_libs = [':empty'], strip_files = ':empty',",
+        ")",
+        "filegroup(",
+        "    name = 'link_dynamic_library',",
+        "    srcs = ['link_dynamic_library.sh'],",
         ")");
 
     config.create(
         "/bazel_tools_workspace/tools/cpp/CROSSTOOL",
         readCrosstoolFile());
+    if (config.isRealFileSystem()) {
+      config.linkTool("tools/cpp/link_dynamic_library.sh");
+    } else {
+      config.create("tools/cpp/link_dynamic_library.sh", "");
+    }
     config.create(
         "/bazel_tools_workspace/tools/objc/BUILD",
         "xcode_config(name = 'host_xcodes')");
diff --git a/src/test/java/com/google/devtools/build/lib/packages/util/MockCcSupport.java b/src/test/java/com/google/devtools/build/lib/packages/util/MockCcSupport.java
index 0a7c700..5901ace 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/util/MockCcSupport.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/util/MockCcSupport.java
@@ -35,10 +35,7 @@
  */
 public abstract class MockCcSupport {
 
-  /**
-   * Filter to remove implicit crosstool artifact and module map inputs
-   * of C/C++ rules.
-   */
+  /** Filter to remove implicit crosstool artifact and module map inputs of C/C++ rules. */
   public static final Predicate<Artifact> CC_ARTIFACT_FILTER =
       new Predicate<Artifact>() {
         @Override
@@ -46,6 +43,7 @@
           String basename = artifact.getExecPath().getBaseName();
           String pathString = artifact.getExecPathString();
           return !pathString.startsWith("third_party/crosstool/")
+              && !pathString.startsWith("tools/cpp/link_dynamic_library")
               && !(pathString.contains("/internal/_middlemen") && basename.contains("crosstool"))
               && !pathString.startsWith("_bin/build_interface_so")
               && !pathString.endsWith(".cppmap");
@@ -502,7 +500,16 @@
         "package(default_visibility = ['//visibility:public'])",
         "cc_library(name = 'stl')",
         "alias(name='toolchain', actual='//third_party/crosstool')",
-        "cc_library(name = 'malloc')");
+        "cc_library(name = 'malloc')",
+        "filegroup(",
+        "    name = 'link_dynamic_library',",
+        "    srcs = ['link_dynamic_library.sh'],",
+        ")");
+    if (config.isRealFileSystem()) {
+      config.linkTool("tools/cpp/link_dynamic_library.sh");
+    } else {
+      config.create("tools/cpp/link_dynamic_library.sh", "");
+    }
   }
 
   protected void createCrosstoolPackage(MockToolsConfig config, boolean addEmbeddedRuntimes)
diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/CcLibraryConfiguredTargetTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/CcLibraryConfiguredTargetTest.java
index 2099df6..c8de88c 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/cpp/CcLibraryConfiguredTargetTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/CcLibraryConfiguredTargetTest.java
@@ -262,8 +262,6 @@
         LinkerInputs.toLibraryArtifacts(action.getLinkCommandLine().getLinkerInputs()));
     assertThat(cppLinkInfo.getInputFileList()).containsExactlyElementsIn(inputs);
     assertEquals(action.getPrimaryOutput().getExecPathString(), cppLinkInfo.getOutputFile());
-    Artifact interfaceOutput = action.getLinkCommandLine().getInterfaceOutput();
-    assertEquals(interfaceOutput.getExecPathString(), cppLinkInfo.getInterfaceOutputFile());
     assertEquals(action.getLinkCommandLine().getLinkTargetType().name(),
         cppLinkInfo.getLinkTargetType());
     assertEquals(action.getLinkCommandLine().getLinkStaticness().name(),
diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/CppLinkActionTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/CppLinkActionTest.java
index eda2715..cd92b39 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/cpp/CppLinkActionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/CppLinkActionTest.java
@@ -21,6 +21,7 @@
 
 import com.google.common.base.Joiner;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.devtools.build.lib.actions.Action;
 import com.google.devtools.build.lib.actions.ActionAnalysisMetadata;
@@ -54,10 +55,13 @@
 @RunWith(JUnit4.class)
 public class CppLinkActionTest extends BuildViewTestCase {
   private RuleContext createDummyRuleContext() throws Exception {
-    return view.getRuleContextForTesting(reporter, scratchConfiguredTarget(
-        "dummyRuleContext", "dummyRuleContext",
-        // CppLinkAction creation requires a CcToolchainProvider.
-        "cc_library(name = 'dummyRuleContext')"),
+    return view.getRuleContextForTesting(
+        reporter,
+        scratchConfiguredTarget(
+            "dummyRuleContext",
+            "dummyRuleContext",
+            // CppLinkAction creation requires a CcToolchainProvider.
+            "cc_library(name = 'dummyRuleContext')"),
         new StubAnalysisEnvironment() {
           @Override
           public void registerAction(ActionAnalysisMetadata... action) {
@@ -65,16 +69,23 @@
           }
 
           @Override
+          public Artifact getEmbeddedToolArtifact(String embeddedPath) {
+            return scratchArtifact("tools/interface_so_builder");
+          }
+
+          @Override
           public Artifact getDerivedArtifact(PathFragment rootRelativePath, Root root) {
             return CppLinkActionTest.this.getDerivedArtifact(
                 rootRelativePath, root, ActionsTestUtil.NULL_ARTIFACT_OWNER);
           }
-        }, masterConfig);
+        },
+        masterConfig);
   }
   
   private final FeatureConfiguration getMockFeatureConfiguration() throws Exception {
     return CcToolchainFeaturesTest.buildFeatures(
-            CppLinkActionConfigs.getCppLinkActionConfigs(CppLinkPlatform.LINUX))
+            CppLinkActionConfigs.getCppLinkActionConfigs(
+                CppLinkPlatform.LINUX, ImmutableSet.<String>of(), "dynamic_library_linker_tool"))
         .getFeatureConfiguration(
             Link.LinkTargetType.EXECUTABLE.getActionName(),
             Link.LinkTargetType.DYNAMIC_LIBRARY.getActionName(),
@@ -107,11 +118,10 @@
     CppLinkAction linkAction =
         createLinkBuilder(
                 Link.LinkTargetType.EXECUTABLE,
-                "out",
+                "dummyRuleContext/out",
                 ImmutableList.<Artifact>of(),
                 ImmutableList.<LibraryToLink>of(),
-                featureConfiguration,
-                false)
+                featureConfiguration)
             .build();
     assertThat(linkAction.getArgv()).contains("some_flag");
   }
@@ -160,11 +170,10 @@
     CppLinkAction linkAction =
         createLinkBuilder(
                 Link.LinkTargetType.EXECUTABLE,
-                "out",
+                "dummyRuleContext/out",
                 ImmutableList.<Artifact>of(),
                 ImmutableList.<LibraryToLink>of(),
-                featureConfiguration,
-                false)
+                featureConfiguration)
             .build();
     assertThat(linkAction.getEnvironment()).containsEntry("foo", "bar");
   }
@@ -315,11 +324,10 @@
     CppLinkAction linkAction =
         createLinkBuilder(
                 Link.LinkTargetType.EXECUTABLE,
-                "binary2",
+                "dummyRuleContext/binary2",
                 objects.build(),
                 ImmutableList.<LibraryToLink>of(),
-                new FeatureConfiguration(),
-                false)
+                new FeatureConfiguration())
             .setFake(true)
             .build();
 
@@ -350,8 +358,7 @@
       String outputPath,
       Iterable<Artifact> nonLibraryInputs,
       ImmutableList<LibraryToLink> libraryInputs,
-      FeatureConfiguration featureConfiguration,
-      boolean shouldIncludeToolchain)
+      FeatureConfiguration featureConfiguration)
       throws Exception {
     RuleContext ruleContext = createDummyRuleContext();
     CppLinkActionBuilder builder =
@@ -359,10 +366,10 @@
                 ruleContext,
                 new Artifact(
                     new PathFragment(outputPath),
-                    getTargetConfiguration().getBinDirectory(
-                        ruleContext.getRule().getRepository())),
+                    getTargetConfiguration()
+                        .getBinDirectory(ruleContext.getRule().getRepository())),
                 ruleContext.getConfiguration(),
-                shouldIncludeToolchain ? CppHelper.getToolchain(ruleContext) : null)
+                CppHelper.getToolchain(ruleContext))
             .addObjectFiles(nonLibraryInputs)
             .addLibraries(NestedSetBuilder.wrap(Order.LINK_ORDER, libraryInputs))
             .setLinkType(type)
@@ -382,8 +389,7 @@
         output.getPathString(),
         ImmutableList.<Artifact>of(),
         ImmutableList.<LibraryToLink>of(),
-        new FeatureConfiguration(),
-        true);
+        new FeatureConfiguration());
   }
 
   public Artifact getOutputArtifact(String relpath) {
@@ -424,6 +430,68 @@
   }
 
   @Test
+  public void testInterfaceOutputForDynamicLibrary() throws Exception {
+    FeatureConfiguration featureConfiguration =
+        CcToolchainFeaturesTest.buildFeatures(
+                "feature {",
+                "   name: 'build_interface_libraries'",
+                "   flag_set {",
+                "       action: '" + LinkTargetType.DYNAMIC_LIBRARY.getActionName() + "',",
+                "       flag_group {",
+                "           flag: '%{generate_interface_library}'",
+                "           flag: '%{interface_library_builder_path}'",
+                "           flag: '%{interface_library_input_path}'",
+                "           flag: '%{interface_library_output_path}'",
+                "       }",
+                "   }",
+                "}",
+                "feature {",
+                "   name: 'dynamic_library_linker_tool'",
+                "   flag_set {",
+                "       action: 'c++-link-dynamic-library'",
+                "       flag_group {",
+                "           flag: 'dynamic_library_linker_tool'",
+                "       }",
+                "   }",
+                "}",
+                "feature {",
+                "    name: 'has_configured_linker_path'",
+                "}",
+                "action_config {",
+                "   config_name: '" + LinkTargetType.DYNAMIC_LIBRARY.getActionName() + "'",
+                "   action_name: '" + LinkTargetType.DYNAMIC_LIBRARY.getActionName() + "'",
+                "   tool {",
+                "       tool_path: 'custom/crosstool/scripts/link_dynamic_library.sh'",
+                "   }",
+                "   implies: 'has_configured_linker_path'",
+                "   implies: 'build_interface_libraries'",
+                "   implies: 'dynamic_library_linker_tool'",
+                "}")
+            .getFeatureConfiguration(
+                "build_interface_libraries",
+                "dynamic_library_linker_tool",
+                LinkTargetType.DYNAMIC_LIBRARY.getActionName());
+    CppLinkActionBuilder builder =
+        createLinkBuilder(
+                LinkTargetType.DYNAMIC_LIBRARY,
+                "foo.so",
+                ImmutableList.<Artifact>of(),
+                ImmutableList.<LibraryToLink>of(),
+                featureConfiguration)
+            .setLibraryIdentifier("foo")
+            .setInterfaceOutput(scratchArtifact("FakeInterfaceOutput.ifso"));
+
+    List<String> commandLine = builder.build().getCommandLine();
+    assertThat(commandLine.size()).isGreaterThan(6);
+    assertThat(commandLine.get(0)).endsWith("custom/crosstool/scripts/link_dynamic_library.sh");
+    assertThat(commandLine.get(1)).isEqualTo("yes");
+    assertThat(commandLine.get(2)).isEqualTo("tools/interface_so_builder");
+    assertThat(commandLine.get(3)).endsWith("foo.so");
+    assertThat(commandLine.get(4)).isEqualTo("FakeInterfaceOutput.ifso");
+    assertThat(commandLine.get(5)).isEqualTo("dynamic_library_linker_tool");
+  }
+
+  @Test
   public void testStaticLinkWithDynamicIsError() throws Exception {
     CppLinkActionBuilder builder =
         createLinkBuilder(LinkTargetType.STATIC_LIBRARY)
diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/LibraryLinkingTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/LibraryLinkingTest.java
index 3adb69e..30adaf6 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/cpp/LibraryLinkingTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/LibraryLinkingTest.java
@@ -21,13 +21,11 @@
 import com.google.devtools.build.lib.analysis.ConfiguredTarget;
 import com.google.devtools.build.lib.analysis.FileProvider;
 import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
-
+import java.util.List;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
-import java.util.List;
-
 /**
  * Test for shared library linking {@link CppLinkAction}.
  */
@@ -35,8 +33,9 @@
 public final class LibraryLinkingTest extends BuildViewTestCase {
   private List<String> getLinkOpts(CppLinkAction linkAction, String... optionPatterns)
       throws Exception {
-    // Strip the first parameter from the argv, which is the gcc command.
-    return linkAction.getRawLinkArgv().subList(1, optionPatterns.length + 3);
+    // Strip the first parameters from the argv, which are the dynamic library script
+    // (usually tools/cpp/link_dynamic_library.sh), and its arguments.
+    return linkAction.getRawLinkArgv().subList(6, optionPatterns.length + 6);
   }
 
   private void assertLinkopts(CppLinkAction linkAction, String... optionPatterns) throws Exception {
diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/LinkBuildVariablesTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/LinkBuildVariablesTest.java
index 7e9a9dc..5cd2ce3 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/cpp/LinkBuildVariablesTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/LinkBuildVariablesTest.java
@@ -22,6 +22,7 @@
 import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
 import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.FeatureConfiguration;
 import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.Variables;
+import com.google.devtools.build.lib.rules.cpp.Link.LinkTargetType;
 import java.util.List;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -162,7 +163,60 @@
   }
 
   /**
-   * TODO(pcloudy): Add test for testing that necessary build variables are populated
-   * when alwayslink=1.
+   * TODO(pcloudy): Add test for testing that necessary build variables are populated when
+   * alwayslink=1.
    */
+  @Test
+  public void testInterfaceLibraryBuildingVariablesWhenGenerationPossible() throws Exception {
+    scratch.file("x/BUILD", "cc_library(", "   name = 'foo',", "   srcs = ['a.cc'],", ")");
+    scratch.file("x/a.cc");
+
+    ConfiguredTarget target = getConfiguredTarget("//x:foo");
+    Variables variables = getLinkBuildVariables(target, LinkTargetType.DYNAMIC_LIBRARY);
+
+    String interfaceLibraryBuilder =
+        Iterables.getOnlyElement(
+            getVariableValue(variables, CppLinkActionBuilder.INTERFACE_LIBRARY_BUILDER_VARIABLE));
+    String interfaceLibraryInput =
+        Iterables.getOnlyElement(
+            getVariableValue(variables, CppLinkActionBuilder.INTERFACE_LIBRARY_INPUT_VARIABLE));
+    String interfaceLibraryOutput =
+        Iterables.getOnlyElement(
+            getVariableValue(variables, CppLinkActionBuilder.INTERFACE_LIBRARY_OUTPUT_VARIABLE));
+    String generateInterfaceLibrary =
+        Iterables.getOnlyElement(
+            getVariableValue(variables, CppLinkActionBuilder.GENERATE_INTERFACE_LIBRARY_VARIABLE));
+
+    assertThat(generateInterfaceLibrary).isEqualTo("yes");
+    assertThat(interfaceLibraryInput).endsWith("libfoo.so");
+    assertThat(interfaceLibraryOutput).endsWith("libfoo.ifso");
+    assertThat(interfaceLibraryBuilder).endsWith("build_interface_so");
+  }
+
+  @Test
+  public void testInterfaceLibraryBuildingVariablesWhenGenerationNotAllowed() throws Exception {
+    scratch.file("x/BUILD", "cc_library(", "   name = 'foo',", "   srcs = ['a.cc'],", ")");
+    scratch.file("x/a.cc");
+
+    ConfiguredTarget target = getConfiguredTarget("//x:foo");
+    Variables variables = getLinkBuildVariables(target, LinkTargetType.STATIC_LIBRARY);
+
+    String interfaceLibraryBuilder =
+        Iterables.getOnlyElement(
+            getVariableValue(variables, CppLinkActionBuilder.INTERFACE_LIBRARY_BUILDER_VARIABLE));
+    String interfaceLibraryInput =
+        Iterables.getOnlyElement(
+            getVariableValue(variables, CppLinkActionBuilder.INTERFACE_LIBRARY_INPUT_VARIABLE));
+    String interfaceLibraryOutput =
+        Iterables.getOnlyElement(
+            getVariableValue(variables, CppLinkActionBuilder.INTERFACE_LIBRARY_OUTPUT_VARIABLE));
+    String generateInterfaceLibrary =
+        Iterables.getOnlyElement(
+            getVariableValue(variables, CppLinkActionBuilder.GENERATE_INTERFACE_LIBRARY_VARIABLE));
+
+    assertThat(generateInterfaceLibrary).isEqualTo("no");
+    assertThat(interfaceLibraryInput).endsWith("ignored");
+    assertThat(interfaceLibraryOutput).endsWith("ignored");
+    assertThat(interfaceLibraryBuilder).endsWith("ignored");
+  }
 }
diff --git a/tools/cpp/BUILD b/tools/cpp/BUILD
index 50d19a8..a3c8cbe 100644
--- a/tools/cpp/BUILD
+++ b/tools/cpp/BUILD
@@ -158,3 +158,8 @@
     name = "srcs",
     srcs = glob(["**"]) + ["//tools/cpp/test:srcs"],
 )
+
+filegroup(
+    name = "link_dynamic_library",
+    srcs = ["link_dynamic_library.sh"],
+)
diff --git a/tools/cpp/BUILD.static b/tools/cpp/BUILD.static
index 32a323a..6f52c9d 100644
--- a/tools/cpp/BUILD.static
+++ b/tools/cpp/BUILD.static
@@ -94,3 +94,8 @@
         "wrapper/bin/pydir/msvc*",
     ]),
 )
+
+filegroup(
+    name = "link_dynamic_library",
+    srcs = ["link_dynamic_library.sh"],
+)
diff --git a/tools/cpp/link_dynamic_library.sh b/tools/cpp/link_dynamic_library.sh
new file mode 100755
index 0000000..ae79a80
--- /dev/null
+++ b/tools/cpp/link_dynamic_library.sh
@@ -0,0 +1,60 @@
+#!/bin/bash
+#
+# Copyright 2016 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This script handles interface library generation for dynamic library
+# link action.
+#
+# Bazel can be configured to generate external interface library script
+# to generate interface libraries in CppLinkAction for dynamic libraries.
+# This is not needed on Windows (as the "interface" libraries are
+# generated by default). This script therefore handles the cases when
+# external script is provided, or when no script should be used.
+
+set -eu
+
+E_LINKER_COMMAND_NOT_FOUND=12
+E_INTERFACE_BUILDER_NOT_FOUND=13
+
+# Should generate interface library switch (<yes|no>); if the value is "no",
+# following 3 args are ignored (but must be present)
+GENERATE_INTERFACE_LIBRARY="$1"
+# Tool which can generate interface library from dynamic library file
+INTERFACE_LIBRARY_BUILDER="$2"
+# Dynamic library from which we want to generate interface library
+DYNAMIC_LIBRARY="$3"
+# Resulting interface library
+INTERFACE_LIBRARY="$4"
+# The command used to generate the dynamic library
+LINKER_COMMAND="$5"
+
+shift 5
+
+if [ ! -e "$LINKER_COMMAND" ]; then
+  echo "Linker command ($LINKER_COMMAND) not found." 1>&2;
+  exit "$E_LINKER_COMMAND_NOT_FOUND"
+fi
+
+if [ "no" == "$GENERATE_INTERFACE_LIBRARY" ]; then
+    INTERFACE_GENERATION=:
+else
+    if [ ! -e "$INTERFACE_LIBRARY_BUILDER" ]; then
+      echo "Interface library builder ($INTERFACE_LIBRARY_BUILDER) not found." 1>&2;
+      exit "$E_INTERFACE_BUILDER_NOT_FOUND"
+    fi
+    INTERFACE_GENERATION="${INTERFACE_LIBRARY_BUILDER} ${DYNAMIC_LIBRARY} ${INTERFACE_LIBRARY}"
+fi
+
+${LINKER_COMMAND} "$@" && ${INTERFACE_GENERATION}