aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorGravatar Cal Peyser <cpeyser@google.com>2016-07-26 18:39:13 +0000
committerGravatar Damien Martin-Guillerez <dmarting@google.com>2016-07-27 11:15:10 +0000
commit9fce7603ffd4f63b5b7e819e6699a603acda965c (patch)
treeaccc96ee605ce840f44614d029ca082ffbded9ea /src
parentcb5aa0067d5775c5ada1b751adc502ad2375352b (diff)
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
Diffstat (limited to 'src')
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/cpp/ArtifactCategory.java162
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/cpp/CcBinary.java5
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/cpp/CcLibrary.java28
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/cpp/CcLibraryHelper.java14
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainFeatures.java169
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/cpp/CppHelper.java6
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/cpp/CppModel.java68
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/cpp/Link.java37
-rw-r--r--src/main/protobuf/crosstool_config.proto18
-rw-r--r--src/test/java/com/google/devtools/build/lib/BUILD1
-rw-r--r--src/test/java/com/google/devtools/build/lib/packages/util/MockCcSupport.java21
-rw-r--r--src/test/java/com/google/devtools/build/lib/rules/cpp/CcLibraryConfiguredTargetTest.java45
12 files changed, 502 insertions, 72 deletions
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 0000000000..04036faf6f
--- /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 f202ce123c..eef2b319de 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 @@ public abstract class CcBinary implements RuleConfiguredTargetFactory {
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 e7db2a5273..896ead8c13 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 @@ public abstract class CcLibrary implements RuleConfiguredTargetFactory {
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 @@ public abstract class CcLibrary implements RuleConfiguredTargetFactory {
// 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 401fefd881..40b1b8b7df 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.NestedSet;
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 @@ public final class CcLibraryHelper {
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 @@ public final class CcLibraryHelper {
/**
* 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 @@ public final class CcLibraryHelper {
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 b48e03a3f7..f935b4faac 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.ImmutableMap;
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 @@ public class CcToolchainFeatures implements Serializable {
/**
* 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 @@ public class CcToolchainFeatures implements Serializable {
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 @@ public class CcToolchainFeatures implements Serializable {
/**
* 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 @@ public class CcToolchainFeatures implements Serializable {
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 @@ public class CcToolchainFeatures implements Serializable {
/**
* 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 @@ public class CcToolchainFeatures implements Serializable {
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 @@ public class CcToolchainFeatures implements Serializable {
}
}
}
+
+ /** 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 @@ public class CcToolchainFeatures implements Serializable {
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 @@ public class CcToolchainFeatures implements Serializable {
*
* @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 @@ public class CcToolchainFeatures implements Serializable {
selectablesByName.put(actionConfig.getName(), actionConfig);
actionConfigsByActionName.put(actionConfig.getActionName(), actionConfig);
}
-
+
this.selectables = selectablesBuilder.build();
this.selectablesByName = ImmutableMap.copyOf(selectablesByName);
@@ -1228,6 +1287,14 @@ public class CcToolchainFeatures implements Serializable {
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 @@ public class CcToolchainFeatures implements Serializable {
}
/**
+ * 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 4a20c22d82..b3367f4048 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 @@ public class CppHelper {
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 38aa8b9299..13866e81d5 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.ImmutableList;
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.analysis.config.BuildConfiguration;
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 @@ public final class CppModel {
}
/**
+ * 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 @@ public final class CppModel {
* 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 @@ public final class CppModel {
//
// 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 @@ public final class CppModel {
? 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 @@ public final class CppModel {
// 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 @@ public final class CppModel {
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 38ab7e8ccf..8826b0acb7 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 abstract class Link {
*/
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 @@ public abstract class Link {
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 bab618c2b0..0ce4984b8f 100644
--- a/src/main/protobuf/crosstool_config.proto
+++ b/src/main/protobuf/crosstool_config.proto
@@ -208,6 +208,20 @@ message CToolchain {
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 @@ message CToolchain {
// 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 @@ message CToolchain {
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 @@ message CToolchain {
// 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 85e6514a35..55f7f5f0e1 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 @@ java_library(
"//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 53e90d2a0c..a3c5af2bd7 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 abstract class MockCcSupport {
+ " }"
+ "}";
+ 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 45c7bd5a74..a374dad06e 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.assertFalse;
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 @@ public class CcLibraryConfiguredTargetTest extends BuildViewTestCase {
}
@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");