// Copyright 2014 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.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.FailAction;
import com.google.devtools.build.lib.analysis.AnalysisEnvironment;
import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode;
import com.google.devtools.build.lib.analysis.RuleContext;
import com.google.devtools.build.lib.analysis.TransitiveInfoCollection;
import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.packages.BuildType;
import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException;
import com.google.devtools.build.lib.rules.cpp.CcCompilationOutputs.Builder;
import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.ExpansionException;
import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.FeatureConfiguration;
import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.Variables.StringSequenceBuilder;
import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.Variables.VariablesExtension;
import com.google.devtools.build.lib.rules.cpp.CppCompileAction.DotdFile;
import com.google.devtools.build.lib.rules.cpp.Link.LinkStaticness;
import com.google.devtools.build.lib.rules.cpp.Link.LinkTargetType;
import com.google.devtools.build.lib.rules.cpp.Link.Picness;
import com.google.devtools.build.lib.rules.cpp.Link.Staticness;
import com.google.devtools.build.lib.rules.cpp.LinkerInputs.LibraryToLink;
import com.google.devtools.build.lib.rules.test.InstrumentedFilesCollector;
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.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import javax.annotation.Nullable;
/**
* Representation of a C/C++ compilation. Its purpose is to share the code that creates compilation
* actions between all classes that need to do so. It follows the builder pattern - load up the
* necessary settings and then call {@link #createCcCompileActions}.
*
*
This class is not thread-safe, and it should only be used once for each set of source files,
* i.e. calling {@link #createCcCompileActions} will throw an Exception if called twice.
*/
public final class CppModel {
private final CppSemantics semantics;
private final RuleContext ruleContext;
private final BuildConfiguration configuration;
private final CppConfiguration cppConfiguration;
// compile model
private CppCompilationContext context;
private final Set sourceFiles = new LinkedHashSet<>();
private final List mandatoryInputs = new ArrayList<>();
private final List copts = new ArrayList<>();
@Nullable private Pattern nocopts;
private boolean fake;
private boolean maySaveTemps;
private boolean onlySingleOutput;
private CcCompilationOutputs compilationOutputs;
// link model
private final List linkopts = new ArrayList<>();
private LinkTargetType linkType = LinkTargetType.STATIC_LIBRARY;
private boolean neverLink;
private final List linkActionInputs = new ArrayList<>();
private boolean allowInterfaceSharedObjects;
private boolean createDynamicLibrary = true;
private Artifact soImplArtifact;
private FeatureConfiguration featureConfiguration;
private List variablesExtensions = new ArrayList<>();
private CcToolchainProvider ccToolchain;
public CppModel(RuleContext ruleContext, CppSemantics semantics,
CcToolchainProvider ccToolchain) {
this.ruleContext = Preconditions.checkNotNull(ruleContext);
this.semantics = semantics;
this.ccToolchain = Preconditions.checkNotNull(ccToolchain);
configuration = ruleContext.getConfiguration();
cppConfiguration = ruleContext.getFragment(CppConfiguration.class);
}
private Artifact getDwoFile(Artifact outputFile) {
return ruleContext.getRelatedArtifact(outputFile.getRootRelativePath(), ".dwo");
}
/**
* If the cpp compilation is a fake, then it creates only a single compile action without PIC.
* Defaults to false.
*/
public CppModel setFake(boolean fake) {
this.fake = fake;
return this;
}
/**
* If set, the CppModel only creates a single .o output that can be linked into a dynamic library,
* i.e., it never generates both PIC and non-PIC outputs. Otherwise it creates outputs that can be
* linked into both static binaries and dynamic libraries (if both require PIC or both require
* non-PIC, then it still only creates a single output). Defaults to false.
*/
public CppModel setOnlySingleOutput(boolean onlySingleOutput) {
this.onlySingleOutput = onlySingleOutput;
return this;
}
/**
* Whether to create actions for temps. This defaults to false.
*/
public CppModel setSaveTemps(boolean maySaveTemps) {
this.maySaveTemps = maySaveTemps;
return this;
}
/**
* Sets the compilation context, i.e. include directories and allowed header files inclusions.
*/
public CppModel setContext(CppCompilationContext context) {
this.context = context;
return this;
}
/**
* Adds a single source file to be compiled. The given build variables will be added to those used
* to compile this source file. Note that this should only be called for primary compilation
* units, including module files or headers to be parsed or preprocessed.
*/
public CppModel addCompilationUnitSources(
Iterable sourceFiles, Label sourceLabel, Map buildVariables,
CppSource.Type type) {
for (Artifact sourceFile : sourceFiles) {
this.sourceFiles.add(CppSource.create(sourceFile, sourceLabel, buildVariables, type));
}
return this;
}
/**
* Adds all the source files. Note that this should only be called for primary compilation units,
* including module files or headers to be parsed or preprocessed.
*/
public CppModel addCompilationUnitSources(Set sources) {
this.sourceFiles.addAll(sources);
return this;
}
/** Adds mandatory inputs. */
public CppModel addMandatoryInputs(Collection artifacts) {
this.mandatoryInputs.addAll(artifacts);
return this;
}
/**
* Adds the given copts.
*/
public CppModel addCopts(Collection copts) {
this.copts.addAll(copts);
return this;
}
/**
* Sets the nocopts pattern. This is used to filter out flags from the system defined set of
* flags. By default no filter is applied.
*/
public CppModel setNoCopts(@Nullable Pattern nocopts) {
this.nocopts = nocopts;
return this;
}
/**
* Adds the given linkopts to the optional dynamic library link command.
*/
public CppModel addLinkopts(Collection linkopts) {
this.linkopts.addAll(linkopts);
return this;
}
/**
* Adds the given variablesExensions for templating the crosstool.
*
*
In general, we prefer the build variables (especially those that derive strictly from
* the configuration) be learned by inspecting the CcToolchain, as passed to the rule in the
* CcToolchainProvider. However, for build variables that must be injected into the rule
* implementation (ex. build variables learned from the BUILD file), should be added using the
* VariablesExtension abstraction. This allows the injection to construct non-trivial build
* variables (lists, ect.).
*/
public CppModel addVariablesExtension(Collection variablesExtensions) {
this.variablesExtensions.addAll(variablesExtensions);
return this;
}
/**
* Sets the link type used for the link actions. Note that only static links are supported at this
* time.
*/
public CppModel setLinkTargetType(LinkTargetType linkType) {
this.linkType = linkType;
return this;
}
public CppModel setNeverLink(boolean neverLink) {
this.neverLink = neverLink;
return this;
}
/**
* Adds an artifact to the inputs of any link actions created by this CppModel.
*/
public CppModel addLinkActionInputs(Collection inputs) {
this.linkActionInputs.addAll(inputs);
return this;
}
/**
* Whether to allow interface dynamic libraries. Note that setting this to true only has an effect
* if the configuration allows it. Defaults to false.
*/
public CppModel setAllowInterfaceSharedObjects(boolean allowInterfaceSharedObjects) {
// TODO(bazel-team): Set the default to true, and require explicit action to disable it.
this.allowInterfaceSharedObjects = allowInterfaceSharedObjects;
return this;
}
public CppModel setCreateDynamicLibrary(boolean createDynamicLibrary) {
this.createDynamicLibrary = createDynamicLibrary;
return this;
}
public CppModel setDynamicLibrary(Artifact soImplFilename) {
this.soImplArtifact = soImplFilename;
return this;
}
/**
* Sets the feature configuration to be used for C/C++ actions.
*/
public CppModel setFeatureConfiguration(FeatureConfiguration featureConfiguration) {
this.featureConfiguration = featureConfiguration;
return this;
}
/**
* @returns whether we want to provide header modules for the current target.
*/
private boolean shouldProvideHeaderModules() {
return featureConfiguration.isEnabled(CppRuleClasses.HEADER_MODULES)
&& !cppConfiguration.isLipoContextCollector();
}
/**
* @return the non-pic header module artifact for the current target.
*/
public Artifact getHeaderModule(Artifact moduleMapArtifact) {
PathFragment objectDir = CppHelper.getObjDirectory(ruleContext.getLabel());
PathFragment outputName = objectDir.getRelative(
semantics.getEffectiveSourcePath(moduleMapArtifact));
return ruleContext.getRelatedArtifact(outputName, ".pcm");
}
/**
* @return the pic header module artifact for the current target.
*/
public Artifact getPicHeaderModule(Artifact moduleMapArtifact) {
PathFragment objectDir = CppHelper.getObjDirectory(ruleContext.getLabel());
PathFragment outputName = objectDir.getRelative(
semantics.getEffectiveSourcePath(moduleMapArtifact));
return ruleContext.getRelatedArtifact(outputName, ".pic.pcm");
}
/**
* @return whether this target needs to generate pic actions.
*/
private boolean getGeneratePicActions() {
return CppHelper.usePic(ruleContext, false);
}
/**
* @return whether this target needs to generate non-pic actions.
*/
private boolean getGenerateNoPicActions() {
return
// If we always need pic for everything, then don't bother to create a no-pic action.
(!CppHelper.usePic(ruleContext, true) || !CppHelper.usePic(ruleContext, false))
// onlySingleOutput guarantees that the code is only ever linked into a dynamic library - so
// we don't need a no-pic action even if linking into a binary would require it.
&& !((onlySingleOutput && getGeneratePicActions()));
}
/**
* @return whether this target needs to generate a pic header module.
*/
public boolean getGeneratesPicHeaderModule() {
return shouldProvideHeaderModules() && !fake && getGeneratePicActions();
}
/**
* @return whether this target needs to generate a non-pic header module.
*/
public boolean getGeneratesNoPicHeaderModule() {
return shouldProvideHeaderModules() && !fake && getGenerateNoPicActions();
}
/**
* Returns a {@code CppCompileActionBuilder} with the common fields for a C++ compile action being
* initialized.
*/
private CppCompileActionBuilder initializeCompileAction(
Artifact sourceArtifact, Label sourceLabel) {
CppCompileActionBuilder builder = createCompileActionBuilder(sourceArtifact, sourceLabel);
if (nocopts != null) {
builder.addNocopts(nocopts);
}
builder.setFeatureConfiguration(featureConfiguration);
return builder;
}
/** Get the safe path strings for a list of paths to use in the build variables. */
private ImmutableSet getSafePathStrings(Collection paths) {
ImmutableSet.Builder result = ImmutableSet.builder();
for (PathFragment path : paths) {
result.add(path.getSafePathString());
}
return result.build();
}
private void setupCompileBuildVariables(
CppCompileActionBuilder builder,
boolean usePic,
PathFragment ccRelativeName,
PathFragment autoFdoImportPath,
Artifact gcnoFile,
Artifact dwoFile,
CppModuleMap cppModuleMap,
Map sourceSpecificBuildVariables) {
CcToolchainFeatures.Variables.Builder buildVariables =
new CcToolchainFeatures.Variables.Builder();
// TODO(bazel-team): Pull out string constants for all build variables.
CppCompilationContext builderContext = builder.getContext();
Artifact sourceFile = builder.getSourceFile();
Artifact outputFile = builder.getOutputFile();
String realOutputFilePath;
buildVariables.addStringVariable("source_file", sourceFile.getExecPathString());
buildVariables.addStringVariable("output_file", outputFile.getExecPathString());
if (builder.getTempOutputFile() != null) {
realOutputFilePath = builder.getTempOutputFile().getPathString();
} else {
realOutputFilePath = builder.getOutputFile().getExecPathString();
}
if (FileType.contains(outputFile, CppFileTypes.ASSEMBLER, CppFileTypes.PIC_ASSEMBLER)) {
buildVariables.addStringVariable("output_assembly_file", realOutputFilePath);
} else if (FileType.contains(outputFile, CppFileTypes.PREPROCESSED_C,
CppFileTypes.PREPROCESSED_CPP, CppFileTypes.PIC_PREPROCESSED_C,
CppFileTypes.PIC_PREPROCESSED_CPP)) {
buildVariables.addStringVariable("output_preprocess_file", realOutputFilePath);
} else {
buildVariables.addStringVariable("output_object_file", realOutputFilePath);
}
DotdFile dotdFile = CppFileTypes.mustProduceDotdFile(sourceFile)
? Preconditions.checkNotNull(builder.getDotdFile()) : null;
// Set dependency_file to enable