blob: e6d942bb4589475b6f58e3130593be5fd2283cf2 [file] [log] [blame]
// 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}.
*
* <p>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 CppCompilationContext interfaceContext;
private final Set<CppSource> sourceFiles = new LinkedHashSet<>();
private final List<Artifact> mandatoryInputs = new ArrayList<>();
private final List<String> copts = new ArrayList<>();
@Nullable private Pattern nocopts;
private boolean fake;
private boolean maySaveTemps;
private boolean onlySingleOutput;
private CcCompilationOutputs compilationOutputs;
// link model
private final List<String> linkopts = new ArrayList<>();
private LinkTargetType linkType = LinkTargetType.STATIC_LIBRARY;
private boolean neverLink;
private final List<Artifact> linkActionInputs = new ArrayList<>();
private boolean allowInterfaceSharedObjects;
private boolean createDynamicLibrary = true;
private Artifact soImplArtifact;
private FeatureConfiguration featureConfiguration;
private List<VariablesExtension> variablesExtensions = new ArrayList<>();
public CppModel(RuleContext ruleContext, CppSemantics semantics) {
this.ruleContext = Preconditions.checkNotNull(ruleContext);
this.semantics = semantics;
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;
}
/**
* Sets the compilation context, i.e. include directories and allowed header files inclusions, for
* the compilation of this model's interface, e.g. header module.
*/
public CppModel setInterfaceContext(CppCompilationContext context) {
this.interfaceContext = 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<Artifact> sourceFiles, Label sourceLabel, Map<String, String> buildVariables) {
for (Artifact sourceFile : sourceFiles) {
this.sourceFiles.add(CppSource.create(sourceFile, sourceLabel, buildVariables));
}
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<CppSource> sources) {
this.sourceFiles.addAll(sources);
return this;
}
/** Adds mandatory inputs. */
public CppModel addMandatoryInputs(Collection<Artifact> artifacts) {
this.mandatoryInputs.addAll(artifacts);
return this;
}
/**
* Adds the given copts.
*/
public CppModel addCopts(Collection<String> 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<String> linkopts) {
this.linkopts.addAll(linkopts);
return this;
}
/**
* Adds the given variablesExensions for templating the crosstool.
*
* <p>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<VariablesExtension> 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<Artifact> 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, boolean forInterface) {
CppCompileActionBuilder builder =
createCompileActionBuilder(sourceArtifact, sourceLabel, forInterface);
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<String> getSafePathStrings(Collection<PathFragment> paths) {
ImmutableSet.Builder<String> 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,
Map<String, String> sourceSpecificBuildVariables) {
CcToolchainFeatures.Variables.Builder buildVariables =
new CcToolchainFeatures.Variables.Builder();
// TODO(bazel-team): Pull out string constants for all build variables.
CppCompilationContext builderContext = builder.getContext();
CppModuleMap cppModuleMap = builderContext.getCppModuleMap();
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.getPath().toString())
? Preconditions.checkNotNull(builder.getDotdFile()) : null;
// Set dependency_file to enable <object>.d file generation.
if (dotdFile != null) {
buildVariables.addStringVariable(
"dependency_file", dotdFile.getSafeExecPath().getPathString());
}
if (featureConfiguration.isEnabled(CppRuleClasses.MODULE_MAPS) && cppModuleMap != null) {
// If the feature is enabled and cppModuleMap is null, we are about to fail during analysis
// in any case, but don't crash.
buildVariables.addStringVariable("module_name", cppModuleMap.getName());
buildVariables.addStringVariable(
"module_map_file", cppModuleMap.getArtifact().getExecPathString());
StringSequenceBuilder sequence = new StringSequenceBuilder();
for (Artifact artifact : builderContext.getDirectModuleMaps()) {
sequence.addValue(artifact.getExecPathString());
}
buildVariables.addCustomBuiltVariable("dependent_module_map_files", sequence);
}
if (featureConfiguration.isEnabled(CppRuleClasses.USE_HEADER_MODULES)) {
// Module inputs will be set later when the action is executed.
buildVariables.addStringSequenceVariable("module_files", ImmutableSet.<String>of());
}
if (featureConfiguration.isEnabled(CppRuleClasses.INCLUDE_PATHS)) {
buildVariables.addStringSequenceVariable(
"include_paths", getSafePathStrings(builderContext.getIncludeDirs()));
buildVariables.addStringSequenceVariable(
"quote_include_paths", getSafePathStrings(builderContext.getQuoteIncludeDirs()));
buildVariables.addStringSequenceVariable(
"system_include_paths", getSafePathStrings(builderContext.getSystemIncludeDirs()));
}
if (featureConfiguration.isEnabled(CppRuleClasses.PREPROCESSOR_DEFINES)) {
String fdoBuildStamp = CppHelper.getFdoBuildStamp(ruleContext);
ImmutableList<String> defines;
if (fdoBuildStamp != null) {
// Stamp FDO builds with FDO subtype string
defines = ImmutableList.<String>builder()
.addAll(builderContext.getDefines())
.add(CppConfiguration.FDO_STAMP_MACRO
+ "=\"" + CppHelper.getFdoBuildStamp(ruleContext) + "\"")
.build();
} else {
defines = builderContext.getDefines();
}
buildVariables.addStringSequenceVariable("preprocessor_defines", defines);
}
if (usePic) {
if (!featureConfiguration.isEnabled(CppRuleClasses.PIC)) {
ruleContext.ruleError("PIC compilation is requested but the toolchain does not support it");
}
buildVariables.addStringVariable("pic", "");
}
if (ccRelativeName != null) {
CppHelper.getFdoSupport(ruleContext).configureCompilation(builder, buildVariables,
ruleContext, ccRelativeName, autoFdoImportPath, usePic, featureConfiguration);
}
if (gcnoFile != null) {
buildVariables.addStringVariable("gcov_gcno_file", gcnoFile.getExecPathString());
}
if (dwoFile != null) {
buildVariables.addStringVariable("per_object_debug_info_file", dwoFile.getExecPathString());
}
buildVariables.addAllStringVariables(CppHelper.getToolchain(ruleContext).getBuildVariables());
buildVariables.addAllStringVariables(sourceSpecificBuildVariables);
for (VariablesExtension extension : variablesExtensions) {
extension.addVariables(buildVariables);
}
CcToolchainFeatures.Variables variables = buildVariables.build();
builder.setVariables(variables);
}
/**
* Constructs the C++ compiler actions. It generally creates one action for every specified source
* file. It takes into account LIPO, fake-ness, coverage, and PIC, in addition to using the
* settings specified on the current object. This method should only be called once.
*/
public CcCompilationOutputs createCcCompileActions() {
CcCompilationOutputs.Builder result = new CcCompilationOutputs.Builder();
Preconditions.checkNotNull(context);
AnalysisEnvironment env = ruleContext.getAnalysisEnvironment();
if (shouldProvideHeaderModules()) {
Artifact moduleMapArtifact = context.getCppModuleMap().getArtifact();
Label moduleMapLabel = Label.parseAbsoluteUnchecked(context.getCppModuleMap().getName());
CppCompileActionBuilder builder =
initializeCompileAction(moduleMapArtifact, moduleMapLabel, /*forInterface=*/ true);
builder.setSemantics(semantics);
// A header module compile action is just like a normal compile action, but:
// - the compiled source file is the module map
// - it creates a header module (.pcm file).
createSourceAction(
FileSystemUtils.removeExtension(semantics.getEffectiveSourcePath(moduleMapArtifact))
.getPathString(),
result,
env,
moduleMapArtifact,
builder,
ArtifactCategory.CPP_MODULE,
/*addObject=*/ false,
/*enableCoverage=*/ false,
/*generateDwo=*/ false,
CppFileTypes.mustProduceDotdFile(moduleMapArtifact.getFilename()),
ImmutableMap.<String, String>of());
}
for (CppSource source : sourceFiles) {
Artifact sourceArtifact = source.getSource();
Label sourceLabel = source.getLabel();
String outputName = FileSystemUtils.removeExtension(
semantics.getEffectiveSourcePath(sourceArtifact)).getPathString();
CppCompileActionBuilder builder =
initializeCompileAction(sourceArtifact, sourceLabel, /*forInterface=*/ false);
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);
} else {
createSourceAction(
outputName,
result,
env,
sourceArtifact,
builder,
ArtifactCategory.OBJECT_FILE,
/*addObject=*/ true,
isCodeCoverageEnabled(),
/*generateDwo=*/ cppConfiguration.useFission(),
CppFileTypes.mustProduceDotdFile(sourceArtifact.getFilename()),
source.getBuildVariables());
}
}
compilationOutputs = result.build();
return compilationOutputs;
}
private void createHeaderAction(String outputName, Builder result, AnalysisEnvironment env,
CppCompileActionBuilder builder, boolean generateDotd) {
String outputNameBase = CppHelper.getArtifactNameForCategory(ruleContext,
ArtifactCategory.GENERATED_HEADER, outputName);
builder
.setOutputs(ArtifactCategory.PROCESSED_HEADER, outputNameBase, generateDotd)
// If we generate pic actions, we prefer the header actions to use the pic artifacts.
.setPicMode(this.getGeneratePicActions());
setupCompileBuildVariables(
builder,
this.getGeneratePicActions(),
/*ccRelativeName=*/ null,
/*autoFdoImportPath=*/ null,
/*gcnoFile=*/ null,
/*dwoFile=*/ null,
ImmutableMap.<String, String>of());
semantics.finalizeCompileActionBuilder(ruleContext, builder);
CppCompileAction compileAction = builder.build();
env.registerAction(compileAction);
Artifact tokenFile = compileAction.getOutputFile();
result.addHeaderTokenFile(tokenFile);
}
private void createClifMatchAction(
String outputName, Builder result, AnalysisEnvironment env, CppCompileActionBuilder builder) {
builder
.setOutputs(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.
.addMandatoryInputs(mandatoryInputs)
.addAdditionalIncludes(mandatoryInputs);
setupCompileBuildVariables(
builder,
/* usePic=*/ false,
/*ccRelativeName=*/ null,
/*autoFdoImportPath=*/ null,
/*gcnoFile=*/ null,
/*dwoFile=*/ null,
/*sourceSpecificBuildVariables=*/ ImmutableMap.<String, String>of());
semantics.finalizeCompileActionBuilder(ruleContext, builder);
CppCompileAction compileAction = builder.build();
env.registerAction(compileAction);
Artifact tokenFile = compileAction.getOutputFile();
result.addHeaderTokenFile(tokenFile);
}
private void createSourceAction(
String outputName,
CcCompilationOutputs.Builder result,
AnalysisEnvironment env,
Artifact sourceArtifact,
CppCompileActionBuilder builder,
ArtifactCategory outputCategory,
boolean addObject,
boolean enableCoverage,
boolean generateDwo,
boolean generateDotd,
Map<String, String> sourceSpecificBuildVariables) {
PathFragment ccRelativeName = semantics.getEffectiveSourcePath(sourceArtifact);
if (cppConfiguration.isLipoOptimization()) {
// TODO(bazel-team): we shouldn't be needing this, merging context with the binary
// is a superset of necessary information.
LipoContextProvider lipoProvider =
Preconditions.checkNotNull(CppHelper.getLipoContextProvider(ruleContext), outputName);
builder.setContext(CppCompilationContext.mergeForLipo(lipoProvider.getLipoContext(),
context));
}
boolean generatePicAction = getGeneratePicActions();
// If we always need pic for everything, then don't bother to create a no-pic action.
boolean generateNoPicAction = getGenerateNoPicActions();
Preconditions.checkState(generatePicAction || generateNoPicAction);
if (fake) {
boolean usePic = !generateNoPicAction;
createFakeSourceAction(outputName, result, env, builder, outputCategory, addObject,
ccRelativeName, sourceArtifact.getExecPath(), usePic, generateDotd);
} else {
// Create PIC compile actions (same as non-PIC, but use -fPIC and
// generate .pic.o, .pic.d, .pic.gcno instead of .o, .d, .gcno.)
if (generatePicAction) {
String picOutputBase = CppHelper.getArtifactNameForCategory(ruleContext,
ArtifactCategory.PIC_FILE, outputName);
CppCompileActionBuilder picBuilder = copyAsPicBuilder(
builder, picOutputBase, outputCategory, generateDotd);
String gcnoFileName = CppHelper.getArtifactNameForCategory(ruleContext,
ArtifactCategory.COVERAGE_DATA_FILE, picOutputBase);
Artifact gcnoFile = enableCoverage
? CppHelper.getCompileOutputArtifact(ruleContext, gcnoFileName)
: null;
Artifact dwoFile = generateDwo ? getDwoFile(picBuilder.getOutputFile()) : null;
setupCompileBuildVariables(
picBuilder,
/*usePic=*/ true,
ccRelativeName,
sourceArtifact.getExecPath(),
gcnoFile,
dwoFile,
sourceSpecificBuildVariables);
if (maySaveTemps) {
result.addTemps(
createTempsActions(sourceArtifact, outputName, picBuilder, /*usePic=*/true,
/*generateDotd=*/ generateDotd, ccRelativeName));
}
picBuilder.setGcnoFile(gcnoFile);
picBuilder.setDwoFile(dwoFile);
semantics.finalizeCompileActionBuilder(ruleContext, picBuilder);
CppCompileAction picAction = picBuilder.build();
env.registerAction(picAction);
if (addObject) {
result.addPicObjectFile(picAction.getOutputFile());
if (featureConfiguration.isEnabled(CppRuleClasses.THIN_LTO)
&& CppFileTypes.LTO_SOURCE.matches(sourceArtifact.getFilename())) {
result.addLTOBitcodeFile(picAction.getOutputFile());
}
}
if (dwoFile != null) {
// Host targets don't produce .dwo files.
result.addPicDwoFile(dwoFile);
}
if (cppConfiguration.isLipoContextCollector() && !generateNoPicAction) {
result.addLipoScannable(picAction);
}
}
if (generateNoPicAction) {
Artifact noPicOutputFile = CppHelper.getCompileOutputArtifact(ruleContext,
CppHelper.getArtifactNameForCategory(ruleContext, outputCategory, outputName));
builder.setOutputs(outputCategory, outputName, generateDotd);
String gcnoFileName = CppHelper.getArtifactNameForCategory(ruleContext,
ArtifactCategory.COVERAGE_DATA_FILE, outputName);
// Create non-PIC compile actions
Artifact gcnoFile =
!cppConfiguration.isLipoOptimization() && enableCoverage
? CppHelper.getCompileOutputArtifact(ruleContext, gcnoFileName)
: null;
Artifact noPicDwoFile = generateDwo ? getDwoFile(noPicOutputFile) : null;
setupCompileBuildVariables(
builder,
/*usePic=*/ false,
ccRelativeName,
sourceArtifact.getExecPath(),
gcnoFile,
noPicDwoFile,
sourceSpecificBuildVariables);
if (maySaveTemps) {
result.addTemps(
createTempsActions(
sourceArtifact,
outputName,
builder,
/*usePic=*/ false,
/*generateDotd*/ generateDotd,
ccRelativeName));
}
builder.setGcnoFile(gcnoFile);
builder.setDwoFile(noPicDwoFile);
semantics.finalizeCompileActionBuilder(ruleContext, builder);
CppCompileAction compileAction = builder.build();
env.registerAction(compileAction);
Artifact objectFile = compileAction.getOutputFile();
if (addObject) {
result.addObjectFile(objectFile);
if (featureConfiguration.isEnabled(CppRuleClasses.THIN_LTO)
&& CppFileTypes.LTO_SOURCE.matches(sourceArtifact.getFilename())) {
result.addLTOBitcodeFile(objectFile);
}
}
if (noPicDwoFile != null) {
// Host targets don't produce .dwo files.
result.addDwoFile(noPicDwoFile);
}
if (cppConfiguration.isLipoContextCollector()) {
result.addLipoScannable(compileAction);
}
}
}
}
String getOutputNameBaseWith(String base, boolean usePic) {
return usePic
? CppHelper.getArtifactNameForCategory(ruleContext, ArtifactCategory.PIC_FILE, base)
: base;
}
private void createFakeSourceAction(String outputName, CcCompilationOutputs.Builder result,
AnalysisEnvironment env, CppCompileActionBuilder builder,
ArtifactCategory outputCategory, boolean addObject, PathFragment ccRelativeName,
PathFragment execPath, boolean usePic, boolean generateDotd) {
String outputNameBase = getOutputNameBaseWith(outputName, usePic);
String tempOutputName = ruleContext.getConfiguration().getBinFragment()
.getRelative(CppHelper.getObjDirectory(ruleContext.getLabel()))
.getRelative(CppHelper.getArtifactNameForCategory(ruleContext, outputCategory,
getOutputNameBaseWith(outputName + ".temp", usePic)))
.getPathString();
builder
.setPicMode(usePic)
.setOutputs(outputCategory, outputNameBase, generateDotd)
.setTempOutputFile(new PathFragment(tempOutputName));
setupCompileBuildVariables(
builder,
usePic,
ccRelativeName,
execPath,
/*gcnoFile=*/ null,
/*dwoFile=*/ null,
ImmutableMap.<String, String>of());
semantics.finalizeCompileActionBuilder(ruleContext, builder);
CppCompileAction action = builder.build();
env.registerAction(action);
if (addObject) {
if (usePic) {
result.addPicObjectFile(action.getOutputFile());
} else {
result.addObjectFile(action.getOutputFile());
}
}
}
/**
* Returns the linked artifact resulting from a linking of the given type. Consults the feature
* configuration to obtain an action_config that provides the artifact. If the feature
* configuration provides no artifact, uses a default.
*
* <p>We cannot assume that the feature configuration contains an action_config for the link
* action, because the linux link action depends on hardcoded values in
* LinkCommandLine.getRawLinkArgv(), which are applied on the condition that an action_config is
* not present.
* TODO(b/30393154): Assert that the given link action has an action_config.
*
* @throws RuleErrorException
*/
private Artifact getLinkedArtifact(LinkTargetType linkTargetType) throws RuleErrorException {
Artifact result = null;
Artifact linuxDefault = CppHelper.getLinuxLinkedArtifact(ruleContext, linkTargetType);
try {
String maybePicName = ruleContext.getLabel().getName();
if (linkTargetType.picness() == Picness.PIC) {
maybePicName = CppHelper.getArtifactNameForCategory(
ruleContext, ArtifactCategory.PIC_FILE, maybePicName);
}
String linkedName = CppHelper.getArtifactNameForCategory(
ruleContext, linkTargetType.getLinkerOutput(), maybePicName);
PathFragment artifactFragment = new PathFragment(ruleContext.getLabel().getName())
.getParentDirectory().getRelative(linkedName);
result = ruleContext.getBinArtifact(artifactFragment);
} catch (ExpansionException e) {
ruleContext.throwWithRuleError(e.getMessage());
}
// If the linked artifact is not the linux default, then a FailAction is generated for the
// linux default to satisfy the requirement of the implicit output.
// TODO(b/30132703): Remove the implicit outputs of cc_library.
if (!result.equals(linuxDefault)) {
ruleContext.registerAction(
new FailAction(
ruleContext.getActionOwner(),
ImmutableList.of(linuxDefault),
String.format(
"the given toolchain supports creation of %s instead of %s",
linuxDefault.getExecPathString(), result.getExecPathString())));
}
return result;
}
/**
* Constructs the C++ linker actions. It generally generates two actions, one for a static library
* and one for a dynamic library. If PIC is required for shared libraries, but not for binaries,
* it additionally creates a third action to generate a PIC static library.
*
* <p>For dynamic libraries, this method can additionally create an interface shared library that
* can be used for linking, but doesn't contain any executable code. This increases the number of
* cache hits for link actions. Call {@link #setAllowInterfaceSharedObjects(boolean)} to enable
* this behavior.
*
* @throws RuleErrorException
*/
public CcLinkingOutputs createCcLinkActions(
CcCompilationOutputs ccOutputs, Iterable<Artifact> nonCodeLinkerInputs)
throws RuleErrorException, InterruptedException {
// For now only handle static links. Note that the dynamic library link below ignores linkType.
// TODO(bazel-team): Either support non-static links or move this check to setLinkType().
Preconditions.checkState(
linkType.staticness() == Staticness.STATIC, "can only handle static links");
CcLinkingOutputs.Builder result = new CcLinkingOutputs.Builder();
if (cppConfiguration.isLipoContextCollector()) {
// Don't try to create LIPO link actions in collector mode,
// because it needs some data that's not available at this point.
return result.build();
}
AnalysisEnvironment env = ruleContext.getAnalysisEnvironment();
boolean usePicForBinaries = CppHelper.usePic(ruleContext, true);
boolean usePicForSharedLibs = CppHelper.usePic(ruleContext, false);
// Create static library (.a). The linkType only reflects whether the library is alwayslink or
// not. The PIC-ness is determined by whether we need to use PIC or not. There are three cases
// for (usePicForSharedLibs usePicForBinaries):
//
// (1) (false false) -> no pic code
// (2) (true false) -> shared libraries as pic, but not binaries
// (3) (true true) -> both shared libraries and binaries as pic
//
// In case (3), we always need PIC, so only create one static library containing the PIC object
// files. The name therefore does not match the content.
//
// Presumably, it is done this way because the .a file is an implicit output of every cc_library
// rule, so we can't use ".pic.a" that in the always-PIC case.
// If the crosstool is configured to select an output artifact, we use that selection.
// Otherwise, we use linux defaults.
Artifact linkedArtifact = getLinkedArtifact(linkType);
PathFragment labelName = new PathFragment(ruleContext.getLabel().getName());
String libraryIdentifier = ruleContext.getPackageDirectory().getRelative(
labelName.replaceName("lib" + labelName.getBaseName())).getPathString();
CppLinkAction maybePicAction =
newLinkActionBuilder(linkedArtifact)
.addObjectFiles(ccOutputs.getObjectFiles(usePicForBinaries))
.addNonCodeInputs(nonCodeLinkerInputs)
.addLTOBitcodeFiles(ccOutputs.getLtoBitcodeFiles())
.setLinkType(linkType)
.setLinkStaticness(LinkStaticness.FULLY_STATIC)
.addActionInputs(linkActionInputs)
.setLibraryIdentifier(libraryIdentifier)
.addVariablesExtensions(variablesExtensions)
.setFeatureConfiguration(featureConfiguration)
.build();
env.registerAction(maybePicAction);
if (linkType != LinkTargetType.EXECUTABLE) {
result.addStaticLibrary(maybePicAction.getOutputLibrary());
}
// Create a second static library (.pic.a). Only in case (2) do we need both PIC and non-PIC
// static libraries. In that case, the first static library contains the non-PIC code, and this
// one contains the PIC code, so the names match the content.
if (!usePicForBinaries && usePicForSharedLibs) {
LinkTargetType picLinkType = (linkType == LinkTargetType.ALWAYS_LINK_STATIC_LIBRARY)
? LinkTargetType.ALWAYS_LINK_PIC_STATIC_LIBRARY
: LinkTargetType.PIC_STATIC_LIBRARY;
// 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))
.addLTOBitcodeFiles(ccOutputs.getLtoBitcodeFiles())
.setLinkType(picLinkType)
.setLinkStaticness(LinkStaticness.FULLY_STATIC)
.addActionInputs(linkActionInputs)
.setLibraryIdentifier(libraryIdentifier)
.addVariablesExtensions(variablesExtensions)
.setFeatureConfiguration(featureConfiguration)
.build();
env.registerAction(picAction);
if (linkType != LinkTargetType.EXECUTABLE) {
result.addPicStaticLibrary(picAction.getOutputLibrary());
}
}
if (!createDynamicLibrary) {
return result.build();
}
// Create dynamic library.
Artifact soImpl;
String mainLibraryIdentifier;
if (soImplArtifact == null) {
// If the crosstool is configured to select an output artifact, we use that selection.
// Otherwise, we use linux defaults.
soImpl = getLinkedArtifact(LinkTargetType.DYNAMIC_LIBRARY);
mainLibraryIdentifier = libraryIdentifier;
} else {
// This branch is only used for vestigial Google-internal rules where the name of the output
// file is explicitly specified in the BUILD file and as such, is platform-dependent. Thus,
// we just hardcode some reasonable logic to compute the library identifier and hope that this
// will eventually go away.
soImpl = soImplArtifact;
mainLibraryIdentifier = FileSystemUtils.removeExtension(
soImpl.getRootRelativePath().getPathString());
}
List<String> sonameLinkopts = ImmutableList.of();
Artifact soInterface = null;
if (cppConfiguration.useInterfaceSharedObjects() && allowInterfaceSharedObjects) {
soInterface =
CppHelper.getLinuxLinkedArtifact(ruleContext, LinkTargetType.INTERFACE_DYNAMIC_LIBRARY);
sonameLinkopts = ImmutableList.of("-Wl,-soname=" +
SolibSymlinkAction.getDynamicLibrarySoname(soImpl.getRootRelativePath(), false));
}
// Should we also link in any libraries that this library depends on?
// That is required on some systems...
CppLinkActionBuilder linkActionBuilder =
newLinkActionBuilder(soImpl)
.setInterfaceOutput(soInterface)
.addObjectFiles(ccOutputs.getObjectFiles(usePicForSharedLibs))
.addNonCodeInputs(ccOutputs.getHeaderTokenFiles())
.addLTOBitcodeFiles(ccOutputs.getLtoBitcodeFiles())
.setLinkType(LinkTargetType.DYNAMIC_LIBRARY)
.setLinkStaticness(LinkStaticness.DYNAMIC)
.addActionInputs(linkActionInputs)
.setLibraryIdentifier(mainLibraryIdentifier)
.addLinkopts(linkopts)
.addLinkopts(sonameLinkopts)
.setRuntimeInputs(
ArtifactCategory.DYNAMIC_LIBRARY,
CppHelper.getToolchain(ruleContext).getDynamicRuntimeLinkMiddleman(),
CppHelper.getToolchain(ruleContext).getDynamicRuntimeLinkInputs())
.setFeatureConfiguration(featureConfiguration)
.addVariablesExtensions(variablesExtensions);
if (!ccOutputs.getLtoBitcodeFiles().isEmpty()
&& featureConfiguration.isEnabled(CppRuleClasses.THIN_LTO)) {
linkActionBuilder.setLTOIndexing(true);
CppLinkAction indexAction = linkActionBuilder.build();
env.registerAction(indexAction);
for (LTOBackendArtifacts ltoArtifacts : indexAction.getAllLTOBackendArtifacts()) {
ltoArtifacts.scheduleLTOBackendAction(
ruleContext, featureConfiguration, usePicForSharedLibs);
}
linkActionBuilder.setLTOIndexing(false);
}
CppLinkAction action = linkActionBuilder.build();
env.registerAction(action);
if (linkType == LinkTargetType.EXECUTABLE) {
return result.build();
}
LibraryToLink dynamicLibrary = action.getOutputLibrary();
LibraryToLink interfaceLibrary = action.getInterfaceOutputLibrary();
if (interfaceLibrary == null) {
interfaceLibrary = dynamicLibrary;
}
// If shared library has neverlink=1, then leave it untouched. Otherwise,
// create a mangled symlink for it and from now on reference it through
// mangled name only.
if (neverLink) {
result.addDynamicLibrary(interfaceLibrary);
result.addExecutionDynamicLibrary(dynamicLibrary);
} else {
Artifact libraryLink = SolibSymlinkAction.getDynamicLibrarySymlink(
ruleContext, interfaceLibrary.getArtifact(), false, false,
ruleContext.getConfiguration());
result.addDynamicLibrary(LinkerInputs.solibLibraryToLink(
libraryLink, interfaceLibrary.getArtifact(), libraryIdentifier));
Artifact implLibraryLink = SolibSymlinkAction.getDynamicLibrarySymlink(
ruleContext, dynamicLibrary.getArtifact(), false, false,
ruleContext.getConfiguration());
result.addExecutionDynamicLibrary(LinkerInputs.solibLibraryToLink(
implLibraryLink, dynamicLibrary.getArtifact(), libraryIdentifier));
}
return result.build();
}
private CppLinkActionBuilder newLinkActionBuilder(Artifact outputArtifact) {
return new CppLinkActionBuilder(ruleContext, outputArtifact)
.setCrosstoolInputs(CppHelper.getToolchain(ruleContext).getLink())
.addNonCodeInputs(context.getTransitiveCompilationPrerequisites());
}
/**
* Creates a basic cpp compile action builder for source file. Configures options,
* crosstool inputs, output and dotd file names, compilation context and copts.
*/
private CppCompileActionBuilder createCompileActionBuilder(
Artifact source, Label label, boolean forInterface) {
CppCompileActionBuilder builder = new CppCompileActionBuilder(
ruleContext, source, label);
builder.setContext(forInterface ? interfaceContext : context).addCopts(copts);
builder.addEnvironment(CppHelper.getToolchain(ruleContext).getEnvironment());
return builder;
}
/**
* Creates cpp PIC compile action builder from the given builder by adding necessary copt and
* changing output and dotd file names.
*/
private CppCompileActionBuilder copyAsPicBuilder(CppCompileActionBuilder builder,
String outputName, ArtifactCategory outputCategory,
boolean generateDotd) {
CppCompileActionBuilder picBuilder = new CppCompileActionBuilder(builder);
picBuilder
.setPicMode(true)
.setOutputs(outputCategory, outputName, generateDotd);
return picBuilder;
}
/**
* Create the actions for "--save_temps".
*/
private ImmutableList<Artifact> createTempsActions(Artifact source, String outputName,
CppCompileActionBuilder builder, boolean usePic, boolean generateDotd,
PathFragment ccRelativeName) {
if (!cppConfiguration.getSaveTemps()) {
return ImmutableList.of();
}
String path = source.getFilename();
boolean isCFile = CppFileTypes.C_SOURCE.matches(path);
boolean isCppFile = CppFileTypes.CPP_SOURCE.matches(path);
if (!isCFile && !isCppFile) {
return ImmutableList.of();
}
ArtifactCategory category = isCFile
? ArtifactCategory.PREPROCESSED_C_SOURCE : ArtifactCategory.PREPROCESSED_CPP_SOURCE;
String outputArtifactNameBase = getOutputNameBaseWith(outputName, usePic);
CppCompileActionBuilder dBuilder = new CppCompileActionBuilder(builder);
dBuilder.setOutputs(category, outputArtifactNameBase, generateDotd);
setupCompileBuildVariables(
dBuilder,
usePic,
ccRelativeName,
source.getExecPath(),
null,
null,
ImmutableMap.<String, String>of());
semantics.finalizeCompileActionBuilder(ruleContext, dBuilder);
CppCompileAction dAction = dBuilder.build();
ruleContext.registerAction(dAction);
CppCompileActionBuilder sdBuilder = new CppCompileActionBuilder(builder);
sdBuilder.setOutputs(ArtifactCategory.GENERATED_ASSEMBLY, outputArtifactNameBase, generateDotd);
setupCompileBuildVariables(
sdBuilder,
usePic,
ccRelativeName,
source.getExecPath(),
null,
null,
ImmutableMap.<String, String>of());
semantics.finalizeCompileActionBuilder(ruleContext, sdBuilder);
CppCompileAction sdAction = sdBuilder.build();
ruleContext.registerAction(sdAction);
return ImmutableList.of(
dAction.getOutputFile(),
sdAction.getOutputFile());
}
/**
* Returns true iff code coverage is enabled for the given target.
*/
private boolean isCodeCoverageEnabled() {
if (configuration.isCodeCoverageEnabled()) {
// If rule is matched by the instrumentation filter, enable instrumentation
if (InstrumentedFilesCollector.shouldIncludeLocalSources(ruleContext)) {
return true;
}
// At this point the rule itself is not matched by the instrumentation filter. However, we
// might still want to instrument C++ rules if one of the targets listed in "deps" is
// instrumented and, therefore, can supply header files that we would want to collect code
// coverage for. For example, think about cc_test rule that tests functionality defined in a
// header file that is supplied by the cc_library.
//
// Note that we only check direct prerequisites and not the transitive closure. This is done
// for two reasons:
// a) It is a good practice to declare libraries which you directly rely on. Including headers
// from a library hidden deep inside the transitive closure makes build dependencies less
// readable and can lead to unexpected breakage.
// b) Traversing the transitive closure for each C++ compile action would require more complex
// implementation (with caching results of this method) to avoid O(N^2) slowdown.
if (ruleContext.getRule().isAttrDefined("deps", BuildType.LABEL_LIST)) {
for (TransitiveInfoCollection dep : ruleContext.getPrerequisites("deps", Mode.TARGET)) {
if (dep.getProvider(CppCompilationContext.class) != null
&& InstrumentedFilesCollector.shouldIncludeLocalSources(configuration, dep)) {
return true;
}
}
}
}
return false;
}
}