diff options
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileActionBuilder.java')
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileActionBuilder.java | 439 |
1 files changed, 439 insertions, 0 deletions
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 new file mode 100644 index 0000000000..0d8da3e4a2 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileActionBuilder.java @@ -0,0 +1,439 @@ +// Copyright 2014 Google Inc. 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.base.Functions; +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +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.actions.Root; +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; +import com.google.devtools.build.lib.collect.nestedset.NestedSet; +import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; +import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.FeatureConfiguration; +import com.google.devtools.build.lib.rules.cpp.CppCompileAction.DotdFile; +import com.google.devtools.build.lib.rules.cpp.CppCompileAction.IncludeResolver; +import com.google.devtools.build.lib.syntax.Label; +import com.google.devtools.build.lib.util.FileType; +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.List; +import java.util.UUID; +import java.util.regex.Pattern; + +/** + * Builder class to construct C++ compile actions. + */ +public class CppCompileActionBuilder { + public static final UUID GUID = UUID.fromString("cee5db0a-d2ad-4c69-9b81-97c936a29075"); + + private final ActionOwner owner; + private final List<String> features = new ArrayList<>(); + private CcToolchainFeatures.FeatureConfiguration featureConfiguration; + private final Artifact sourceFile; + private final Label sourceLabel; + private final NestedSetBuilder<Artifact> mandatoryInputsBuilder; + private NestedSetBuilder<Artifact> pluginInputsBuilder; + private Artifact optionalSourceFile; + private Artifact outputFile; + 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 enableLayeringCheck; + private boolean compileHeaderModules; + private String fdoBuildStamp; + private IncludeResolver includeResolver = CppCompileAction.VOID_INCLUDE_RESOLVER; + private UUID actionClassId = GUID; + private Class<? extends CppCompileActionContext> actionContext; + private CppConfiguration cppConfiguration; + private ImmutableMap<Artifact, IncludeScannable> lipoScannableMap; + + /** + * 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) { + 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.pluginInputsBuilder = NestedSetBuilder.stableOrder(); + this.lipoScannableMap = getLipoScannableMap(ruleContext); + + features.addAll(ruleContext.getFeatures()); + } + + private static ImmutableMap<Artifact, IncludeScannable> getLipoScannableMap( + RuleContext ruleContext) { + if (!ruleContext.getFragment(CppConfiguration.class).isLipoOptimization()) { + return null; + } + + LipoContextProvider provider = ruleContext.getPrerequisite( + ":lipo_context_collector", Mode.DONT_CHECK, LipoContextProvider.class); + return provider.getIncludeScannables(); + } + + /** + * Creates a builder for an owner that is not required to be rule. + */ + public CppCompileActionBuilder( + ActionOwner owner, AnalysisEnvironment analysisEnvironment, Artifact sourceFile, + Label sourceLabel, BuildConfiguration configuration) { + this.owner = owner; + this.actionContext = CppCompileActionContext.class; + this.cppConfiguration = configuration.getFragment(CppConfiguration.class); + this.analysisEnvironment = analysisEnvironment; + this.sourceFile = sourceFile; + this.sourceLabel = sourceLabel; + this.configuration = configuration; + this.mandatoryInputsBuilder = NestedSetBuilder.stableOrder(); + this.pluginInputsBuilder = NestedSetBuilder.stableOrder(); + this.lipoScannableMap = ImmutableMap.of(); + } + + /** + * Creates a builder that is a copy of another builder. + */ + public CppCompileActionBuilder(CppCompileActionBuilder other) { + this.owner = other.owner; + this.features.addAll(other.features); + this.featureConfiguration = other.featureConfiguration; + this.sourceFile = other.sourceFile; + this.sourceLabel = other.sourceLabel; + this.mandatoryInputsBuilder = NestedSetBuilder.<Artifact>stableOrder() + .addTransitive(other.mandatoryInputsBuilder.build()); + this.pluginInputsBuilder = NestedSetBuilder.<Artifact>stableOrder() + .addTransitive(other.pluginInputsBuilder.build()); + this.optionalSourceFile = other.optionalSourceFile; + this.outputFile = other.outputFile; + 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.enableLayeringCheck = other.enableLayeringCheck; + this.compileHeaderModules = other.compileHeaderModules; + this.includeResolver = other.includeResolver; + this.actionClassId = other.actionClassId; + this.actionContext = other.actionContext; + this.cppConfiguration = other.cppConfiguration; + this.lipoScannableMap = other.lipoScannableMap; + } + + public PathFragment getTempOutputFile() { + return tempOutputFile; + } + + public Artifact getSourceFile() { + return sourceFile; + } + + public CppCompilationContext getContext() { + return context; + } + + public NestedSet<Artifact> getMandatoryInputs() { + return mandatoryInputsBuilder.build(); + } + + /** + * Returns the .dwo output file that matches the specified .o output file. If Fission mode + * isn't enabled for this build, this is null (we don't produce .dwo files in that case). + */ + private static Artifact getDwoFile(Artifact outputFile, AnalysisEnvironment artifactFactory, + CppConfiguration cppConfiguration) { + + // Only create .dwo's for .o compilations (i.e. not .ii or .S). + boolean isObjectOutput = CppFileTypes.OBJECT_FILE.matches(outputFile.getExecPath()) + || CppFileTypes.PIC_OBJECT_FILE.matches(outputFile.getExecPath()); + + // Note configurations can be null for tests. + if (cppConfiguration != null && cppConfiguration.useFission() && isObjectOutput) { + return artifactFactory.getDerivedArtifact( + FileSystemUtils.replaceExtension(outputFile.getRootRelativePath(), ".dwo"), + outputFile.getRoot()); + } else { + return null; + } + } + + private static Predicate<String> getNocoptPredicate(Collection<Pattern> patterns) { + final ImmutableList<Pattern> finalPatterns = ImmutableList.copyOf(patterns); + if (finalPatterns.isEmpty()) { + return Predicates.alwaysTrue(); + } else { + return new Predicate<String>() { + @Override + public boolean apply(String option) { + for (Pattern pattern : finalPatterns) { + if (pattern.matcher(option).matches()) { + return false; + } + } + + return true; + } + }; + } + } + + 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()); + } + + /** + * Builds the Action as configured and returns the to be generated Artifact. + * + * <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 build() { + // Configuration can be null in tests. + NestedSetBuilder<Artifact> realMandatoryInputsBuilder = NestedSetBuilder.compileOrder(); + realMandatoryInputsBuilder.addTransitive(mandatoryInputsBuilder.build()); + if (tempOutputFile == null && configuration != null + && !configuration.getFragment(CppConfiguration.class).shouldScanIncludes()) { + realMandatoryInputsBuilder.addTransitive(context.getDeclaredIncludeSrcs()); + } + realMandatoryInputsBuilder.addTransitive(context.getAdditionalInputs()); + realMandatoryInputsBuilder.addTransitive(pluginInputsBuilder.build()); + realMandatoryInputsBuilder.add(sourceFile); + boolean fake = tempOutputFile != null; + + // Copying the collections is needed to make the builder reusable. + if (fake) { + return new FakeCppCompileAction(owner, ImmutableList.copyOf(features), featureConfiguration, + sourceFile, sourceLabel, realMandatoryInputsBuilder.build(), outputFile, tempOutputFile, + dotdFile, configuration, cppConfiguration, context, ImmutableList.copyOf(copts), + ImmutableList.copyOf(pluginOpts), getNocoptPredicate(nocopts), + extraSystemIncludePrefixes, enableLayeringCheck, fdoBuildStamp); + } else { + NestedSet<Artifact> realMandatoryInputs = realMandatoryInputsBuilder.build(); + + return new CppCompileAction(owner, ImmutableList.copyOf(features), featureConfiguration, + sourceFile, sourceLabel, realMandatoryInputs, outputFile, dotdFile, + gcnoFile, getDwoFile(outputFile, analysisEnvironment, cppConfiguration), + optionalSourceFile, configuration, cppConfiguration, context, + actionContext, ImmutableList.copyOf(copts), + ImmutableList.copyOf(pluginOpts), + getNocoptPredicate(nocopts), + extraSystemIncludePrefixes, enableLayeringCheck, fdoBuildStamp, + includeResolver, getLipoScannables(realMandatoryInputs), actionClassId, + compileHeaderModules); + } + } + + /** + * Sets the feature configuration to be used for the action. + */ + public CppCompileActionBuilder setFeatureConfiguration( + FeatureConfiguration featureConfiguration) { + this.featureConfiguration = featureConfiguration; + return this; + } + + public CppCompileActionBuilder setIncludeResolver(IncludeResolver includeResolver) { + this.includeResolver = includeResolver; + return this; + } + + public CppCompileActionBuilder setCppConfiguration(CppConfiguration cppConfiguration) { + this.cppConfiguration = cppConfiguration; + return this; + } + + public CppCompileActionBuilder setActionContext( + Class<? extends CppCompileActionContext> actionContext) { + this.actionContext = actionContext; + return this; + } + + public CppCompileActionBuilder setActionClassId(UUID uuid) { + this.actionClassId = uuid; + return this; + } + + public CppCompileActionBuilder setExtraSystemIncludePrefixes( + Collection<PathFragment> extraSystemIncludePrefixes) { + this.extraSystemIncludePrefixes = ImmutableList.copyOf(extraSystemIncludePrefixes); + return this; + } + + public CppCompileActionBuilder addPluginInput(Artifact artifact) { + pluginInputsBuilder.add(artifact); + return this; + } + + public CppCompileActionBuilder clearPluginInputs() { + pluginInputsBuilder = NestedSetBuilder.stableOrder(); + return this; + } + + /** + * Set an optional source file (usually with metadata of the main source file). The optional + * source file can only be set once, whether via this method or through the constructor + * {@link #CppCompileActionBuilder(CppCompileActionBuilder)}. + */ + public CppCompileActionBuilder addOptionalSourceFile(Artifact artifact) { + Preconditions.checkState(optionalSourceFile == null, "%s %s", optionalSourceFile, artifact); + optionalSourceFile = artifact; + return this; + } + + public CppCompileActionBuilder addMandatoryInputs(Iterable<Artifact> artifacts) { + mandatoryInputsBuilder.addAll(artifacts); + return this; + } + + public CppCompileActionBuilder addTransitiveMandatoryInputs(NestedSet<Artifact> artifacts) { + mandatoryInputsBuilder.addTransitive(artifacts); + return this; + } + + public CppCompileActionBuilder setOutputFile(Artifact outputFile) { + this.outputFile = outputFile; + return this; + } + + /** + * The temp output file is not an artifact, since it does not appear in the outputs of the + * action. + * + * <p>This is theoretically a problem if that file already existed before, since then Blaze + * does not delete it before executing the rule, but 1. that only applies for local + * execution which does not happen very often and 2. it is only a problem if the compiler is + * affected by the presence of this file, which it should not be. + */ + public CppCompileActionBuilder setTempOutputFile(PathFragment tempOutputFile) { + this.tempOutputFile = tempOutputFile; + return this; + } + + @VisibleForTesting + public CppCompileActionBuilder setDotdFileForTesting(Artifact dotdFile) { + this.dotdFile = new DotdFile(dotdFile); + return this; + } + + public CppCompileActionBuilder setDotdFile(PathFragment outputName, String extension, + RuleContext ruleContext) { + if (configuration.getFragment(CppConfiguration.class).getInmemoryDotdFiles()) { + // Just set the path, no artifact is constructed + PathFragment file = FileSystemUtils.replaceExtension(outputName, extension); + Root root = configuration.getBinDirectory(); + dotdFile = new DotdFile(root.getExecPath().getRelative(file)); + } else { + dotdFile = new DotdFile(ruleContext.getRelatedArtifact(outputName, extension)); + } + return this; + } + + public CppCompileActionBuilder setGcnoFile(Artifact gcnoFile) { + this.gcnoFile = gcnoFile; + return this; + } + + public CppCompileActionBuilder addCopt(String copt) { + copts.add(copt); + return this; + } + + public CppCompileActionBuilder addPluginOpt(String opt) { + pluginOpts.add(opt); + return this; + } + + public CppCompileActionBuilder clearPluginOpts() { + pluginOpts.clear(); + return this; + } + + public CppCompileActionBuilder addCopts(Iterable<? extends String> copts) { + Iterables.addAll(this.copts, copts); + return this; + } + + public CppCompileActionBuilder addCopts(int position, Iterable<? extends String> copts) { + this.copts.addAll(position, ImmutableList.copyOf(copts)); + return this; + } + + public CppCompileActionBuilder addNocopts(Pattern nocopts) { + this.nocopts.add(nocopts); + return this; + } + + public CppCompileActionBuilder setContext(CppCompilationContext context) { + this.context = context; + return this; + } + + public CppCompileActionBuilder setEnableLayeringCheck(boolean enableLayeringCheck) { + this.enableLayeringCheck = enableLayeringCheck; + return this; + } + + /** + * Sets whether the CompileAction should use header modules for its compilation. + */ + public CppCompileActionBuilder setCompileHeaderModules(boolean compileHeaderModules) { + this.compileHeaderModules = compileHeaderModules; + return this; + } + + public CppCompileActionBuilder setFdoBuildStamp(String fdoBuildStamp) { + this.fdoBuildStamp = fdoBuildStamp; + return this; + } +} |