Linker outputs can optionally be configured from the CROSSTOOL.  Introduces infrastructure to allow other artifact categories (such as debug symbols or compiler outputs) to be defined in other changes.

--
MOS_MIGRATED_REVID=128495797
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/ArtifactCategory.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/ArtifactCategory.java
new file mode 100644
index 0000000..04036fa
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/ArtifactCategory.java
@@ -0,0 +1,162 @@
+// 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.
+package com.google.devtools.build.lib.rules.cpp;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableMap;
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.analysis.RuleContext;
+import com.google.devtools.build.lib.vfs.PathFragment;
+
+/**
+ * A category of artifacts that are candidate input/output to an action, for which the toolchain can
+ * select a single artifact.
+ */
+public enum ArtifactCategory {
+  STATIC_LIBRARY {
+    @Override
+    public String getCategoryName() {
+      return STATIC_LIBRARY_CATEGORY_NAME;
+    }
+
+    @Override
+    public ImmutableMap<String, String> getTemplateVariables(RuleContext ruleContext) {
+      return ImmutableMap.of("base_name", ruleContext.getLabel().getName());
+    }
+  },
+
+  PIC_STATIC_LIBRARY {
+    @Override
+    public String getCategoryName() {
+      return PIC_STATIC_LIBRARY_CATEGORY_NAME;
+    }
+
+    @Override
+    public ImmutableMap<String, String> getTemplateVariables(RuleContext ruleContext) {
+      return ImmutableMap.of("base_name", ruleContext.getLabel().getName());
+    }
+  },
+
+  ALWAYS_LINK_STATIC_LIBRARY {
+    @Override
+    public String getCategoryName() {
+      return ALWAYS_LINK_STATIC_LIBRARY_CATEGORY_NAME;
+    }
+
+    @Override
+    public ImmutableMap<String, String> getTemplateVariables(RuleContext ruleContext) {
+      return ImmutableMap.of("base_name", ruleContext.getLabel().getName());
+    }
+  },
+
+  ALWAYS_LINK_PIC_STATIC_LIBRARY {
+    @Override
+    public String getCategoryName() {
+      return ALWAYS_LINK_PIC_STATIC_LIBRARY_CATEGORY_NAME;
+    }
+
+    @Override
+    public ImmutableMap<String, String> getTemplateVariables(RuleContext ruleContext) {
+      return ImmutableMap.of("base_name", ruleContext.getLabel().getName());
+    }
+  },
+
+  DYNAMIC_LIBRARY {
+    @Override
+    public String getCategoryName() {
+      return DYNAMIC_LIBRARY_CATEGORY_NAME;
+    }
+
+    @Override
+    public ImmutableMap<String, String> getTemplateVariables(RuleContext ruleContext) {
+      return ImmutableMap.of("base_name", ruleContext.getLabel().getName());
+    }
+  },
+
+  EXECUTABLE {
+    @Override
+    public String getCategoryName() {
+      return EXECUTABLE_CATEGORY_NAME;
+    }
+
+    @Override
+    public ImmutableMap<String, String> getTemplateVariables(RuleContext ruleContext) {
+      return ImmutableMap.of("base_name", ruleContext.getLabel().getName());
+    }
+  },
+
+  INTERFACE {
+    @Override
+    public String getCategoryName() {
+      return INTERFACE_CATEGORY_NAME;
+    }
+
+    @Override
+    public ImmutableMap<String, String> getTemplateVariables(RuleContext ruleContext) {
+      return ImmutableMap.of("base_name", ruleContext.getLabel().getName());
+    }
+  },
+
+  DEBUG_SYMBOLS {
+    @Override
+    public String getCategoryName() {
+      return DEBUG_SYMBOL_CATEGORY_NAME;
+    }
+
+    @Override
+    public ImmutableMap<String, String> getTemplateVariables(RuleContext ruleContext) {
+      return ImmutableMap.of();
+    }
+  };
+
+  /** Error for template evaluation failure. */
+  @VisibleForTesting
+  public static final String TEMPLATE_EVAL_FAILURE =
+      "Error evaluating file name pattern for artifact category %s: %s";
+
+  @VisibleForTesting public static final String STATIC_LIBRARY_CATEGORY_NAME = "static_library";
+
+  @VisibleForTesting
+  public static final String PIC_STATIC_LIBRARY_CATEGORY_NAME = "pic_static_library";
+
+  @VisibleForTesting
+  public static final String ALWAYS_LINK_STATIC_LIBRARY_CATEGORY_NAME = "alwayslink_static_library";
+
+  @VisibleForTesting
+  public static final String ALWAYS_LINK_PIC_STATIC_LIBRARY_CATEGORY_NAME =
+      "alwayslink_pic_static_library";
+
+  @VisibleForTesting public static final String DYNAMIC_LIBRARY_CATEGORY_NAME = "dynamic_library";
+  @VisibleForTesting public static final String EXECUTABLE_CATEGORY_NAME = "executable";
+  @VisibleForTesting public static final String INTERFACE_CATEGORY_NAME = "interface_library";
+  private static final String DEBUG_SYMBOL_CATEGORY_NAME = "debug_symbol";
+
+  /** Returns the name of the category. */
+  public abstract String getCategoryName();
+
+  /** Returns an artifact given a templated name. */
+  public Artifact getArtifactForName(String artifactName, RuleContext ruleContext) {
+    PathFragment name =
+        new PathFragment(ruleContext.getLabel().getName()).replaceName(artifactName);
+    return ruleContext.getPackageRelativeArtifact(
+        name, ruleContext.getConfiguration().getBinDirectory());
+  }
+
+  /**
+   * Returns a map of candidate template variables to their values. For example, the entry (foo,
+   * bar) indicates that the crosstool artifact name "library_%{foo}.extension" should be templated
+   * to "library_bar.extension".
+   */
+  public abstract ImmutableMap<String, String> getTemplateVariables(RuleContext ruleContext);
+}
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 f202ce1..eef2b31 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
@@ -156,8 +156,9 @@
     return CcBinary.init(semantics, context, /*fake =*/ false, /*isTest =*/ false);
   }
 
-  public static ConfiguredTarget init(CppSemantics semantics, RuleContext ruleContext, boolean fake,
-      boolean isTest) throws InterruptedException {
+  public static ConfiguredTarget init(
+      CppSemantics semantics, RuleContext ruleContext, boolean fake, boolean isTest)
+      throws InterruptedException, RuleErrorException {
     ruleContext.checkSrcsSamePackage(true);
     FeatureConfiguration featureConfiguration = CcCommon.configureFeatures(ruleContext);
     CcCommon common = new CcCommon(ruleContext);
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 e7db2a5..896ead8 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
@@ -106,12 +106,16 @@
     return builder.build();
   }
 
-  public static void init(CppSemantics semantics, RuleContext ruleContext,
-      RuleConfiguredTargetBuilder targetBuilder, LinkTargetType linkType,
+  public static void init(
+      CppSemantics semantics,
+      RuleContext ruleContext,
+      RuleConfiguredTargetBuilder targetBuilder,
+      LinkTargetType linkType,
       boolean neverLink,
       boolean linkStatic,
       boolean collectLinkstamp,
-      boolean addDynamicRuntimeInputArtifactsToRunfiles) {
+      boolean addDynamicRuntimeInputArtifactsToRunfiles)
+      throws RuleErrorException {
     FeatureConfiguration featureConfiguration = CcCommon.configureFeatures(ruleContext);
     final CcCommon common = new CcCommon(ruleContext);
     PrecompiledFiles precompiledFiles = new PrecompiledFiles(ruleContext);
@@ -186,19 +190,19 @@
     // doesn't support it, then register an action which complains when triggered,
     // which only happens when some rule explicitly depends on the dynamic library.
     if (!createDynamicLibrary && !supportsDynamicLinker) {
-      Artifact solibArtifact = CppHelper.getLinkedArtifact(
-          ruleContext, LinkTargetType.DYNAMIC_LIBRARY);
+      Artifact solibArtifact =
+          CppHelper.getLinuxLinkedArtifact(ruleContext, LinkTargetType.DYNAMIC_LIBRARY);
       ruleContext.registerAction(new FailAction(ruleContext.getActionOwner(),
           ImmutableList.of(solibArtifact), "Toolchain does not support dynamic linking"));
     } else if (!createDynamicLibrary
         && ruleContext.attributes().isConfigurable("srcs", BuildType.LABEL_LIST)) {
-    // If "srcs" is configurable, the .so output is always declared because the logic that
-    // determines implicit outs doesn't know which value of "srcs" will ultimately get chosen. Here,
-    // where we *do* have the correct value, it may not contain any source files to generate an
-    // .so with. If that's the case, register a fake generating action to prevent a "no generating
-    // action for this artifact" error.
-      Artifact solibArtifact = CppHelper.getLinkedArtifact(
-          ruleContext, LinkTargetType.DYNAMIC_LIBRARY);
+      // If "srcs" is configurable, the .so output is always declared because the logic that
+      // determines implicit outs doesn't know which value of "srcs" will ultimately get chosen. 
+      // Here, where we *do* have the correct value, it may not contain any source files to 
+      // generate an .so with. If that's the case, register a fake generating action to prevent 
+      // a "no generating action for this artifact" error.
+      Artifact solibArtifact =
+          CppHelper.getLinuxLinkedArtifact(ruleContext, LinkTargetType.DYNAMIC_LIBRARY);
       ruleContext.registerAction(new FailAction(ruleContext.getActionOwner(),
           ImmutableList.of(solibArtifact), "configurable \"srcs\" triggers an implicit .so output "
           + "even though there are no sources to compile in this configuration"));
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLibraryHelper.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLibraryHelper.java
index 401fefd..40b1b8b 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLibraryHelper.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLibraryHelper.java
@@ -39,6 +39,7 @@
 import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
 import com.google.devtools.build.lib.collect.nestedset.Order;
 import com.google.devtools.build.lib.packages.BuildType;
+import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException;
 import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.FeatureConfiguration;
 import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.Variables.VariablesExtension;
 import com.google.devtools.build.lib.rules.cpp.CppConfiguration.HeadersCheckingMode;
@@ -134,7 +135,6 @@
             Link.LinkTargetType.ALWAYS_LINK_PIC_STATIC_LIBRARY.getActionName(),
             Link.LinkTargetType.EXECUTABLE.getActionName()));
 
-
     private final FileTypeSet sourceTypeSet;
     private final Set<String> actionConfigSet;
 
@@ -851,8 +851,10 @@
 
   /**
    * Create the C++ compile and link actions, and the corresponding C++-related providers.
+   *
+   * @throws RuleErrorException
    */
-  public Info build() {
+  public Info build() throws RuleErrorException {
     // Fail early if there is no lipo context collector on the rule - otherwise we end up failing
     // in lipo optimization.
     Preconditions.checkState(
@@ -1038,14 +1040,16 @@
 
     if (ruleContext.attributes().get("alwayslink", Type.BOOLEAN)) {
       archiveFile.add(
-          CppHelper.getLinkedArtifact(ruleContext, Link.LinkTargetType.ALWAYS_LINK_STATIC_LIBRARY));
+          CppHelper.getLinuxLinkedArtifact(
+              ruleContext, Link.LinkTargetType.ALWAYS_LINK_STATIC_LIBRARY));
     } else {
-      archiveFile.add(CppHelper.getLinkedArtifact(ruleContext, Link.LinkTargetType.STATIC_LIBRARY));
+      archiveFile.add(
+          CppHelper.getLinuxLinkedArtifact(ruleContext, Link.LinkTargetType.STATIC_LIBRARY));
     }
 
     if (CppRuleClasses.shouldCreateDynamicLibrary(ruleContext.attributes())) {
       dynamicLibrary.add(
-          CppHelper.getLinkedArtifact(ruleContext, Link.LinkTargetType.DYNAMIC_LIBRARY));
+          CppHelper.getLinuxLinkedArtifact(ruleContext, Link.LinkTargetType.DYNAMIC_LIBRARY));
     }
 
     outputGroups.put("archive", archiveFile.build());
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 b48e03a..f935b4f 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
@@ -26,10 +26,13 @@
 import com.google.common.collect.ImmutableMultimap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.analysis.RuleContext;
 import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
 import com.google.devtools.build.lib.vfs.PathFragment;
 import com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig.CToolchain;
+import com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig.CToolchain.Tool;
 import java.io.IOException;
 import java.io.ObjectInputStream;
 import java.io.Serializable;
@@ -67,7 +70,7 @@
   
   /**
    * Thrown when a flag value cannot be expanded under a set of build variables.
-   * 
+   *
    * <p>This happens for example when a flag references a variable that is not provided by the
    * action, or when a flag group references multiple variables of sequence type.
    */
@@ -76,13 +79,17 @@
       super(message);
     }
   }
-  
+
+  /** Error message thrown when a toolchain does not provide a required artifact_name_pattern. */
+  public static final String MISSING_ARTIFACT_NAME_PATTERN_ERROR_TEMPLATE =
+      "Toolchain must provide artifact_name_pattern for category %s";
+
   /**
    * A piece of a single string value.
-   * 
-   * <p>A single value can contain a combination of text and variables (for example
-   * "-f %{var1}/%{var2}"). We split the string into chunks, where each chunk represents either a
-   * text snippet, or a variable that is to be replaced.
+   *
+   * <p>A single value can contain a combination of text and variables (for example "-f
+   * %{var1}/%{var2}"). We split the string into chunks, where each chunk represents either a text
+   * snippet, or a variable that is to be replaced.
    */
   interface StringChunk {
     
@@ -146,22 +153,21 @@
   
   /**
    * Parser for toolchain string values.
-   * 
+   *
    * <p>A string value contains a snippet of text supporting variable expansion. For example, a
-   * string value "-f %{var1}/%{var2}" will expand the values of the variables "var1" and "var2"
-   * in the corresponding places in the string.
-   * 
-   * <p>The {@code StringValueParser} takes a string and parses it into a list of
-   * {@link StringChunk} objects, where each chunk represents either a snippet of text or a
-   * variable to be expanded. In the above example, the resulting chunks would be
-   * ["-f ", var1, "/", var2].
-   * 
+   * string value "-f %{var1}/%{var2}" will expand the values of the variables "var1" and "var2" in
+   * the corresponding places in the string.
+   *
+   * <p>The {@code StringValueParser} takes a string and parses it into a list of {@link
+   * StringChunk} objects, where each chunk represents either a snippet of text or a variable to be
+   * expanded. In the above example, the resulting chunks would be ["-f ", var1, "/", var2].
+   *
    * <p>In addition to the list of chunks, the {@link StringValueParser} also provides the set of
    * variables necessary for the expansion of this flag via {@link #getUsedVariables}.
-   * 
+   *
    * <p>To get a literal percent character, "%%" can be used in the string.
    */
-  private static class StringValueParser {
+  static class StringValueParser {
 
     private final String value;
     
@@ -173,22 +179,18 @@
     private final ImmutableList.Builder<StringChunk> chunks = ImmutableList.builder();
     private final ImmutableSet.Builder<String> usedVariables = ImmutableSet.builder();
     
-    private StringValueParser(String value) throws InvalidConfigurationException {
+    StringValueParser(String value) throws InvalidConfigurationException {
       this.value = value;
       parse();
     }
     
-    /**
-     * @return the parsed chunks for this string.
-     */
-    private ImmutableList<StringChunk> getChunks() {
+    /** @return the parsed chunks for this string. */
+    ImmutableList<StringChunk> getChunks() {
       return chunks.build();
     }
     
-    /**
-     * @return all variable names needed to expand this string.
-     */
-    private ImmutableSet<String> getUsedVariables() {
+    /** @return all variable names needed to expand this string. */
+    ImmutableSet<String> getUsedVariables() {
       return usedVariables.build();
     }
     
@@ -402,14 +404,15 @@
 
     /**
      * Expands all flags in this group and adds them to {@code commandLine}.
-     * 
+     *
      * <p>The flags of the group will be expanded either:
+     *
      * <ul>
-     * <li>once, if there is no variable of sequence type in any of the group's flags, or</li>
-     * <li>for each element in the sequence, if there is one variable of sequence type within
-     * the flags.</li>
+     * <li>once, if there is no variable of sequence type in any of the group's flags, or
+     * <li>for each element in the sequence, if there is one variable of sequence type within the
+     *     flags.
      * </ul>
-     * 
+     *
      * <p>Having more than a single variable of sequence type in a single flag group is not
      * supported.
      */
@@ -633,6 +636,7 @@
       this.configName = actionConfig.getConfigName();
       this.actionName = actionConfig.getActionName();
       this.tools = actionConfig.getToolList();
+
       ImmutableList.Builder<FlagSet> flagSetBuilder = ImmutableList.builder();
       for (CToolchain.FlagSet flagSet : actionConfig.getFlagSetList()) {
         if (!flagSet.getActionList().isEmpty()) {
@@ -694,6 +698,57 @@
       }
     }
   }
+
+  /** A description of how artifacts of a certain type are named. */
+  @Immutable
+  private static class ArtifactNamePattern {
+
+    private final ArtifactCategory artifactCategory;
+    private final ImmutableSet<String> variables;
+    private final ImmutableList<StringChunk> chunks;
+
+    private ArtifactNamePattern(CToolchain.ArtifactNamePattern artifactNamePattern)
+        throws InvalidConfigurationException {
+
+      ArtifactCategory foundCategory = null;
+      for (ArtifactCategory artifactCategory : ArtifactCategory.values()) {
+        if (artifactNamePattern.getCategoryName().equals(artifactCategory.getCategoryName())) {
+          foundCategory = artifactCategory;
+        }
+      }
+      if (foundCategory == null) {
+        throw new ExpansionException(
+            String.format(
+                "Artifact category %s not recognized", artifactNamePattern.getCategoryName()));
+      }
+      this.artifactCategory = foundCategory;
+      
+      StringValueParser parser = new StringValueParser(artifactNamePattern.getPattern());
+      this.variables = parser.getUsedVariables();
+      this.chunks = parser.getChunks();
+    }
+
+    /** Returns the ArtifactCategory for this ArtifactNamePattern. */
+    ArtifactCategory getArtifactCategory() {
+      return this.artifactCategory;
+    }
+
+    /**
+     * Returns the artifact name that pattern selects for a given rule.
+     */
+    public String getArtifactName(RuleContext ruleContext) {
+      StringBuilder result = new StringBuilder();
+      Variables.View artifactNameVariables =
+          new Variables.Builder()
+              .addAllVariables(artifactCategory.getTemplateVariables(ruleContext))
+              .build()
+              .getView(variables);
+      for (StringChunk chunk : chunks) {
+        chunk.expand(artifactNameVariables.getVariables(), result);
+      }
+      return result.toString();
+    }
+  }
   
   /**
    * Configured build variables usable by the toolchain configuration.
@@ -1132,6 +1187,9 @@
       return actionConfig.getTool(enabledFeatureNames);
     }
   }
+
+  /** All artifact name patterns defined in this feature configuration. */
+  private final ImmutableList<ArtifactNamePattern> artifactNamePatterns;
   
   /**
    * All features and action configs in the order in which they were specified in the configuration.
@@ -1194,6 +1252,7 @@
    *
    * @param toolchain the toolchain configuration as specified by the user.
    * @throws InvalidConfigurationException if the configuration has logical errors.
+   * @throws ArtifactNamePatternNotProvidedException
    */
   @VisibleForTesting
   public CcToolchainFeatures(CToolchain toolchain) throws InvalidConfigurationException {
@@ -1219,7 +1278,7 @@
       selectablesByName.put(actionConfig.getName(), actionConfig);
       actionConfigsByActionName.put(actionConfig.getActionName(), actionConfig);
     }
-
+       
     this.selectables = selectablesBuilder.build();
     this.selectablesByName = ImmutableMap.copyOf(selectablesByName);
 
@@ -1228,6 +1287,14 @@
 
     this.actionConfigsByActionName = actionConfigsByActionName.build();
 
+    ImmutableList.Builder<ArtifactNamePattern> artifactNamePatternsBuilder =
+        ImmutableList.builder();
+    for (CToolchain.ArtifactNamePattern artifactNamePattern :
+        toolchain.getArtifactNamePatternList()) {
+      artifactNamePatternsBuilder.add(new ArtifactNamePattern(artifactNamePattern));
+    }
+    this.artifactNamePatterns = artifactNamePatternsBuilder.build();
+
     // Next, we build up all forward references for 'implies' and 'requires' edges.
     ImmutableMultimap.Builder<CrosstoolSelectable, CrosstoolSelectable> implies =
         ImmutableMultimap.builder();
@@ -1383,10 +1450,42 @@
   }
   
   /**
+   * Returns the artifact selected by the toolchain for the given action type and action category,
+   * or null if the category is not supported by the action config.
+   */
+  Artifact getArtifactForCategory(ArtifactCategory artifactCategory, RuleContext ruleContext)
+      throws ExpansionException {
+    ArtifactNamePattern patternForCategory = null;
+    for (ArtifactNamePattern artifactNamePattern : artifactNamePatterns) {
+      if (artifactNamePattern.getArtifactCategory() == artifactCategory) {
+        patternForCategory = artifactNamePattern;
+      }
+    }
+    if (patternForCategory == null) {
+      throw new ExpansionException(
+          String.format(
+              MISSING_ARTIFACT_NAME_PATTERN_ERROR_TEMPLATE, artifactCategory.getCategoryName()));
+    }
+
+    String templatedName = patternForCategory.getArtifactName(ruleContext);
+    return artifactCategory.getArtifactForName(templatedName, ruleContext);
+  }
+
+  /** Returns true if the toolchain defines an ArtifactNamePattern for the given category. */
+  boolean hasPatternForArtifactCategory(ArtifactCategory artifactCategory) {
+    for (ArtifactNamePattern artifactNamePattern : artifactNamePatterns) {
+      if (artifactNamePattern.getArtifactCategory() == artifactCategory) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
    * Implements the feature selection algorithm.
-   * 
-   * <p>Feature selection is done by first enabling all features reachable by an 'implies' edge,
-   * and then iteratively pruning features that have unmet requirements.
+   *
+   * <p>Feature selection is done by first enabling all features reachable by an 'implies' edge, and
+   * then iteratively pruning features that have unmet requirements.
    */
   private class FeatureSelection {
     
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppHelper.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppHelper.java
index 4a20c22..b3367f4 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppHelper.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppHelper.java
@@ -324,10 +324,8 @@
         src.getRoot());
   }
 
-  /**
-   * Returns the linked artifact.
-   */
-  public static Artifact getLinkedArtifact(RuleContext ruleContext, LinkTargetType linkType) {
+  /** Returns the linked artifact for linux. */
+  public static Artifact getLinuxLinkedArtifact(RuleContext ruleContext, LinkTargetType linkType) {
     PathFragment name = new PathFragment(ruleContext.getLabel().getName());
     if (linkType != LinkTargetType.EXECUTABLE) {
       name = name.replaceName("lib" + name.getBaseName() + linkType.getExtension());
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppModel.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppModel.java
index 38aa8b9..13866e8 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppModel.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppModel.java
@@ -18,6 +18,7 @@
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.actions.FailAction;
 import com.google.devtools.build.lib.analysis.AnalysisEnvironment;
 import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode;
 import com.google.devtools.build.lib.analysis.RuleContext;
@@ -26,7 +27,9 @@
 import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.collect.nestedset.NestedSet;
 import com.google.devtools.build.lib.packages.BuildType;
+import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException;
 import com.google.devtools.build.lib.rules.cpp.CcCompilationOutputs.Builder;
+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.CcToolchainFeatures.Variables.VariablesExtension;
 import com.google.devtools.build.lib.rules.cpp.CppCompileAction.DotdFile;
@@ -737,6 +740,49 @@
   }
 
   /**
+   * Returns the linked artifact resulting from a linking of the given type. Consults the feature
+   * configuration to obtain an action_config that provides the artifact. If the feature
+   * configuration provides no artifact, uses a default.
+   *
+   * <p>We cannot assume that the feature configuration contains an action_config for the link
+   * action, because the linux link action depends on hardcoded values in
+   * LinkCommandLine.getRawLinkArgv(), which are applied on the condition that an action_config is
+   * not present.
+   * TODO(b/30393154): Assert that the given link action has an action_config.
+   *
+   * @throws RuleErrorException
+   */
+  private Artifact getLinkedArtifact(LinkTargetType linkTargetType) throws RuleErrorException {
+    Artifact result = null;
+    Artifact linuxDefault = CppHelper.getLinuxLinkedArtifact(ruleContext, linkTargetType);
+    CcToolchainFeatures toolchain = CppHelper.getToolchain(ruleContext).getFeatures();
+
+    if (toolchain.hasPatternForArtifactCategory(linkTargetType.getLinkerOutput())) {
+      try {
+        result = toolchain.getArtifactForCategory(linkTargetType.getLinkerOutput(), ruleContext);
+      } catch (ExpansionException e) {
+        ruleContext.throwWithRuleError(e.getMessage());
+      }
+    } else {
+      return linuxDefault;
+    }
+    // If the linked artifact is not the linux default, then a FailAction is generated for the
+    // linux default to satisfy the requirement of the implicit output.
+    // TODO(b/30132703): Remove the implicit outputs of cc_library.
+    if (!result.equals(linuxDefault)) {
+      ruleContext.registerAction(
+          new FailAction(
+              ruleContext.getActionOwner(),
+              ImmutableList.of(linuxDefault),
+              String.format(
+                  "the given toolchain supports creation of %s instead of %s",
+                  linuxDefault.getExecPathString(), result.getExecPathString())));
+    }
+
+    return result;
+  }
+
+  /**
    * 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,
    * it additionally creates a third action to generate a PIC static library.
@@ -745,8 +791,11 @@
    * can be used for linking, but doesn't contain any executable code. This increases the number of
    * cache hits for link actions. Call {@link #setAllowInterfaceSharedObjects(boolean)} to enable
    * this behavior.
+   *
+   * @throws RuleErrorException
    */
-  public CcLinkingOutputs createCcLinkActions(CcCompilationOutputs ccOutputs) {
+  public CcLinkingOutputs createCcLinkActions(CcCompilationOutputs ccOutputs)
+      throws RuleErrorException {
     // For now only handle static links. Note that the dynamic library link below ignores linkType.
     // TODO(bazel-team): Either support non-static links or move this check to setLinkType().
     Preconditions.checkState(linkType.isStaticLibraryLink(), "can only handle static links");
@@ -775,7 +824,11 @@
     //
     // Presumably, it is done this way because the .a file is an implicit output of every cc_library
     // rule, so we can't use ".pic.a" that in the always-PIC case.
-    Artifact linkedArtifact = CppHelper.getLinkedArtifact(ruleContext, linkType);
+
+    // If the crosstool is configured to select an output artifact, we use that selection.
+    // Otherwise, we use linux defaults.
+    Artifact linkedArtifact = getLinkedArtifact(linkType);
+
     CppLinkAction maybePicAction =
         newLinkActionBuilder(linkedArtifact)
             .addNonLibraryInputs(ccOutputs.getObjectFiles(usePicForBinaries))
@@ -796,7 +849,10 @@
           ? LinkTargetType.ALWAYS_LINK_PIC_STATIC_LIBRARY
           : LinkTargetType.PIC_STATIC_LIBRARY;
 
-      Artifact picArtifact = CppHelper.getLinkedArtifact(ruleContext, picLinkType);
+      // If the crosstool is configured to select an output artifact, we use that selection.
+      // Otherwise, we use linux defaults.
+      Artifact picArtifact = getLinkedArtifact(picLinkType);
+
       CppLinkAction picAction =
           newLinkActionBuilder(picArtifact)
               .addNonLibraryInputs(ccOutputs.getObjectFiles(true))
@@ -817,7 +873,9 @@
     // Create dynamic library.
     Artifact soImpl;
     if (soImplArtifact == null) {
-      soImpl = CppHelper.getLinkedArtifact(ruleContext, LinkTargetType.DYNAMIC_LIBRARY);
+      // If the crosstool is configured to select an output artifact, we use that selection.
+      // Otherwise, we use linux defaults.
+      soImpl = getLinkedArtifact(LinkTargetType.DYNAMIC_LIBRARY);
     } else {
       soImpl = soImplArtifact;
     }
@@ -826,7 +884,7 @@
     Artifact soInterface = null;
     if (cppConfiguration.useInterfaceSharedObjects() && allowInterfaceSharedObjects) {
       soInterface =
-          CppHelper.getLinkedArtifact(ruleContext, LinkTargetType.INTERFACE_DYNAMIC_LIBRARY);
+          CppHelper.getLinuxLinkedArtifact(ruleContext, LinkTargetType.INTERFACE_DYNAMIC_LIBRARY);
       sonameLinkopts = ImmutableList.of("-Wl,-soname=" +
           SolibSymlinkAction.getDynamicLibrarySoname(soImpl.getRootRelativePath(), false));
     }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/Link.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/Link.java
index 38ab7e8..8826b0a 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/Link.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/Link.java
@@ -94,34 +94,50 @@
    */
   public enum LinkTargetType {
     /** A normal static archive. */
-    STATIC_LIBRARY(".a", true, "c++-link-static-library"),
+    STATIC_LIBRARY(".a", true, "c++-link-static-library", ArtifactCategory.STATIC_LIBRARY),
 
     /** A static archive with .pic.o object files (compiled with -fPIC). */
-    PIC_STATIC_LIBRARY(".pic.a", true, "c++-link-pic-static-library"),
+    PIC_STATIC_LIBRARY(
+        ".pic.a", true, "c++-link-pic-static-library", ArtifactCategory.PIC_STATIC_LIBRARY),
 
     /** An interface dynamic library. */
-    INTERFACE_DYNAMIC_LIBRARY(".ifso", false, "c++-link-interface-dynamic-library"),
+    INTERFACE_DYNAMIC_LIBRARY(
+        ".ifso", false, "c++-link-interface-dynamic-library", ArtifactCategory.INTERFACE),
 
     /** A dynamic library. */
-    DYNAMIC_LIBRARY(".so", false, "c++-link-dynamic-library"),
+    DYNAMIC_LIBRARY(".so", false, "c++-link-dynamic-library", ArtifactCategory.DYNAMIC_LIBRARY),
 
     /** A static archive without removal of unused object files. */
-    ALWAYS_LINK_STATIC_LIBRARY(".lo", true, "c++-link-alwayslink-static-library"),
+    ALWAYS_LINK_STATIC_LIBRARY(
+        ".lo",
+        true,
+        "c++-link-alwayslink-static-library",
+        ArtifactCategory.ALWAYS_LINK_STATIC_LIBRARY),
 
     /** A PIC static archive without removal of unused object files. */
-    ALWAYS_LINK_PIC_STATIC_LIBRARY(".pic.lo", true, "c++-link-alwayslink-pic-static-library"),
+    ALWAYS_LINK_PIC_STATIC_LIBRARY(
+        ".pic.lo",
+        true,
+        "c++-link-alwayslink-pic-static-library",
+        ArtifactCategory.ALWAYS_LINK_PIC_STATIC_LIBRARY),
 
     /** An executable binary. */
-    EXECUTABLE("", false, "c++-link-executable");
+    EXECUTABLE("", false, "c++-link-executable", ArtifactCategory.EXECUTABLE);
 
     private final String extension;
     private final boolean staticLibraryLink;
     private final String actionName;
+    private final ArtifactCategory linkerOutput;
 
-    private LinkTargetType(String extension, boolean staticLibraryLink, String actionName) {
+    private LinkTargetType(
+        String extension,
+        boolean staticLibraryLink,
+        String actionName,
+        ArtifactCategory linkerOutput) {
       this.extension = extension;
       this.staticLibraryLink = staticLibraryLink;
       this.actionName = actionName;
+      this.linkerOutput = linkerOutput;
     }
 
     public String getExtension() {
@@ -132,6 +148,11 @@
       return staticLibraryLink;
     }
     
+    /** Returns an {@code ArtifactCategory} identifying the artifact type this link action emits. */
+    public ArtifactCategory getLinkerOutput() {
+      return linkerOutput;
+    }
+    
     /**
      * The name of a link action with this LinkTargetType, for the purpose of crosstool feature
      * selection.
diff --git a/src/main/protobuf/crosstool_config.proto b/src/main/protobuf/crosstool_config.proto
index bab618c..0ce4984 100644
--- a/src/main/protobuf/crosstool_config.proto
+++ b/src/main/protobuf/crosstool_config.proto
@@ -208,6 +208,20 @@
     repeated string execution_requirement = 3;
   }
 
+  // The name for an artifact of a given category of input or output artifacts
+  // to an action.
+  message ArtifactNamePattern {
+    // The category of artifacts that this selection applies to.  This field
+    // is compared against a list of categories defined in bazel. Example
+    // categories include "linked_output" or "debug_symbols". An error is thrown
+    // if no category is matched.
+    required string category_name = 1;
+    // The pattern for creating the artifact for this selection.  The given
+    // pattern is templated by bazel and then used to create an artifact in
+    // a target-specific directory in bazel-bin.
+    required string pattern = 2;
+  }
+
   // An action config corresponds to a blaze action, and allows selection of
   // a tool based on activated features.  Action configs come in two varieties:
   // automatic (the blaze action will exist whether or not the action config
@@ -217,6 +231,7 @@
   // Action config activation occurs by the same semantics as features: a
   // feature can 'require' or 'imply' an action config in the same way that it
   // would another feature.
+  // Next ID: 8
   message ActionConfig {
     // The name other features will use to activate this action config.  Can
     // be the same as action_name.
@@ -261,6 +276,7 @@
 
   repeated Feature feature = 50;
   repeated ActionConfig action_config = 53;
+  repeated ArtifactNamePattern artifact_name_pattern = 54;
 
   // The unique identifier of the toolchain within the crosstool release. It
   // must be possible to use this as a directory name in a path.
@@ -401,7 +417,7 @@
   // why they are recorded here.
   repeated string debian_extra_requires = 33;
 
-  // Next free id: 54
+  // Next free id: 55
 }
 
 message ToolPath {
diff --git a/src/test/java/com/google/devtools/build/lib/BUILD b/src/test/java/com/google/devtools/build/lib/BUILD
index 85e6514..55f7f5f 100644
--- a/src/test/java/com/google/devtools/build/lib/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/BUILD
@@ -527,6 +527,7 @@
         "//src/main/java/com/google/devtools/build/lib:util",
         "//src/main/java/com/google/devtools/build/lib:vfs",
         "//src/main/java/com/google/devtools/build/lib/actions",
+        "//src/main/java/com/google/devtools/build/lib/rules/cpp",
         "//src/main/java/com/google/devtools/build/skyframe",
         "//src/main/java/com/google/devtools/common/options",
         "//src/main/protobuf:build_java_proto",
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 53e90d2..a3c5af2 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
@@ -209,6 +209,27 @@
           + "  }"
           + "}";
 
+  public static final String STATIC_LINK_AS_LIB_CONFIGURATION =
+      ""
+          + "artifact_name_pattern {"
+          + "   category_name: 'static_library'"
+          + "   pattern: '%{base_name}.lib'"
+          + "}";
+
+  public static final String STATIC_LINK_AS_DOT_A_CONFIGURATION =
+      ""
+          + "artifact_name_pattern {"
+          + "   category_name: 'static_library'"
+          + "   pattern: 'lib%{base_name}.a'"
+          + "}";
+
+  public static final String STATIC_LINK_BAD_TEMPLATE_CONFIGURATION =
+      ""
+          + "artifact_name_pattern {"
+          + "   category_name: 'static_library'"
+          + "   pattern: 'foo%{bad_variable}bar'"
+          + "}";
+
   /** Filter to remove implicit dependencies of C/C++ rules. */
   private final Predicate<Label> ccLabelFilter =
       new Predicate<Label>() {
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 45c7bd5..a374dad 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
@@ -22,6 +22,7 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import com.google.common.base.Predicate;
 import com.google.common.collect.ImmutableList;
@@ -242,6 +243,50 @@
   }
 
   @Test
+  public void testLinkActionCanConsumeArtifactExtensions() throws Exception {
+    AnalysisMock.get()
+        .ccSupport()
+        .setupCrosstool(mockToolsConfig, MockCcSupport.STATIC_LINK_AS_LIB_CONFIGURATION);
+    useConfiguration("--features=" + Link.LinkTargetType.STATIC_LIBRARY.getActionName());
+    ConfiguredTarget hello = getConfiguredTarget("//hello:hello");
+    Artifact archive =
+        FileType.filter(getFilesToBuild(hello), FileType.of(".lib")).iterator().next();
+
+    CppLinkAction action = (CppLinkAction) getGeneratingAction(archive);
+
+    assertThat(action.getArgv()).contains(archive.getExecPathString());
+  }
+
+  @Test
+  public void testArtifactSelectionBaseNameTemplating() throws Exception {
+    AnalysisMock.get()
+        .ccSupport()
+        .setupCrosstool(mockToolsConfig, MockCcSupport.STATIC_LINK_AS_DOT_A_CONFIGURATION);
+    useConfiguration("--features=" + Link.LinkTargetType.STATIC_LIBRARY.getActionName());
+    ConfiguredTarget hello = getConfiguredTarget("//hello:hello");
+    Artifact archive =
+        FileType.filter(getFilesToBuild(hello), CppFileTypes.ARCHIVE).iterator().next();
+    assertThat(archive.getExecPathString()).endsWith("libhello.a");
+  }
+
+  @Test
+  public void testArtifactSelectionErrorOnBadTemplateVariable() throws Exception {
+    AnalysisMock.get()
+        .ccSupport()
+        .setupCrosstool(mockToolsConfig, MockCcSupport.STATIC_LINK_BAD_TEMPLATE_CONFIGURATION);
+    useConfiguration("--features=" + Link.LinkTargetType.STATIC_LIBRARY.getActionName());
+    try {
+      getConfiguredTarget("//hello:hello");
+      fail("Should fail");
+    } catch (AssertionError e) {
+      assertThat(e.getMessage())
+          .contains(
+              "Invalid toolchain configuration: unknown variable 'bad_variable' "
+                  + "can not be expanded.");
+    }
+  }
+
+  @Test
   public void testArtifactsToAlwaysBuild() throws Exception {
     // ArtifactsToAlwaysBuild should apply both for static libraries.
     ConfiguredTarget helloStatic = getConfiguredTarget("//hello:hello_static");