Introduce CppCompileActionTemplate, which expands into a list of CppCompileActions that to be executed at execution time.
--
PiperOrigin-RevId: 147163077
MOS_MIGRATED_REVID=147163077
diff --git a/src/main/java/com/google/devtools/build/lib/actions/Artifact.java b/src/main/java/com/google/devtools/build/lib/actions/Artifact.java
index 8072b3a..29b9902 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/Artifact.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/Artifact.java
@@ -508,6 +508,10 @@
"The parent of TreeFileArtifact (parent-relative path: %s) is not a TreeArtifact: %s",
parentRelativePath,
parent);
+ Preconditions.checkState(
+ parentRelativePath.isNormalized() && !parentRelativePath.isAbsolute(),
+ "%s is not a proper normalized relative path",
+ parentRelativePath);
this.parentTreeArtifact = parent;
this.parentRelativePath = parentRelativePath;
}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/actions/ActionTemplate.java b/src/main/java/com/google/devtools/build/lib/analysis/actions/ActionTemplate.java
new file mode 100644
index 0000000..afad7b7
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/analysis/actions/ActionTemplate.java
@@ -0,0 +1,75 @@
+// Copyright 2017 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.analysis.actions;
+
+import com.google.devtools.build.lib.actions.Action;
+import com.google.devtools.build.lib.actions.ActionAnalysisMetadata;
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.actions.Artifact.TreeFileArtifact;
+import com.google.devtools.build.lib.actions.ArtifactOwner;
+
+/**
+ * A placeholder action that, at execution time, expands into a list of {@link Action}s to be
+ * executed.
+ *
+ * <p>ActionTemplate is for users who want to dynamically register Actions operating on
+ * individual {@link TreeFileArtifact} inside input and output TreeArtifacts at execution time.
+ *
+ * <p>It takes in one TreeArtifact and generates one TreeArtifact. The following happens at
+ * execution time for ActionTemplate:
+ * <ol>
+ * <li>Input TreeArtifact is resolved.
+ * <li>For each individual {@link TreeFileArtifact} inside input TreeArtifact, generate an output
+ * {@link TreeFileArtifact} inside output TreeArtifact.
+ * <li>For each pair of input and output {@link TreeFileArtifact}s, generate an associated
+ * {@link Action}.
+ * <li>All expanded {@link Action}s are executed and their output {@link TreeFileArtifact}s
+ * collected.
+ * <li>Output TreeArtifact is resolved.
+ * </ol>
+ *
+ * <p>Implementations of ActionTemplate must follow the contract of this interface and also make
+ * sure:
+ * <ol>
+ * <li>ActionTemplate instances should be immutable and side-effect free.
+ * <li>ActionTemplate inputs and outputs are supersets of the inputs and outputs of expanded
+ * actions, excluding inputs discovered at execution time. This ensures the ActionTemplate
+ * can properly represent the expanded actions at analysis time, and the action graph
+ * at analysis time is correct. This is important because the action graph is walked in a lot
+ * of places for correctness checks and build analysis.
+ * <li>The outputs of expanded actions must be under the output TreeArtifact and must not have
+ * artifact or artifact path prefix conflicts.
+ * </ol>
+ */
+public interface ActionTemplate<T extends Action> extends ActionAnalysisMetadata {
+ /**
+ * Given a list of input TreeFileArtifacts resolved at execution time, returns a list of expanded
+ * SpawnActions to be executed.
+ *
+ * @param inputTreeFileArtifacts the list of {@link TreeFileArtifact}s inside input TreeArtifact
+ * resolved at execution time
+ * @param artifactOwner the {@link ArtifactOwner} of the generated output
+ * {@link TreeFileArtifact}s
+ * @return a list of expanded {@link Action}s to execute, one for each input
+ * {@link TreeFileArtifact}
+ */
+ Iterable<T> generateActionForInputArtifacts(
+ Iterable<TreeFileArtifact> inputTreeFileArtifacts, ArtifactOwner artifactOwner);
+
+ /** Returns the input TreeArtifact. */
+ Artifact getInputTreeArtifact();
+
+ /** Returns the output TreeArtifact. */
+ Artifact getOutputTreeArtifact();
+}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/actions/SpawnActionTemplate.java b/src/main/java/com/google/devtools/build/lib/analysis/actions/SpawnActionTemplate.java
index 4587e4a..6394c0c 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/actions/SpawnActionTemplate.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/actions/SpawnActionTemplate.java
@@ -18,12 +18,9 @@
import com.google.devtools.build.lib.actions.ActionAnalysisMetadata;
import com.google.devtools.build.lib.actions.ActionInputHelper;
import com.google.devtools.build.lib.actions.ActionOwner;
-import com.google.devtools.build.lib.actions.Actions;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.Artifact.TreeFileArtifact;
import com.google.devtools.build.lib.actions.ArtifactOwner;
-import com.google.devtools.build.lib.actions.ArtifactPrefixConflictException;
-import com.google.devtools.build.lib.actions.MutableActionGraph.ActionConflictException;
import com.google.devtools.build.lib.analysis.FilesToRunProvider;
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
@@ -32,27 +29,9 @@
import java.util.Map;
/**
- * A placeholder action that, at execution time, expands into a list of {@link SpawnAction}s that
- * will be executed.
- *
- * <p>SpawnActionTemplate is for users who want to dynamically register SpawnActions operating on
- * individual {@link TreeFileArtifact} inside input and output TreeArtifacts at execution time.
- *
- * <p>It takes in one TreeArtifact and generates one TreeArtifact. The following happens at
- * execution time for SpawnActionTemplate:
- * <ol>
- * <li>Input TreeArtifact is resolved.
- * <li>For each individual {@link TreeFileArtifact} inside input TreeArtifact, generate an output
- * {@link TreeFileArtifact} inside output TreeArtifact at the parent-relative path provided by
- * {@link OutputPathMapper}.
- * <li>For each pair of input and output {@link TreeFileArtifact}s, generate an associated
- * {@link SpawnAction}.
- * <li>All expanded {@link SpawnAction}s are executed and their output {@link TreeFileArtifact}s
- * collected.
- * <li>Output TreeArtifact is resolved.
- * </ol>
+ * An {@link ActionTemplate} that expands into {@link SpawnAction}s at execution time.
*/
-public final class SpawnActionTemplate implements ActionAnalysisMetadata {
+public final class SpawnActionTemplate implements ActionTemplate<SpawnAction> {
private final Artifact inputTreeArtifact;
private final Artifact outputTreeArtifact;
private final NestedSet<Artifact> commonInputs;
@@ -110,39 +89,23 @@
this.commandLineTemplate = commandLineTemplate;
}
- /**
- * Given a list of input TreeFileArtifacts resolved at execution time, returns a list of expanded
- * SpawnActions to be executed.
- *
- * @param inputTreeFileArtifacts the list of {@link TreeFileArtifact}s inside input TreeArtifact
- * resolved at execution time
- * @param artifactOwner the {@link ArtifactOwner} of the generated output
- * {@link TreeFileArtifact}s
- * @return a list of expanded {@link SpawnAction}s to execute, one for each input
- * {@link TreeFileArtifact}
- * @throws ActionConflictException if the expanded actions have duplicated outputs
- * @throws ArtifactPrefixConflictException if there is prefix conflict among the outputs of
- * expanded actions
- */
+ @Override
public Iterable<SpawnAction> generateActionForInputArtifacts(
- Iterable<TreeFileArtifact> inputTreeFileArtifacts, ArtifactOwner artifactOwner)
- throws ActionConflictException, ArtifactPrefixConflictException {
+ Iterable<TreeFileArtifact> inputTreeFileArtifacts, ArtifactOwner artifactOwner) {
ImmutableList.Builder<SpawnAction> expandedActions = new ImmutableList.Builder<>();
for (TreeFileArtifact inputTreeFileArtifact : inputTreeFileArtifacts) {
PathFragment parentRelativeOutputPath =
outputPathMapper.parentRelativeOutputPath(inputTreeFileArtifact);
- TreeFileArtifact outputTreeFileArtifact = createTreeFileArtifact(
+ TreeFileArtifact outputTreeFileArtifact = ActionInputHelper.treeFileArtifact(
outputTreeArtifact,
- checkOutputParentRelativePath(parentRelativeOutputPath),
+ parentRelativeOutputPath,
artifactOwner);
expandedActions.add(createAction(inputTreeFileArtifact, outputTreeFileArtifact));
}
- Iterable<SpawnAction> actions = expandedActions.build();
- checkActionAndArtifactConflicts(ImmutableList.<ActionAnalysisMetadata>copyOf(actions));
- return actions;
+ return expandedActions.build();
}
/**
@@ -171,37 +134,6 @@
/*paramFileWriteAction=*/ null);
}
- private static void checkActionAndArtifactConflicts(Iterable<ActionAnalysisMetadata> actions)
- throws ActionConflictException, ArtifactPrefixConflictException {
- Map<Artifact, ActionAnalysisMetadata> generatingActions =
- Actions.findAndThrowActionConflict(actions);
- Map<ActionAnalysisMetadata, ArtifactPrefixConflictException> artifactPrefixConflictMap =
- Actions.findArtifactPrefixConflicts(generatingActions);
-
- if (!artifactPrefixConflictMap.isEmpty()) {
- throw artifactPrefixConflictMap.values().iterator().next();
- }
-
- return;
- }
-
- private static PathFragment checkOutputParentRelativePath(PathFragment parentRelativeOutputPath) {
- Preconditions.checkArgument(
- parentRelativeOutputPath.isNormalized() && !parentRelativeOutputPath.isAbsolute(),
- "%s is not a proper relative path",
- parentRelativeOutputPath);
- return parentRelativeOutputPath;
- }
-
- private static TreeFileArtifact createTreeFileArtifact(Artifact parentTreeArtifact,
- PathFragment parentRelativeOutputPath, ArtifactOwner artifactOwner) {
- return ActionInputHelper.treeFileArtifact(
- parentTreeArtifact,
- parentRelativeOutputPath,
- artifactOwner);
- }
-
-
/**
* Returns the input TreeArtifact.
*
@@ -209,11 +141,13 @@
* TreeFileArtifacts. Skyframe then expands this SpawnActionTemplate with the TreeFileArtifacts
* through {@link #generateActionForInputArtifacts}.
*/
+ @Override
public Artifact getInputTreeArtifact() {
return inputTreeArtifact;
}
/** Returns the output TreeArtifact. */
+ @Override
public Artifact getOutputTreeArtifact() {
return outputTreeArtifact;
}
@@ -223,7 +157,6 @@
return actionOwner;
}
-
@Override
public final String getMnemonic() {
return mnemonic;
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationOutputs.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationOutputs.java
index f601913d..f70a4ed 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationOutputs.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationOutputs.java
@@ -200,7 +200,10 @@
* Adds an .o file.
*/
public Builder addObjectFile(Artifact artifact) {
- Preconditions.checkArgument(Link.OBJECT_FILETYPES.matches(artifact.getFilename()));
+ // We skip file extension checks for TreeArtifacts because they represent directory artifacts
+ // without a file extension.
+ Preconditions.checkArgument(
+ artifact.isTreeArtifact() || Link.OBJECT_FILETYPES.matches(artifact.getFilename()));
objectFiles.add(artifact);
return this;
}
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 355d01b..9d52957 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
@@ -451,13 +451,16 @@
* file type also to compilationUnitSources.
*/
private void addHeader(Artifact header, Label label) {
- boolean isHeader = CppFileTypes.CPP_HEADER.matches(header.getExecPath());
+ // We assume TreeArtifacts passed in are directories containing proper headers.
+ boolean isHeader =
+ CppFileTypes.CPP_HEADER.matches(header.getExecPath()) || header.isTreeArtifact();
boolean isTextualInclude = CppFileTypes.CPP_TEXTUAL_INCLUDE.matches(header.getExecPath());
publicHeaders.add(header);
if (isTextualInclude || !isHeader || !shouldProcessHeaders()) {
return;
}
- compilationUnitSources.add(CppSource.create(header, label, ImmutableMap.<String, String>of()));
+ compilationUnitSources.add(
+ CppSource.create(header, label, ImmutableMap.<String, String>of(), CppSource.Type.HEADER));
}
/** Adds a header to {@code publicHeaders}, but not to this target's module map. */
@@ -476,14 +479,26 @@
Preconditions.checkNotNull(featureConfiguration);
boolean isHeader = CppFileTypes.CPP_HEADER.matches(source.getExecPath());
boolean isTextualInclude = CppFileTypes.CPP_TEXTUAL_INCLUDE.matches(source.getExecPath());
- boolean isCompiledSource = sourceCategory.getSourceTypes().matches(source.getExecPathString());
+ // We assume TreeArtifacts passed in are directories containing proper sources for compilation.
+ boolean isCompiledSource =
+ sourceCategory.getSourceTypes().matches(source.getExecPathString())
+ || source.isTreeArtifact();
if (isHeader || isTextualInclude) {
privateHeaders.add(source);
}
if (isTextualInclude || !isCompiledSource || (isHeader && !shouldProcessHeaders())) {
return;
}
- compilationUnitSources.add(CppSource.create(source, label, buildVariables));
+ boolean isClifInputProto = CppFileTypes.CLIF_INPUT_PROTO.matches(source.getExecPathString());
+ CppSource.Type type;
+ if (isHeader) {
+ type = CppSource.Type.HEADER;
+ } else if (isClifInputProto) {
+ type = CppSource.Type.CLIF_INPUT_PROTO;
+ } else {
+ type = CppSource.Type.SOURCE;
+ }
+ compilationUnitSources.add(CppSource.create(source, label, buildVariables, type));
}
private boolean shouldProcessHeaders() {
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 f9295c6..3d03ece 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
@@ -1407,6 +1407,13 @@
private final Map<String, VariableValue> variablesMap = new LinkedHashMap<>();
private final Map<String, String> stringVariablesMap = new LinkedHashMap<>();
+ public Builder() {};
+
+ public Builder(Variables variables) {
+ variablesMap.putAll(variables.variablesMap);
+ stringVariablesMap.putAll(variables.stringVariablesMap);
+ }
+
/** Add a variable that expands {@code name} to {@code value}. */
public Builder addStringVariable(String name, String value) {
Preconditions.checkArgument(
@@ -1419,6 +1426,14 @@
return this;
}
+ /** Overrides a variable to expands {@code name} to {@code value} instead. */
+ public Builder overrideStringVariable(String name, String value) {
+ Preconditions.checkNotNull(
+ value, "Cannot set null as a value for variable '%s'", name);
+ stringVariablesMap.put(name, value);
+ return this;
+ }
+
/**
* Add a sequence variable that expands {@code name} to {@code values}.
*
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileAction.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileAction.java
index 6d73265..c51edff 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileAction.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileAction.java
@@ -36,7 +36,6 @@
import com.google.devtools.build.lib.actions.ResourceSet;
import com.google.devtools.build.lib.actions.extra.CppCompileInfo;
import com.google.devtools.build.lib.actions.extra.ExtraActionInfo;
-import com.google.devtools.build.lib.analysis.RuleContext;
import com.google.devtools.build.lib.analysis.config.PerLabelOptions;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.collect.CollectionUtils;
@@ -236,6 +235,7 @@
* Creates a new action to compile C/C++ source files.
*
* @param owner the owner of the action, usually the configured target that emitted it
+ * @param allInputs the list of all action inputs.
* @param features TODO(bazel-team): Add parameter description.
* @param featureConfiguration TODO(bazel-team): Add parameter description.
* @param variables TODO(bazel-team): Add parameter description.
@@ -266,14 +266,15 @@
* @param executionRequirements out-of-band hints to be passed to the execution backend to signal
* platform requirements
* @param environment TODO(bazel-team): Add parameter description
+ * @param builtinIncludeFiles List of include files that may be included even if they are not
+ * mentioned in the source file or any of the headers included by it
* @param actionName a string giving the name of this action for the purpose of toolchain
* evaluation
- * @param ruleContext The rule-context that produced this action
* @param cppSemantics C++ compilation semantics
- * @param ccToolchain C++ toolchain provider
*/
protected CppCompileAction(
ActionOwner owner,
+ NestedSet<Artifact> allInputs,
// TODO(bazel-team): Eventually we will remove 'features'; all functionality in 'features'
// will be provided by 'featureConfiguration'.
ImmutableList<String> features,
@@ -306,18 +307,11 @@
ImmutableMap<String, String> executionInfo,
ImmutableMap<String, String> environment,
String actionName,
- RuleContext ruleContext,
- CppSemantics cppSemantics,
- CcToolchainProvider ccToolchain) {
+ Iterable<Artifact> builtinIncludeFiles,
+ CppSemantics cppSemantics) {
super(
owner,
- createInputs(
- ruleContext,
- ccToolchain,
- mandatoryInputs,
- context.getTransitiveCompilationPrerequisites(),
- optionalSourceFile,
- lipoScannables),
+ allInputs,
CollectionUtils.asListWithoutNulls(
outputFile, (dotdFile == null ? null : dotdFile.artifact()), gcnoFile, dwoFile));
this.localShellEnvironment = localShellEnvironment;
@@ -351,81 +345,12 @@
// artifact and will definitely exist prior to this action execution.
this.mandatoryInputs = mandatoryInputs;
this.prunableInputs = prunableInputs;
- this.builtinIncludeFiles = ccToolchain.getBuiltinIncludeFiles();
+ this.builtinIncludeFiles = ImmutableList.copyOf(builtinIncludeFiles);
this.cppSemantics = cppSemantics;
- if (cppSemantics.needsIncludeValidation()) {
- verifyIncludePaths(ruleContext);
- }
this.additionalIncludeScannables = ImmutableList.copyOf(additionalIncludeScannables);
}
/**
- * Verifies that the include paths of this action are within the limits of the execution root.
- */
- private void verifyIncludePaths(RuleContext ruleContext) {
- if (ruleContext == null) {
- return;
- }
-
- Iterable<PathFragment> ignoredDirs = getValidationIgnoredDirs();
-
- // We currently do not check the output of:
- // - getQuoteIncludeDirs(): those only come from includes attributes, and are checked in
- // CcCommon.getIncludeDirsFromIncludesAttribute().
- // - getBuiltinIncludeDirs(): while in practice this doesn't happen, bazel can be configured
- // to use an absolute system root, in which case the builtin include dirs might be absolute.
- for (PathFragment include : Iterables.concat(getIncludeDirs(), getSystemIncludeDirs())) {
-
- // Ignore headers from built-in include directories.
- if (FileSystemUtils.startsWithAny(include, ignoredDirs)) {
- continue;
- }
-
- // One starting ../ is okay for getting to a sibling repository.
- if (include.startsWith(new PathFragment(Label.EXTERNAL_PATH_PREFIX))) {
- include = include.relativeTo(Label.EXTERNAL_PATH_PREFIX);
- }
-
- if (include.isAbsolute()
- || !PathFragment.EMPTY_FRAGMENT.getRelative(include).normalize().isNormalized()) {
- ruleContext.ruleError(
- "The include path '" + include + "' references a path outside of the execution root.");
- }
- }
- }
-
- private static NestedSet<Artifact> createInputs(
- RuleContext ruleContext,
- CcToolchainProvider ccToolchain,
- NestedSet<Artifact> mandatoryInputs,
- Set<Artifact> prerequisites,
- Artifact optionalSourceFile,
- Iterable<IncludeScannable> lipoScannables) {
- NestedSetBuilder<Artifact> builder = NestedSetBuilder.stableOrder();
- if (optionalSourceFile != null) {
- builder.add(optionalSourceFile);
- }
- builder.addAll(prerequisites);
- builder.addAll(ccToolchain.getBuiltinIncludeFiles());
- builder.addTransitive(mandatoryInputs);
- if (lipoScannables != null && lipoScannables.iterator().hasNext()) {
- // We need to add "legal generated scanner files" coming through LIPO scannables here. These
- // usually contain pre-grepped source files, i.e. files just containing the #include lines
- // extracted from generated files. With LIPO, some of these files can be accessed, even though
- // there is no direct dependency on them. Adding the artifacts as inputs to this compile
- // action ensures that the action generating them is actually executed.
- for (IncludeScannable lipoScannable : lipoScannables) {
- for (Artifact value : lipoScannable.getLegalGeneratedScannerFileMap().values()) {
- if (value != null) {
- builder.add(value);
- }
- }
- }
- }
- return builder.build();
- }
-
- /**
* Whether we should do "include scanning". Note that this does *not* mean whether we should parse
* the .d files to determine which include files were used during compilation. Instead, this means
* whether we should a) run the pre-execution include scanner (see {@code IncludeScanningContext})
@@ -959,7 +884,7 @@
errors.assertProblemFree(this, getSourceFile());
}
- private Iterable<PathFragment> getValidationIgnoredDirs() {
+ Iterable<PathFragment> getValidationIgnoredDirs() {
List<PathFragment> cxxSystemIncludeDirs = cppConfiguration.getBuiltInIncludeDirectories();
return Iterables.concat(
cxxSystemIncludeDirs, context.getSystemIncludeDirs());
@@ -1383,7 +1308,7 @@
CcToolchainFeatures.Variables variables,
String actionName) {
this.sourceFile = Preconditions.checkNotNull(sourceFile);
- this.dotdFile = CppFileTypes.mustProduceDotdFile(sourceFile.getPath().toString())
+ this.dotdFile = CppFileTypes.mustProduceDotdFile(sourceFile)
? Preconditions.checkNotNull(dotdFile) : null;
this.copts = Preconditions.checkNotNull(copts);
this.coptsFilter = coptsFilter;
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileActionBuilder.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileActionBuilder.java
index ee606bc..39e7ff9 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileActionBuilder.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileActionBuilder.java
@@ -22,7 +22,6 @@
import com.google.common.collect.Iterables;
import com.google.devtools.build.lib.actions.ActionOwner;
import com.google.devtools.build.lib.actions.Artifact;
-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;
import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
@@ -34,12 +33,14 @@
import com.google.devtools.build.lib.rules.cpp.CppCompileAction.SpecialInputsHandler;
import com.google.devtools.build.lib.util.FileType;
import com.google.devtools.build.lib.util.Preconditions;
+import com.google.devtools.build.lib.vfs.FileSystemUtils;
import com.google.devtools.build.lib.vfs.PathFragment;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.UUID;
import java.util.regex.Pattern;
@@ -53,7 +54,7 @@
private final List<String> features = new ArrayList<>();
private CcToolchainFeatures.FeatureConfiguration featureConfiguration;
private CcToolchainFeatures.Variables variables;
- private final Artifact sourceFile;
+ private Artifact sourceFile;
private final Label sourceLabel;
private final NestedSetBuilder<Artifact> mandatoryInputsBuilder;
private Artifact optionalSourceFile;
@@ -62,12 +63,10 @@
private PathFragment tempOutputFile;
private DotdFile dotdFile;
private Artifact gcnoFile;
- private final BuildConfiguration configuration;
private CppCompilationContext context = CppCompilationContext.EMPTY;
private final List<String> copts = new ArrayList<>();
private final List<String> pluginOpts = new ArrayList<>();
private final List<Pattern> nocopts = new ArrayList<>();
- private AnalysisEnvironment analysisEnvironment;
private ImmutableList<PathFragment> extraSystemIncludePrefixes = ImmutableList.of();
private boolean usePic;
private boolean allowUsingHeaderModules;
@@ -78,34 +77,50 @@
private ImmutableMap<Artifact, IncludeScannable> lipoScannableMap;
private final ImmutableList.Builder<Artifact> additionalIncludeFiles =
new ImmutableList.Builder<>();
- private RuleContext ruleContext = null;
private Boolean shouldScanIncludes;
private Map<String, String> executionInfo = new LinkedHashMap<>();
private Map<String, String> environment = new LinkedHashMap<>();
private CppSemantics cppSemantics;
private CcToolchainProvider ccToolchain;
+ private final ImmutableMap<String, String> localShellEnvironment;
+ private final boolean codeCoverageEnabled;
// New fields need to be added to the copy constructor.
/**
* Creates a builder from a rule. This also uses the configuration and
* artifact factory from the rule.
*/
- public CppCompileActionBuilder(RuleContext ruleContext, Artifact sourceFile, Label sourceLabel,
+ public CppCompileActionBuilder(RuleContext ruleContext, Label sourceLabel,
CcToolchainProvider ccToolchain) {
- this.owner = ruleContext.getActionOwner();
- this.actionContext = CppCompileActionContext.class;
- this.cppConfiguration = ruleContext.getFragment(CppConfiguration.class);
- this.analysisEnvironment = ruleContext.getAnalysisEnvironment();
- this.sourceFile = sourceFile;
- this.sourceLabel = sourceLabel;
- this.configuration = ruleContext.getConfiguration();
- this.mandatoryInputsBuilder = NestedSetBuilder.stableOrder();
- this.lipoScannableMap = getLipoScannableMap(ruleContext);
- this.ruleContext = ruleContext;
- this.allowUsingHeaderModules = true;
- this.ccToolchain = ccToolchain;
+ this(
+ ruleContext.getActionOwner(),
+ sourceLabel,
+ ruleContext.getFragment(CppConfiguration.class),
+ ruleContext.getConfiguration(),
+ getLipoScannableMap(ruleContext),
+ ruleContext.getFeatures(),
+ ccToolchain);
+ }
- features.addAll(ruleContext.getFeatures());
+ private CppCompileActionBuilder(
+ ActionOwner actionOwner,
+ Label sourceLabel,
+ CppConfiguration cppConfiguration,
+ BuildConfiguration configuration,
+ Map<Artifact, IncludeScannable> lipoScannableMap,
+ Set<String> features,
+ CcToolchainProvider ccToolchain) {
+ this.owner = actionOwner;
+ this.sourceLabel = sourceLabel;
+ this.cppConfiguration = cppConfiguration;
+ this.lipoScannableMap = ImmutableMap.copyOf(lipoScannableMap);
+ this.features.addAll(features);
+ this.mandatoryInputsBuilder = NestedSetBuilder.stableOrder();
+ this.allowUsingHeaderModules = true;
+ this.localShellEnvironment = configuration.getLocalShellEnvironment();
+ this.codeCoverageEnabled = configuration.isCodeCoverageEnabled();
+ this.actionContext = CppCompileActionContext.class;
+ this.ccToolchain = ccToolchain;
}
private static ImmutableMap<Artifact, IncludeScannable> getLipoScannableMap(
@@ -115,7 +130,7 @@
// contain headers, will still create CppCompileActions without providing a
// lipo_context_collector.
|| ruleContext.attributes().getAttributeDefinition(":lipo_context_collector") == null) {
- return null;
+ return ImmutableMap.<Artifact, IncludeScannable>of();
}
LipoContextProvider provider = ruleContext.getPrerequisite(
":lipo_context_collector", Mode.DONT_CHECK, LipoContextProvider.class);
@@ -129,6 +144,7 @@
this.owner = other.owner;
this.features.addAll(other.features);
this.featureConfiguration = other.featureConfiguration;
+ this.variables = other.variables;
this.sourceFile = other.sourceFile;
this.sourceLabel = other.sourceLabel;
this.mandatoryInputsBuilder = NestedSetBuilder.<Artifact>stableOrder()
@@ -139,12 +155,10 @@
this.tempOutputFile = other.tempOutputFile;
this.dotdFile = other.dotdFile;
this.gcnoFile = other.gcnoFile;
- this.configuration = other.configuration;
this.context = other.context;
this.copts.addAll(other.copts);
this.pluginOpts.addAll(other.pluginOpts);
this.nocopts.addAll(other.nocopts);
- this.analysisEnvironment = other.analysisEnvironment;
this.extraSystemIncludePrefixes = ImmutableList.copyOf(other.extraSystemIncludePrefixes);
this.specialInputsHandler = other.specialInputsHandler;
this.actionClassId = other.actionClassId;
@@ -153,10 +167,11 @@
this.usePic = other.usePic;
this.allowUsingHeaderModules = other.allowUsingHeaderModules;
this.lipoScannableMap = other.lipoScannableMap;
- this.ruleContext = other.ruleContext;
this.shouldScanIncludes = other.shouldScanIncludes;
this.executionInfo = new LinkedHashMap<>(other.executionInfo);
this.environment = new LinkedHashMap<>(other.environment);
+ this.localShellEnvironment = other.localShellEnvironment;
+ this.codeCoverageEnabled = other.codeCoverageEnabled;
this.cppSemantics = other.cppSemantics;
this.ccToolchain = other.ccToolchain;
}
@@ -165,6 +180,11 @@
return tempOutputFile;
}
+ public CppCompileActionBuilder setSourceFile(Artifact sourceFile) {
+ this.sourceFile = sourceFile;
+ return this;
+ }
+
public Artifact getSourceFile() {
return sourceFile;
}
@@ -198,16 +218,21 @@
}
private Iterable<IncludeScannable> getLipoScannables(NestedSet<Artifact> realMandatoryInputs) {
- return lipoScannableMap == null ? ImmutableList.<IncludeScannable>of() : Iterables.filter(
- Iterables.transform(
- Iterables.filter(
- FileType.filter(
- realMandatoryInputs,
- CppFileTypes.C_SOURCE, CppFileTypes.CPP_SOURCE,
- CppFileTypes.ASSEMBLER_WITH_C_PREPROCESSOR),
- Predicates.not(Predicates.equalTo(getSourceFile()))),
- Functions.forMap(lipoScannableMap, null)),
- Predicates.notNull());
+ boolean fake = tempOutputFile != null;
+
+ return lipoScannableMap == null || fake
+ ? ImmutableList.<IncludeScannable>of()
+ : Iterables.filter(
+ Iterables.transform(
+ Iterables.filter(
+ FileType.filter(
+ realMandatoryInputs,
+ CppFileTypes.C_SOURCE,
+ CppFileTypes.CPP_SOURCE,
+ CppFileTypes.ASSEMBLER_WITH_C_PREPROCESSOR),
+ Predicates.not(Predicates.equalTo(getSourceFile()))),
+ Functions.forMap(lipoScannableMap, null)),
+ Predicates.notNull());
}
private String getActionName() {
@@ -248,7 +273,23 @@
}
/**
- * Builds the Action as configured and returns the to be generated Artifact.
+ * Builds the action and performs some validations on the action.
+ *
+ * <p>This method may be called multiple times to create multiple compile
+ * actions (usually after calling some setters to modify the generated
+ * action).
+ */
+ public CppCompileAction buildAndValidate(RuleContext ruleContext) {
+ CppCompileAction action = build();
+ if (cppSemantics.needsIncludeValidation()) {
+ verifyActionIncludePaths(action, ruleContext);
+ }
+ return action;
+ }
+
+ /**
+ * Builds the Action as configured without validations. Users may want to call
+ * {@link #buildAndValidate} instead.
*
* <p>This method may be called multiple times to create multiple compile
* actions (usually after calling some setters to modify the generated
@@ -262,19 +303,6 @@
allowUsingHeaderModules
&& featureConfiguration.isEnabled(CppRuleClasses.USE_HEADER_MODULES);
- boolean fake = tempOutputFile != null;
- // Configuration can be null in tests.
- NestedSetBuilder<Artifact> realMandatoryInputsBuilder = NestedSetBuilder.compileOrder();
- realMandatoryInputsBuilder.addTransitive(mandatoryInputsBuilder.build());
- boolean shouldPruneModules =
- cppConfiguration.getPruneCppModules() && shouldScanIncludes && useHeaderModules;
- if (useHeaderModules && !shouldPruneModules) {
- realMandatoryInputsBuilder.addTransitive(context.getTransitiveModules(usePic));
- }
- realMandatoryInputsBuilder.addTransitive(context.getAdditionalInputs());
-
- realMandatoryInputsBuilder.add(sourceFile);
-
// If the crosstool uses action_configs to configure cc compilation, collect execution info
// from there, otherwise, use no execution info.
// TODO(b/27903698): Assert that the crosstool has an action_config for this action.
@@ -285,7 +313,8 @@
}
}
- NestedSet<Artifact> realMandatoryInputs = realMandatoryInputsBuilder.build();
+ NestedSet<Artifact> realMandatoryInputs = buildMandatoryInputs();
+ NestedSet<Artifact> allInputs = buildAllInputs();
NestedSetBuilder<Artifact> prunableInputBuilder = NestedSetBuilder.stableOrder();
prunableInputBuilder.addTransitive(context.getDeclaredIncludeSrcs());
@@ -294,15 +323,17 @@
NestedSet<Artifact> prunableInputs = prunableInputBuilder.build();
// Copying the collections is needed to make the builder reusable.
+ boolean fake = tempOutputFile != null;
if (fake) {
return new FakeCppCompileAction(
owner,
+ allInputs,
ImmutableList.copyOf(features),
featureConfiguration,
variables,
sourceFile,
shouldScanIncludes,
- shouldPruneModules,
+ shouldPruneModules(),
usePic,
useHeaderModules,
sourceLabel,
@@ -311,25 +342,27 @@
outputFile,
tempOutputFile,
dotdFile,
- configuration,
+ localShellEnvironment,
+ codeCoverageEnabled,
cppConfiguration,
context,
actionContext,
ImmutableList.copyOf(copts),
getNocoptPredicate(nocopts),
- ruleContext,
+ getLipoScannables(realMandatoryInputs),
+ ccToolchain.getBuiltinIncludeFiles(),
cppSemantics,
- ccToolchain,
ImmutableMap.copyOf(executionInfo));
} else {
return new CppCompileAction(
owner,
+ allInputs,
ImmutableList.copyOf(features),
featureConfiguration,
variables,
sourceFile,
shouldScanIncludes,
- shouldPruneModules,
+ shouldPruneModules(),
usePic,
useHeaderModules,
sourceLabel,
@@ -340,8 +373,8 @@
gcnoFile,
dwoFile,
optionalSourceFile,
- configuration.getLocalShellEnvironment(),
- configuration.isCodeCoverageEnabled(),
+ localShellEnvironment,
+ codeCoverageEnabled,
cppConfiguration,
context,
actionContext,
@@ -354,9 +387,84 @@
ImmutableMap.copyOf(executionInfo),
ImmutableMap.copyOf(environment),
getActionName(),
- ruleContext,
- cppSemantics,
- ccToolchain);
+ ccToolchain.getBuiltinIncludeFiles(),
+ cppSemantics);
+ }
+ }
+
+ /**
+ * Returns the list of mandatory inputs for the {@link CppCompileAction} as configured.
+ */
+ NestedSet<Artifact> buildMandatoryInputs() {
+ NestedSetBuilder<Artifact> realMandatoryInputsBuilder = NestedSetBuilder.compileOrder();
+ realMandatoryInputsBuilder.addTransitive(mandatoryInputsBuilder.build());
+ if (useHeaderModules() && !shouldPruneModules()) {
+ realMandatoryInputsBuilder.addTransitive(context.getTransitiveModules(usePic));
+ }
+ realMandatoryInputsBuilder.addTransitive(context.getAdditionalInputs());
+ realMandatoryInputsBuilder.add(Preconditions.checkNotNull(sourceFile));
+ return realMandatoryInputsBuilder.build();
+ }
+
+ /**
+ * Returns the list of all inputs for the {@link CppCompileAction} as configured.
+ */
+ NestedSet<Artifact> buildAllInputs() {
+ NestedSet<Artifact> mandatoryInputs = buildMandatoryInputs();
+ NestedSetBuilder<Artifact> builder = NestedSetBuilder.stableOrder();
+ if (optionalSourceFile != null) {
+ builder.add(optionalSourceFile);
+ }
+ builder.addAll(context.getTransitiveCompilationPrerequisites());
+ builder.addAll(ccToolchain.getBuiltinIncludeFiles());
+ builder.addTransitive(mandatoryInputs);
+ Iterable<IncludeScannable> lipoScannables = getLipoScannables(mandatoryInputs);
+ // We need to add "legal generated scanner files" coming through LIPO scannables here. These
+ // usually contain pre-grepped source files, i.e. files just containing the #include lines
+ // extracted from generated files. With LIPO, some of these files can be accessed, even though
+ // there is no direct dependency on them. Adding the artifacts as inputs to this compile
+ // action ensures that the action generating them is actually executed.
+ for (IncludeScannable lipoScannable : lipoScannables) {
+ for (Artifact value : lipoScannable.getLegalGeneratedScannerFileMap().values()) {
+ if (value != null) {
+ builder.add(value);
+ }
+ }
+ }
+ return builder.build();
+ }
+
+ private boolean useHeaderModules() {
+ return allowUsingHeaderModules
+ && featureConfiguration.isEnabled(CppRuleClasses.USE_HEADER_MODULES);
+ }
+
+ private boolean shouldPruneModules() {
+ return cppConfiguration.getPruneCppModules() && shouldScanIncludes && useHeaderModules();
+ }
+
+ private static void verifyActionIncludePaths(CppCompileAction action, RuleContext ruleContext) {
+ Iterable<PathFragment> ignoredDirs = action.getValidationIgnoredDirs();
+ // We currently do not check the output of:
+ // - getQuoteIncludeDirs(): those only come from includes attributes, and are checked in
+ // CcCommon.getIncludeDirsFromIncludesAttribute().
+ // - getBuiltinIncludeDirs(): while in practice this doesn't happen, bazel can be configured
+ // to use an absolute system root, in which case the builtin include dirs might be absolute.
+ for (PathFragment include :
+ Iterables.concat(action.getIncludeDirs(), action.getSystemIncludeDirs())) {
+ // Ignore headers from built-in include directories.
+ if (FileSystemUtils.startsWithAny(include, ignoredDirs)) {
+ continue;
+ }
+ // One starting ../ is okay for getting to a sibling repository.
+ if (include.startsWith(new PathFragment(Label.EXTERNAL_PATH_PREFIX))) {
+ include = include.relativeTo(Label.EXTERNAL_PATH_PREFIX);
+ }
+ if (include.isAbsolute()
+ || !PathFragment.EMPTY_FRAGMENT.getRelative(include).normalize().isNormalized()) {
+ ruleContext.ruleError(
+ "The include path '" + include + "' references a path outside of the execution root.");
+ }
}
}
@@ -377,6 +485,13 @@
return this;
}
+ /**
+ * Returns the build variables to be used for the action.
+ */
+ CcToolchainFeatures.Variables getVariables() {
+ return variables;
+ }
+
public CppCompileActionBuilder addEnvironment(Map<String, String> environment) {
this.environment.putAll(environment);
return this;
@@ -435,22 +550,24 @@
return this;
}
- public CppCompileActionBuilder setOutputsForTesting(Artifact outputFile, Artifact dotdFile) {
+ public CppCompileActionBuilder setOutputs(Artifact outputFile, Artifact dotdFile) {
this.outputFile = outputFile;
this.dotdFile = dotdFile == null ? null : new DotdFile(dotdFile);
return this;
}
public CppCompileActionBuilder setOutputs(
- ArtifactCategory outputCategory, String outputName, boolean generateDotd) {
+ RuleContext ruleContext, ArtifactCategory outputCategory, String outputName,
+ boolean generateDotd) {
this.outputFile = CppHelper.getCompileOutputArtifact(
ruleContext,
CppHelper.getArtifactNameForCategory(ruleContext, ccToolchain, outputCategory, outputName));
if (generateDotd) {
String dotdFileName =
CppHelper.getDotdFileName(ruleContext, ccToolchain, outputCategory, outputName);
- if (configuration.getFragment(CppConfiguration.class).getInmemoryDotdFiles()) {
+ if (cppConfiguration.getInmemoryDotdFiles()) {
// Just set the path, no artifact is constructed
+ BuildConfiguration configuration = ruleContext.getConfiguration();
dotdFile = new DotdFile(
configuration.getBinDirectory(ruleContext.getRule().getRepository()).getExecPath()
.getRelative(CppHelper.getObjDirectory(ruleContext.getLabel()))
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileActionTemplate.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileActionTemplate.java
new file mode 100644
index 0000000..21abea8
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileActionTemplate.java
@@ -0,0 +1,200 @@
+// Copyright 2017 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.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.devtools.build.lib.actions.ActionAnalysisMetadata;
+import com.google.devtools.build.lib.actions.ActionAnalysisMetadata.MiddlemanType;
+import com.google.devtools.build.lib.actions.ActionInputHelper;
+import com.google.devtools.build.lib.actions.ActionOwner;
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.actions.Artifact.TreeFileArtifact;
+import com.google.devtools.build.lib.actions.ArtifactOwner;
+import com.google.devtools.build.lib.analysis.actions.ActionTemplate;
+import com.google.devtools.build.lib.collect.nestedset.NestedSet;
+import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
+import com.google.devtools.build.lib.vfs.FileSystemUtils;
+import com.google.devtools.build.lib.vfs.PathFragment;
+
+/**
+ * An {@link ActionTemplate} that expands into {@link CppCompileAction}s at execution time.
+ */
+public final class CppCompileActionTemplate implements ActionTemplate<CppCompileAction> {
+ private final CppCompileActionBuilder cppCompileActionBuilder;
+ private final Artifact sourceTreeArtifact;
+ private final Artifact outputTreeArtifact;
+ private final CppConfiguration cppConfiguration;
+ private final Iterable<ArtifactCategory> categories;
+ private final ActionOwner actionOwner;
+ private final NestedSet<Artifact> mandatoryInputs;
+ private final NestedSet<Artifact> allInputs;
+
+ /**
+ * Creates an CppCompileActionTemplate.
+ * @param sourceTreeArtifact the TreeArtifact that contains source files to compile.
+ * @param outputTreeArtifact the TreeArtifact that contains compilation outputs.
+ * @param cppCompileActionBuilder An almost completely configured {@link CppCompileActionBuilder}
+ * without the input and output files set. It is used as a template to instantiate expanded
+ * {CppCompileAction}s.
+ * @param cppConfiguration configuration for cpp.
+ * @param categories A list of {@link ArtifactCategory} used to calculate output file name from
+ * a source file name.
+ * @param actionOwner the owner of this {@link ActionTemplate}.
+ */
+ CppCompileActionTemplate(
+ Artifact sourceTreeArtifact,
+ Artifact outputTreeArtifact,
+ CppCompileActionBuilder cppCompileActionBuilder,
+ CppConfiguration cppConfiguration,
+ Iterable<ArtifactCategory> categories,
+ ActionOwner actionOwner) {
+ this.cppCompileActionBuilder = cppCompileActionBuilder;
+ this.sourceTreeArtifact = sourceTreeArtifact;
+ this.outputTreeArtifact = outputTreeArtifact;
+ this.cppConfiguration = cppConfiguration;
+ this.categories = categories;
+ this.actionOwner = actionOwner;
+ this.mandatoryInputs = cppCompileActionBuilder.buildMandatoryInputs();
+ this.allInputs = cppCompileActionBuilder.buildAllInputs();
+ }
+
+ @Override
+ public Iterable<CppCompileAction> generateActionForInputArtifacts(
+ Iterable<TreeFileArtifact> inputTreeFileArtifacts, ArtifactOwner artifactOwner) {
+ ImmutableList.Builder<CppCompileAction> expandedActions = new ImmutableList.Builder<>();
+ for (TreeFileArtifact inputTreeFileArtifact : inputTreeFileArtifacts) {
+ String outputName = outputTreeFileArtifactName(inputTreeFileArtifact);
+ TreeFileArtifact outputTreeFileArtifact = ActionInputHelper.treeFileArtifact(
+ outputTreeArtifact,
+ new PathFragment(outputName),
+ artifactOwner);
+
+ expandedActions.add(createAction(inputTreeFileArtifact, outputTreeFileArtifact));
+ }
+
+ return expandedActions.build();
+ }
+
+ private CppCompileAction createAction(
+ Artifact sourceTreeFileArtifact, Artifact outputTreeFileArtifact) {
+ CppCompileActionBuilder builder = new CppCompileActionBuilder(cppCompileActionBuilder);
+ builder.setSourceFile(sourceTreeFileArtifact);
+ builder.setOutputs(outputTreeFileArtifact, null);
+
+ CcToolchainFeatures.Variables.Builder buildVariables =
+ new CcToolchainFeatures.Variables.Builder(builder.getVariables());
+ buildVariables.overrideStringVariable(
+ "source_file", sourceTreeFileArtifact.getExecPathString());
+ buildVariables.overrideStringVariable(
+ "output_file", outputTreeFileArtifact.getExecPathString());
+ buildVariables.overrideStringVariable(
+ "output_object_file", outputTreeFileArtifact.getExecPathString());
+
+ builder.setVariables(buildVariables.build());
+
+ return builder.build();
+ }
+
+ private String outputTreeFileArtifactName(TreeFileArtifact inputTreeFileArtifact) {
+ String outputName = FileSystemUtils.removeExtension(
+ inputTreeFileArtifact.getParentRelativePath().getPathString());
+ for (ArtifactCategory category : categories) {
+ outputName = cppConfiguration.getFeatures().getArtifactNameForCategory(category, outputName);
+ }
+ return outputName;
+ }
+
+ @Override
+ public Artifact getInputTreeArtifact() {
+ return sourceTreeArtifact;
+ }
+
+ @Override
+ public Artifact getOutputTreeArtifact() {
+ return outputTreeArtifact;
+ }
+
+ @Override
+ public ActionOwner getOwner() {
+ return actionOwner;
+ }
+
+ @Override
+ public final String getMnemonic() {
+ return "CppCompileActionTemplate";
+ }
+
+ @Override
+ public Iterable<Artifact> getMandatoryInputs() {
+ return NestedSetBuilder.<Artifact>compileOrder()
+ .add(sourceTreeArtifact)
+ .addTransitive(mandatoryInputs)
+ .build();
+ }
+
+ @Override
+ public ImmutableSet<Artifact> getMandatoryOutputs() {
+ return ImmutableSet.<Artifact>of();
+ }
+
+ @Override
+ public Iterable<Artifact> getTools() {
+ return ImmutableList.<Artifact>of();
+ }
+
+ @Override
+ public Iterable<Artifact> getInputs() {
+ return NestedSetBuilder.<Artifact>stableOrder()
+ .add(sourceTreeArtifact)
+ .addTransitive(allInputs)
+ .build();
+ }
+
+ @Override
+ public ImmutableSet<Artifact> getOutputs() {
+ return ImmutableSet.of(outputTreeArtifact);
+ }
+
+ @Override
+ public Iterable<String> getClientEnvironmentVariables() {
+ return ImmutableList.<String>of();
+ }
+
+ @Override
+ public Artifact getPrimaryInput() {
+ return sourceTreeArtifact;
+ }
+
+ @Override
+ public Artifact getPrimaryOutput() {
+ return outputTreeArtifact;
+ }
+
+ @Override
+ public boolean shouldReportPathPrefixConflict(ActionAnalysisMetadata action) {
+ return this != action;
+ }
+
+ @Override
+ public MiddlemanType getActionType() {
+ return MiddlemanType.NORMAL;
+ }
+
+ @Override
+ public String prettyPrint() {
+ return String.format(
+ "CppCompileActionTemplate compiling " + sourceTreeArtifact.getExecPathString());
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppFileTypes.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppFileTypes.java
index 2ad342d..dc17485 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppFileTypes.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppFileTypes.java
@@ -183,10 +183,16 @@
}
};
- public static final boolean mustProduceDotdFile(String source) {
- return !ASSEMBLER.matches(source)
- && !PIC_ASSEMBLER.matches(source)
- && !CLIF_INPUT_PROTO.matches(source);
+ public static final boolean mustProduceDotdFile(Artifact source) {
+ // Sources from TreeArtifacts and TreeFileArtifacts will not generate dotd file.
+ if (source.isTreeArtifact() || source.hasParent()) {
+ return false;
+ }
+
+ String fileName = source.getFilename();
+ return !ASSEMBLER.matches(fileName)
+ && !PIC_ASSEMBLER.matches(fileName)
+ && !CLIF_INPUT_PROTO.matches(fileName);
}
}
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 e4b19dc..5228588 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
@@ -584,9 +584,19 @@
ruleContext.getConfiguration().getBinDirectory(ruleContext.getRule().getRepository()));
}
- static String getArtifactNameForCategory(
- RuleContext ruleContext, CcToolchainProvider toolchain, ArtifactCategory category,
- String outputName) {
+ /**
+ * Returns the corresponding compiled TreeArtifact given the source TreeArtifact.
+ */
+ public static Artifact getCompileOutputTreeArtifact(
+ RuleContext ruleContext, Artifact sourceTreeArtifact) {
+ PathFragment objectDir = getObjDirectory(ruleContext.getLabel());
+ PathFragment rootRelativePath = sourceTreeArtifact.getRootRelativePath();
+ return ruleContext.getTreeArtifact(
+ objectDir.getRelative(rootRelativePath), sourceTreeArtifact.getRoot());
+ }
+
+ static String getArtifactNameForCategory(RuleContext ruleContext, CcToolchainProvider toolchain,
+ ArtifactCategory category, String outputName) {
return toolchain.getFeatures().getArtifactNameForCategory(category, outputName);
}
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 b22e989..ffe5790 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
@@ -954,8 +954,11 @@
}
private void addObjectFile(LinkerInput input) {
+ // We skip file extension checks for TreeArtifacts because they represent directory artifacts
+ // without a file extension.
String name = input.getArtifact().getFilename();
- Preconditions.checkArgument(Link.OBJECT_FILETYPES.matches(name), name);
+ Preconditions.checkArgument(
+ input.getArtifact().isTreeArtifact() || Link.OBJECT_FILETYPES.matches(name), name);
this.objectFiles.add(input);
}
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 1e18629..7960bac 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
@@ -144,9 +144,10 @@
* units, including module files or headers to be parsed or preprocessed.
*/
public CppModel addCompilationUnitSources(
- Iterable<Artifact> sourceFiles, Label sourceLabel, Map<String, String> buildVariables) {
+ Iterable<Artifact> sourceFiles, Label sourceLabel, Map<String, String> buildVariables,
+ CppSource.Type type) {
for (Artifact sourceFile : sourceFiles) {
- this.sourceFiles.add(CppSource.create(sourceFile, sourceLabel, buildVariables));
+ this.sourceFiles.add(CppSource.create(sourceFile, sourceLabel, buildVariables, type));
}
return this;
}
@@ -380,7 +381,7 @@
buildVariables.addStringVariable("output_object_file", realOutputFilePath);
}
- DotdFile dotdFile = CppFileTypes.mustProduceDotdFile(sourceFile.getPath().toString())
+ DotdFile dotdFile = CppFileTypes.mustProduceDotdFile(sourceFile)
? Preconditions.checkNotNull(builder.getDotdFile()) : null;
// Set dependency_file to enable <object>.d file generation.
if (dotdFile != null) {
@@ -485,33 +486,61 @@
CppCompileActionBuilder builder = initializeCompileAction(sourceArtifact, sourceLabel);
builder.setSemantics(semantics);
-
- if (CppFileTypes.CPP_HEADER.matches(source.getSource().getExecPath())) {
- createHeaderAction(outputName, result, env, builder,
- CppFileTypes.mustProduceDotdFile(sourceArtifact.getFilename()));
- } else if (CppFileTypes.CLIF_INPUT_PROTO.matches(source.getSource().getExecPath())) {
- createClifMatchAction(outputName, result, env, builder);
+
+ if (!sourceArtifact.isTreeArtifact()) {
+ switch (source.getType()) {
+ case HEADER:
+ createHeaderAction(outputName, result, env, builder,
+ CppFileTypes.mustProduceDotdFile(sourceArtifact));
+ break;
+ case CLIF_INPUT_PROTO:
+ createClifMatchAction(outputName, result, env, builder);
+ break;
+ default:
+ boolean bitcodeOutput =
+ featureConfiguration.isEnabled(CppRuleClasses.THIN_LTO)
+ && CppFileTypes.LTO_SOURCE.matches(sourceArtifact.getFilename());
+ createSourceAction(
+ outputName,
+ result,
+ env,
+ sourceArtifact,
+ builder,
+ ArtifactCategory.OBJECT_FILE,
+ context.getCppModuleMap(),
+ /*addObject=*/ true,
+ isCodeCoverageEnabled(),
+ // The source action does not generate dwo when it has bitcode
+ // output (since it isn't generating a native object with debug
+ // info). In that case the LTOBackendAction will generate the dwo.
+ /*generateDwo=*/ cppConfiguration.useFission() && !bitcodeOutput,
+ CppFileTypes.mustProduceDotdFile(sourceArtifact),
+ source.getBuildVariables(), /*addHeaderTokenFile=*/
+ false);
+ break;
+ }
} else {
- boolean bitcodeOutput =
- featureConfiguration.isEnabled(CppRuleClasses.THIN_LTO)
- && CppFileTypes.LTO_SOURCE.matches(sourceArtifact.getFilename());
- createSourceAction(
- outputName,
- result,
- env,
- sourceArtifact,
- builder,
- ArtifactCategory.OBJECT_FILE,
- context.getCppModuleMap(),
- /*addObject=*/ true,
- isCodeCoverageEnabled(),
- // The source action does not generate dwo when it has bitcode
- // output (since it isn't generating a native object with debug
- // info). In that case the LTOBackendAction will generate the dwo.
- /*generateDwo=*/ cppConfiguration.useFission() && !bitcodeOutput,
- CppFileTypes.mustProduceDotdFile(sourceArtifact.getFilename()),
- source.getBuildVariables(), /*addHeaderTokenFile=*/
- false);
+ switch (source.getType()) {
+ case HEADER:
+ Artifact headerTokenFile =
+ createCompileActionTemplate(
+ env,
+ source,
+ builder,
+ ImmutableList.of(
+ ArtifactCategory.GENERATED_HEADER, ArtifactCategory.PROCESSED_HEADER));
+ result.addHeaderTokenFile(headerTokenFile);
+ break;
+ case SOURCE:
+ Artifact objectFile =
+ createCompileActionTemplate(
+ env, source, builder, ImmutableList.of(ArtifactCategory.OBJECT_FILE));
+ result.addObjectFile(objectFile);
+ break;
+ default:
+ throw new IllegalStateException(
+ "Encountered invalid source types when creating CppCompileActionTemplates");
+ }
}
}
@@ -525,7 +554,7 @@
ruleContext, ccToolchain, ArtifactCategory.GENERATED_HEADER, outputName);
builder
- .setOutputs(ArtifactCategory.PROCESSED_HEADER, outputNameBase, generateDotd)
+ .setOutputs(ruleContext, ArtifactCategory.PROCESSED_HEADER, outputNameBase, generateDotd)
// If we generate pic actions, we prefer the header actions to use the pic artifacts.
.setPicMode(this.getGeneratePicActions());
setupCompileBuildVariables(
@@ -538,7 +567,7 @@
builder.getContext().getCppModuleMap(),
ImmutableMap.<String, String>of());
semantics.finalizeCompileActionBuilder(ruleContext, builder);
- CppCompileAction compileAction = builder.build();
+ CppCompileAction compileAction = builder.buildAndValidate(ruleContext);
env.registerAction(compileAction);
Artifact tokenFile = compileAction.getOutputFile();
result.addHeaderTokenFile(tokenFile);
@@ -568,7 +597,7 @@
/*addObject=*/ false,
/*enableCoverage=*/ false,
/*generateDwo=*/ false,
- CppFileTypes.mustProduceDotdFile(moduleMapArtifact.getFilename()),
+ CppFileTypes.mustProduceDotdFile(moduleMapArtifact),
ImmutableMap.<String, String>of(), /*addHeaderTokenFile=*/
addHeaderTokenFiles);
}
@@ -576,7 +605,7 @@
private void createClifMatchAction(
String outputName, Builder result, AnalysisEnvironment env, CppCompileActionBuilder builder) {
builder
- .setOutputs(ArtifactCategory.CLIF_OUTPUT_PROTO, outputName, false)
+ .setOutputs(ruleContext, ArtifactCategory.CLIF_OUTPUT_PROTO, outputName, false)
.setPicMode(false)
// The additional headers in a clif action are both mandatory inputs and
// need to be include-scanned.
@@ -592,7 +621,7 @@
builder.getContext().getCppModuleMap(),
/*sourceSpecificBuildVariables=*/ ImmutableMap.<String, String>of());
semantics.finalizeCompileActionBuilder(ruleContext, builder);
- CppCompileAction compileAction = builder.build();
+ CppCompileAction compileAction = builder.buildAndValidate(ruleContext);
env.registerAction(compileAction);
Artifact tokenFile = compileAction.getOutputFile();
result.addHeaderTokenFile(tokenFile);
@@ -663,7 +692,7 @@
picBuilder.setDwoFile(dwoFile);
semantics.finalizeCompileActionBuilder(ruleContext, picBuilder);
- CppCompileAction picAction = picBuilder.build();
+ CppCompileAction picAction = picBuilder.buildAndValidate(ruleContext);
env.registerAction(picAction);
if (addHeaderTokenFiles) {
result.addHeaderTokenFile(picAction.getOutputFile());
@@ -690,7 +719,7 @@
ruleContext,
CppHelper.getArtifactNameForCategory(
ruleContext, ccToolchain, outputCategory, outputName));
- builder.setOutputs(outputCategory, outputName, generateDotd);
+ builder.setOutputs(ruleContext, outputCategory, outputName, generateDotd);
String gcnoFileName = CppHelper.getArtifactNameForCategory(ruleContext,
ccToolchain, ArtifactCategory.COVERAGE_DATA_FILE, outputName);
@@ -727,7 +756,7 @@
builder.setDwoFile(noPicDwoFile);
semantics.finalizeCompileActionBuilder(ruleContext, builder);
- CppCompileAction compileAction = builder.build();
+ CppCompileAction compileAction = builder.buildAndValidate(ruleContext);
env.registerAction(compileAction);
Artifact objectFile = compileAction.getOutputFile();
if (addHeaderTokenFiles) {
@@ -751,6 +780,35 @@
}
}
+ private Artifact createCompileActionTemplate(AnalysisEnvironment env,
+ CppSource source, CppCompileActionBuilder builder,
+ Iterable<ArtifactCategory> outputCategories) {
+ Artifact sourceArtifact = source.getSource();
+ Artifact outputFiles = CppHelper.getCompileOutputTreeArtifact(ruleContext, sourceArtifact);
+ // TODO(rduan): Dotd file output is not supported yet.
+ builder.setOutputs(outputFiles, null);
+ setupCompileBuildVariables(
+ builder,
+ /* usePic=*/ false,
+ /*ccRelativeName=*/ null,
+ /*autoFdoImportPath=*/ null,
+ /*gcnoFile=*/ null,
+ /*dwoFile=*/ null,
+ builder.getContext().getCppModuleMap(),
+ source.getBuildVariables());
+ semantics.finalizeCompileActionBuilder(ruleContext, builder);
+ CppCompileActionTemplate actionTemplate = new CppCompileActionTemplate(
+ sourceArtifact,
+ outputFiles,
+ builder,
+ cppConfiguration,
+ outputCategories,
+ ruleContext.getActionOwner());
+ env.registerAction(actionTemplate);
+
+ return outputFiles;
+ }
+
String getOutputNameBaseWith(String base, boolean usePic) {
return usePic
? CppHelper.getArtifactNameForCategory(
@@ -771,7 +829,7 @@
.getPathString();
builder
.setPicMode(usePic)
- .setOutputs(outputCategory, outputNameBase, generateDotd)
+ .setOutputs(ruleContext, outputCategory, outputNameBase, generateDotd)
.setTempOutputFile(new PathFragment(tempOutputName));
setupCompileBuildVariables(
@@ -784,7 +842,7 @@
builder.getContext().getCppModuleMap(),
ImmutableMap.<String, String>of());
semantics.finalizeCompileActionBuilder(ruleContext, builder);
- CppCompileAction action = builder.build();
+ CppCompileAction action = builder.buildAndValidate(ruleContext);
env.registerAction(action);
if (addObject) {
if (usePic) {
@@ -818,7 +876,6 @@
maybePicName = CppHelper.getArtifactNameForCategory(
ruleContext, ccToolchain, ArtifactCategory.PIC_FILE, maybePicName);
}
-
String linkedName = CppHelper.getArtifactNameForCategory(
ruleContext, ccToolchain, linkTargetType.getLinkerOutput(), maybePicName);
PathFragment artifactFragment = new PathFragment(ruleContext.getLabel().getName())
@@ -923,7 +980,6 @@
// 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)
.addObjectFiles(ccOutputs.getObjectFiles(true))
@@ -1062,9 +1118,8 @@
* inputs, output and dotd file names, compilation context and copts.
*/
private CppCompileActionBuilder createCompileActionBuilder(Artifact source, Label label) {
- CppCompileActionBuilder builder = new CppCompileActionBuilder(
- ruleContext, source, label, ccToolchain);
-
+ CppCompileActionBuilder builder = new CppCompileActionBuilder(ruleContext, label, ccToolchain);
+ builder.setSourceFile(source);
builder.setContext(context).addCopts(copts);
builder.addEnvironment(ccToolchain.getEnvironment());
return builder;
@@ -1080,7 +1135,7 @@
CppCompileActionBuilder picBuilder = new CppCompileActionBuilder(builder);
picBuilder
.setPicMode(true)
- .setOutputs(outputCategory, outputName, generateDotd);
+ .setOutputs(ruleContext, outputCategory, outputName, generateDotd);
return picBuilder;
}
@@ -1109,7 +1164,7 @@
String outputArtifactNameBase = getOutputNameBaseWith(outputName, usePic);
CppCompileActionBuilder dBuilder = new CppCompileActionBuilder(builder);
- dBuilder.setOutputs(category, outputArtifactNameBase, generateDotd);
+ dBuilder.setOutputs(ruleContext, category, outputArtifactNameBase, generateDotd);
setupCompileBuildVariables(
dBuilder,
usePic,
@@ -1120,11 +1175,12 @@
builder.getContext().getCppModuleMap(),
ImmutableMap.<String, String>of());
semantics.finalizeCompileActionBuilder(ruleContext, dBuilder);
- CppCompileAction dAction = dBuilder.build();
+ CppCompileAction dAction = dBuilder.buildAndValidate(ruleContext);
ruleContext.registerAction(dAction);
CppCompileActionBuilder sdBuilder = new CppCompileActionBuilder(builder);
- sdBuilder.setOutputs(ArtifactCategory.GENERATED_ASSEMBLY, outputArtifactNameBase, generateDotd);
+ sdBuilder.setOutputs(
+ ruleContext, ArtifactCategory.GENERATED_ASSEMBLY, outputArtifactNameBase, generateDotd);
setupCompileBuildVariables(
sdBuilder,
usePic,
@@ -1135,7 +1191,7 @@
builder.getContext().getCppModuleMap(),
ImmutableMap.<String, String>of());
semantics.finalizeCompileActionBuilder(ruleContext, sdBuilder);
- CppCompileAction sdAction = sdBuilder.build();
+ CppCompileAction sdAction = sdBuilder.buildAndValidate(ruleContext);
ruleContext.registerAction(sdAction);
return ImmutableList.of(
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppSource.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppSource.java
index 17ec8d0..bc6ff4b 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppSource.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppSource.java
@@ -22,7 +22,17 @@
/** A source file that is an input to a c++ compilation. */
@AutoValue
-abstract class CppSource {
+public abstract class CppSource {
+
+ /**
+ * Types of sources.
+ */
+ public enum Type {
+ SOURCE,
+ HEADER,
+ CLIF_INPUT_PROTO,
+ }
+
/**
* Creates a {@code CppSource}.
*
@@ -30,9 +40,11 @@
* @param label the label from which this source arises in the build graph
* @param buildVariables build variables that should be used specifically in the compilation
* of this source
+ * @param type type of the source file.
*/
- static CppSource create(Artifact source, Label label, Map<String, String> buildVariables) {
- return new AutoValue_CppSource(source, label, buildVariables);
+ static CppSource create(Artifact source, Label label, Map<String, String> buildVariables,
+ Type type) {
+ return new AutoValue_CppSource(source, label, buildVariables, type);
}
/**
@@ -49,4 +61,9 @@
* Returns build variables to be used specifically in the compilation of this source.
*/
abstract Map<String, String> getBuildVariables();
+
+ /**
+ * Returns the type of this source.
+ */
+ abstract Type getType();
}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/FakeCppCompileAction.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/FakeCppCompileAction.java
index dc42ddd..cf57fd6 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/FakeCppCompileAction.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/FakeCppCompileAction.java
@@ -29,8 +29,6 @@
import com.google.devtools.build.lib.actions.ExecException;
import com.google.devtools.build.lib.actions.Executor;
import com.google.devtools.build.lib.actions.ResourceSet;
-import com.google.devtools.build.lib.analysis.RuleContext;
-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.collect.nestedset.NestedSetBuilder;
@@ -61,6 +59,7 @@
FakeCppCompileAction(
ActionOwner owner,
+ NestedSet<Artifact> allInputs,
ImmutableList<String> features,
FeatureConfiguration featureConfiguration,
CcToolchainFeatures.Variables variables,
@@ -75,18 +74,20 @@
Artifact outputFile,
PathFragment tempOutputFile,
DotdFile dotdFile,
- BuildConfiguration configuration,
+ ImmutableMap<String, String> localShellEnvironment,
+ boolean codeCoverageEnabled,
CppConfiguration cppConfiguration,
CppCompilationContext context,
Class<? extends CppCompileActionContext> actionContext,
ImmutableList<String> copts,
Predicate<String> nocopts,
- RuleContext ruleContext,
+ Iterable<IncludeScannable> lipoScannables,
+ Iterable<Artifact> builtinIncludeFiles,
CppSemantics cppSemantics,
- CcToolchainProvider ccToolchain,
ImmutableMap<String, String> executionInfo) {
super(
owner,
+ allInputs,
features,
featureConfiguration,
variables,
@@ -103,8 +104,8 @@
null,
null,
null,
- configuration.getLocalShellEnvironment(),
- configuration.isCodeCoverageEnabled(),
+ localShellEnvironment,
+ codeCoverageEnabled,
cppConfiguration,
// We only allow inclusion of header files explicitly declared in
// "srcs", so we only use declaredIncludeSrcs, not declaredIncludeDirs.
@@ -118,15 +119,14 @@
copts,
nocopts,
VOID_SPECIAL_INPUTS_HANDLER,
- ImmutableList.<IncludeScannable>of(),
+ lipoScannables,
ImmutableList.<Artifact>of(),
GUID,
executionInfo,
ImmutableMap.<String, String>of(),
CppCompileAction.CPP_COMPILE,
- ruleContext,
- cppSemantics,
- ccToolchain);
+ builtinIncludeFiles,
+ cppSemantics);
this.tempOutputFile = Preconditions.checkNotNull(tempOutputFile);
}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/LinkerInputs.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/LinkerInputs.java
index d4346a3..bb65c34 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/LinkerInputs.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/LinkerInputs.java
@@ -47,7 +47,10 @@
break;
case OBJECT_FILE:
- Preconditions.checkState(Link.OBJECT_FILETYPES.matches(basename));
+ // We skip file extension checks for TreeArtifacts because they represent directory
+ // artifacts without a file extension.
+ Preconditions.checkState(
+ artifact.isTreeArtifact() || Link.OBJECT_FILETYPES.matches(basename));
break;
default:
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ActionTemplateExpansionFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/ActionTemplateExpansionFunction.java
index 6a18db7..3164f16 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/ActionTemplateExpansionFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/ActionTemplateExpansionFunction.java
@@ -15,16 +15,20 @@
import com.google.common.collect.ImmutableList;
import com.google.devtools.build.lib.actions.Action;
+import com.google.devtools.build.lib.actions.ActionAnalysisMetadata;
+import com.google.devtools.build.lib.actions.Actions;
+import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.Artifact.TreeFileArtifact;
import com.google.devtools.build.lib.actions.ArtifactPrefixConflictException;
import com.google.devtools.build.lib.actions.MutableActionGraph.ActionConflictException;
-import com.google.devtools.build.lib.analysis.actions.SpawnActionTemplate;
+import com.google.devtools.build.lib.analysis.actions.ActionTemplate;
import com.google.devtools.build.lib.events.Event;
import com.google.devtools.build.lib.skyframe.ActionTemplateExpansionValue.ActionTemplateExpansionKey;
import com.google.devtools.build.skyframe.SkyFunction;
import com.google.devtools.build.skyframe.SkyFunctionException;
import com.google.devtools.build.skyframe.SkyKey;
import com.google.devtools.build.skyframe.SkyValue;
+import java.util.Map;
import javax.annotation.Nullable;
/**
@@ -40,7 +44,7 @@
public SkyValue compute(SkyKey skyKey, Environment env)
throws ActionTemplateExpansionFunctionException, InterruptedException {
ActionTemplateExpansionKey key = (ActionTemplateExpansionKey) skyKey.argument();
- SpawnActionTemplate actionTemplate = key.getActionTemplate();
+ ActionTemplate actionTemplate = key.getActionTemplate();
// Requests the TreeArtifactValue object for the input TreeArtifact.
SkyKey artifactValueKey = ArtifactSkyKey.key(actionTemplate.getInputTreeArtifact(), true);
@@ -56,6 +60,9 @@
// Expand the action template using the list of expanded input TreeFileArtifacts.
expandedActions = ImmutableList.<Action>copyOf(
actionTemplate.generateActionForInputArtifacts(inputTreeFileArtifacts, key));
+ // TODO(rduan): Add a check to verify the inputs of expanded actions are subsets of inputs
+ // of the ActionTemplate.
+ checkActionAndArtifactConflicts(expandedActions);
} catch (ActionConflictException e) {
e.reportTo(env.getListener());
throw new ActionTemplateExpansionFunctionException(e);
@@ -78,6 +85,18 @@
}
}
+ private static void checkActionAndArtifactConflicts(Iterable<Action> actions)
+ throws ActionConflictException, ArtifactPrefixConflictException {
+ Map<Artifact, ActionAnalysisMetadata> generatingActions =
+ Actions.findAndThrowActionConflict(ImmutableList.<ActionAnalysisMetadata>copyOf(actions));
+ Map<ActionAnalysisMetadata, ArtifactPrefixConflictException> artifactPrefixConflictMap =
+ Actions.findArtifactPrefixConflicts(generatingActions);
+
+ if (!artifactPrefixConflictMap.isEmpty()) {
+ throw artifactPrefixConflictMap.values().iterator().next();
+ }
+ }
+
@Nullable
@Override
public String extractTag(SkyKey skyKey) {
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ActionTemplateExpansionValue.java b/src/main/java/com/google/devtools/build/lib/skyframe/ActionTemplateExpansionValue.java
index fa42902..0ac6e60 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/ActionTemplateExpansionValue.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/ActionTemplateExpansionValue.java
@@ -16,7 +16,7 @@
import com.google.common.collect.ImmutableList;
import com.google.devtools.build.lib.actions.Action;
import com.google.devtools.build.lib.actions.ActionAnalysisMetadata;
-import com.google.devtools.build.lib.analysis.actions.SpawnActionTemplate;
+import com.google.devtools.build.lib.analysis.actions.ActionTemplate;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.skyframe.ActionLookupValue.ActionLookupKey;
import com.google.devtools.build.lib.util.Preconditions;
@@ -38,22 +38,22 @@
return expandedActions;
}
- static SkyKey key(SpawnActionTemplate actionTemplate) {
+ static SkyKey key(ActionTemplate<?> actionTemplate) {
return SkyKey.create(
SkyFunctions.ACTION_TEMPLATE_EXPANSION,
createActionTemplateExpansionKey(actionTemplate));
}
static ActionTemplateExpansionKey createActionTemplateExpansionKey(
- SpawnActionTemplate actionTemplate) {
+ ActionTemplate<?> actionTemplate) {
return new ActionTemplateExpansionKey(actionTemplate);
}
static final class ActionTemplateExpansionKey extends ActionLookupKey {
- private final SpawnActionTemplate actionTemplate;
+ private final ActionTemplate<?> actionTemplate;
- ActionTemplateExpansionKey(SpawnActionTemplate actionTemplate) {
+ ActionTemplateExpansionKey(ActionTemplate<?> actionTemplate) {
Preconditions.checkNotNull(
actionTemplate,
"Passed in action template cannot be null: %s",
@@ -72,8 +72,8 @@
return actionTemplate.getOwner().getLabel();
}
- /** Returns the associated {@link SpawnActionTemplate} */
- SpawnActionTemplate getActionTemplate() {
+ /** Returns the associated {@link ActionTemplate} */
+ ActionTemplate<?> getActionTemplate() {
return actionTemplate;
}
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ArtifactFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/ArtifactFunction.java
index cecc089..fad6603 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/ArtifactFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/ArtifactFunction.java
@@ -25,7 +25,7 @@
import com.google.devtools.build.lib.actions.Artifact.TreeFileArtifact;
import com.google.devtools.build.lib.actions.ArtifactOwner;
import com.google.devtools.build.lib.actions.MissingInputFileException;
-import com.google.devtools.build.lib.analysis.actions.SpawnActionTemplate;
+import com.google.devtools.build.lib.analysis.actions.ActionTemplate;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.events.Event;
import com.google.devtools.build.lib.events.EventHandler;
@@ -76,7 +76,7 @@
// If the action is an ActionTemplate, we need to expand the ActionTemplate into concrete
// actions, execute those actions in parallel and then aggregate the action execution results.
- if (artifact.isTreeArtifact() && actionMetadata instanceof SpawnActionTemplate) {
+ if (artifact.isTreeArtifact() && actionMetadata instanceof ActionTemplate) {
// Create the directory structures for the output TreeArtifact first.
try {
FileSystemUtils.createDirectoryAndParents(artifact.getPath());
@@ -91,7 +91,7 @@
}
return createTreeArtifactValueFromActionTemplate(
- (SpawnActionTemplate) actionMetadata, artifact, env);
+ (ActionTemplate) actionMetadata, artifact, env);
} else {
Preconditions.checkState(
actionMetadata instanceof Action,
@@ -118,7 +118,7 @@
}
private static TreeArtifactValue createTreeArtifactValueFromActionTemplate(
- SpawnActionTemplate actionTemplate, Artifact treeArtifact, Environment env)
+ ActionTemplate actionTemplate, Artifact treeArtifact, Environment env)
throws ArtifactFunctionException, InterruptedException {
// Request the list of expanded actions from the ActionTemplate.
ActionTemplateExpansionValue expansionValue = (ActionTemplateExpansionValue) env.getValue(
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/actions/SpawnActionTemplateTest.java b/src/test/java/com/google/devtools/build/lib/analysis/actions/SpawnActionTemplateTest.java
index cb6fe32..5d45da6 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/actions/SpawnActionTemplateTest.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/actions/SpawnActionTemplateTest.java
@@ -24,21 +24,17 @@
import com.google.devtools.build.lib.actions.Artifact.SpecialArtifactType;
import com.google.devtools.build.lib.actions.Artifact.TreeFileArtifact;
import com.google.devtools.build.lib.actions.ArtifactOwner;
-import com.google.devtools.build.lib.actions.ArtifactPrefixConflictException;
-import com.google.devtools.build.lib.actions.MutableActionGraph.ActionConflictException;
import com.google.devtools.build.lib.actions.Root;
import com.google.devtools.build.lib.actions.util.ActionsTestUtil;
import com.google.devtools.build.lib.analysis.actions.SpawnActionTemplate.OutputPathMapper;
import com.google.devtools.build.lib.testutil.Scratch;
import com.google.devtools.build.lib.vfs.PathFragment;
-
+import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-import java.util.List;
-
/**
* Tests {@link SpawnActionTemplate}.
*/
@@ -292,82 +288,6 @@
}
}
- @Test
- public void testExpandedAction_actionConflicts() throws Exception {
- Artifact inputTreeArtifact = createInputTreeArtifact();
- Artifact outputTreeArtifact = createOutputTreeArtifact();
-
- OutputPathMapper mapper = new OutputPathMapper() {
- @Override
- public PathFragment parentRelativeOutputPath(TreeFileArtifact inputTreeFileArtifact) {
- return new PathFragment("conflict_path");
- }
- };
-
- SpawnActionTemplate actionTemplate = builder(inputTreeArtifact, outputTreeArtifact)
- .setExecutable(new PathFragment("/bin/cp"))
- .setCommandLineTemplate(
- createSimpleCommandLineTemplate(inputTreeArtifact, outputTreeArtifact))
- .setOutputPathMapper(mapper)
- .build(ActionsTestUtil.NULL_ACTION_OWNER);
-
- Iterable<TreeFileArtifact> inputTreeFileArtifacts =
- createInputTreeFileArtifacts(inputTreeArtifact);
-
- try {
- actionTemplate.generateActionForInputArtifacts(
- inputTreeFileArtifacts, ArtifactOwner.NULL_OWNER);
- fail("Expected ActionConflictException");
- } catch (ActionConflictException e) {
- // expected
- }
- }
-
- @Test
- public void testExpandedAction_artifactPrefixConflicts() throws Exception {
- Artifact inputTreeArtifact = createInputTreeArtifact();
- Artifact outputTreeArtifact = createOutputTreeArtifact();
-
- OutputPathMapper mapper = new OutputPathMapper() {
- private int i = 0;
- @Override
- public PathFragment parentRelativeOutputPath(TreeFileArtifact inputTreeFileArtifact) {
- PathFragment path;
- switch (i) {
- case 0:
- path = new PathFragment("path_prefix");
- break;
- case 1:
- path = new PathFragment("path_prefix/conflict");
- break;
- default:
- path = inputTreeFileArtifact.getParentRelativePath();
- }
-
- ++i;
- return path;
- }
- };
-
- SpawnActionTemplate actionTemplate = builder(inputTreeArtifact, outputTreeArtifact)
- .setExecutable(new PathFragment("/bin/cp"))
- .setCommandLineTemplate(
- createSimpleCommandLineTemplate(inputTreeArtifact, outputTreeArtifact))
- .setOutputPathMapper(mapper)
- .build(ActionsTestUtil.NULL_ACTION_OWNER);
-
- Iterable<TreeFileArtifact> inputTreeFileArtifacts =
- createInputTreeFileArtifacts(inputTreeArtifact);
-
- try {
- actionTemplate.generateActionForInputArtifacts(
- inputTreeFileArtifacts, ArtifactOwner.NULL_OWNER);
- fail("Expected ArtifactPrefixConflictException");
- } catch (ArtifactPrefixConflictException e) {
- // expected
- }
- }
-
private SpawnActionTemplate.Builder builder(Artifact inputTreeArtifact,
Artifact outputTreeArtifact) {
return new SpawnActionTemplate.Builder(inputTreeArtifact, outputTreeArtifact);
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/TreeArtifactBuildTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/TreeArtifactBuildTest.java
index cbb3976..307e4a6 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/TreeArtifactBuildTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/TreeArtifactBuildTest.java
@@ -47,6 +47,7 @@
import com.google.devtools.build.lib.actions.util.ActionsTestUtil;
import com.google.devtools.build.lib.actions.util.TestAction;
import com.google.devtools.build.lib.actions.util.TestAction.DummyAction;
+import com.google.devtools.build.lib.analysis.actions.ActionTemplate;
import com.google.devtools.build.lib.analysis.actions.SpawnActionTemplate;
import com.google.devtools.build.lib.events.Event;
import com.google.devtools.build.lib.events.EventKind;
@@ -706,7 +707,7 @@
ImmutableList.<Artifact>of(treeFileArtifactB), expectedOutputTreeFileArtifact2);
actionTemplateExpansionFunction = new DummyActionTemplateExpansionFunction(
- ImmutableMultimap.of(
+ ImmutableMultimap.<ActionTemplate<?>, Action>of(
actionTemplate, generateOutputAction,
actionTemplate, noGenerateOutputAction));
@@ -746,7 +747,7 @@
ImmutableList.<Artifact>of(expectedOutputTreeFileArtifact2));
actionTemplateExpansionFunction = new DummyActionTemplateExpansionFunction(
- ImmutableMultimap.of(
+ ImmutableMultimap.<ActionTemplate<?>, Action>of(
actionTemplate, generateOutputAction,
actionTemplate, noGenerateOutputAction));
@@ -791,7 +792,7 @@
ImmutableList.<Artifact>of(expectedOutputTreeFileArtifact2));
actionTemplateExpansionFunction = new DummyActionTemplateExpansionFunction(
- ImmutableMultimap.of(
+ ImmutableMultimap.<ActionTemplate<?>, Action>of(
actionTemplate, generateOutputAction,
actionTemplate, throwingAction));
@@ -835,7 +836,7 @@
ImmutableList.<Artifact>of(expectedOutputTreeFileArtifact2));
actionTemplateExpansionFunction = new DummyActionTemplateExpansionFunction(
- ImmutableMultimap.of(
+ ImmutableMultimap.<ActionTemplate<?>, Action>of(
actionTemplate, throwingAction,
actionTemplate, anotherThrowingAction));
@@ -1179,17 +1180,17 @@
/** A dummy action template expansion function that just returns the injected actions */
private static class DummyActionTemplateExpansionFunction implements SkyFunction {
- private final Multimap<SpawnActionTemplate, Action> actionTemplateToActionMap;
+ private final Multimap<ActionTemplate<?>, Action> actionTemplateToActionMap;
DummyActionTemplateExpansionFunction(
- Multimap<SpawnActionTemplate, Action> actionTemplateToActionMap) {
+ Multimap<ActionTemplate<?>, Action> actionTemplateToActionMap) {
this.actionTemplateToActionMap = actionTemplateToActionMap;
}
@Override
public SkyValue compute(SkyKey skyKey, Environment env) {
ActionTemplateExpansionKey key = (ActionTemplateExpansionKey) skyKey.argument();
- SpawnActionTemplate actionTemplate = key.getActionTemplate();
+ ActionTemplate<?> actionTemplate = key.getActionTemplate();
return new ActionTemplateExpansionValue(
Preconditions.checkNotNull(actionTemplateToActionMap.get(actionTemplate)));
}