blob: ec174b1fc5ccedd6af44900a08035978c54c5351 [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.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.FailAction;
import com.google.devtools.build.lib.analysis.AnalysisEnvironment;
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.analysis.config.PerLabelOptions;
import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget.Mode;
import com.google.devtools.build.lib.analysis.test.InstrumentedFilesCollector;
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.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.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* 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 {
/** Name of the build variable for the path to the source file being compiled. */
public static final String SOURCE_FILE_VARIABLE_NAME = "source_file";
/** Name of the build variable for the path to the input file being processed. */
public static final String INPUT_FILE_VARIABLE_NAME = "input_file";
/** Name of the build variable for the path to the compilation output file. */
public static final String OUTPUT_FILE_VARIABLE_NAME = "output_file";
/**
* Name of the build variable for the path to the compilation output file in case of assembly
* source.
*/
public static final String OUTPUT_ASSEMBLY_FILE_VARIABLE_NAME = "output_assembly_file";
/**
* Name of the build variable for the path to the compilation output file in case of preprocessed
* source.
*/
public static final String OUTPUT_PREPROCESS_FILE_VARIABLE_NAME = "output_preprocess_file";
/** Name of the build variable for the path to the output file when output is an object file. */
public static final String OUTPUT_OBJECT_FILE_VARIABLE_NAME = "output_object_file";
/** Name of the build variable for the module file name. */
public static final String MODULE_NAME_VARIABLE_NAME = "module_name";
/** Name of the build variable for the module map file name. */
public static final String MODULE_MAP_FILE_VARIABLE_NAME = "module_map_file";
/** Name of the build variable for the dependent module map file name. */
public static final String DEPENDENT_MODULE_MAP_FILES_VARIABLE_NAME =
"dependent_module_map_files";
/** Name of the build variable for the collection of module files. */
public static final String MODULE_FILES_VARIABLE_NAME = "module_files";
/**
* Name of the build variable for includes that compiler needs to include into sources to be
* compiled.
*/
public static final String INCLUDES_VARIABLE_NAME = "includes";
/**
* Name of the build variable for the collection of include paths.
*
* @see CppCompilationContext#getIncludeDirs().
*/
public static final String INCLUDE_PATHS_VARIABLE_NAME = "include_paths";
/**
* Name of the build variable for the collection of quote include paths.
*
* @see CppCompilationContext#getIncludeDirs().
*/
public static final String QUOTE_INCLUDE_PATHS_VARIABLE_NAME = "quote_include_paths";
/**
* Name of the build variable for the collection of system include paths.
*
* @see CppCompilationContext#getIncludeDirs().
*/
public static final String SYSTEM_INCLUDE_PATHS_VARIABLE_NAME = "system_include_paths";
/** Name of the build variable for the dependency file path */
public static final String DEPENDENCY_FILE_VARIABLE_NAME = "dependency_file";
/** Name of the build variable for the collection of macros defined for preprocessor. */
public static final String PREPROCESSOR_DEFINES_VARIABLE_NAME = "preprocessor_defines";
/** Name of the build variable present when the output is compiled as position independent. */
public static final String PIC_VARIABLE_NAME = "pic";
/** Name of the build variable for the gcov coverage file path. */
public static final String GCOV_GCNO_FILE_VARIABLE_NAME = "gcov_gcno_file";
/** Name of the build variable for the per object debug info file. */
public static final String PER_OBJECT_DEBUG_INFO_FILE_VARIABLE_NAME =
"per_object_debug_info_file";
/** Name of the build variable for the LTO indexing bitcode file. */
public static final String LTO_INDEXING_BITCODE_FILE_VARIABLE_NAME = "lto_indexing_bitcode_file";
/** Name of the build variable for stripopts for the strip action. */
public static final String STRIPOPTS_VARIABLE_NAME = "stripopts";
/**
* Build variable for all flags coming from legacy crosstool fields, such as compiler_flag,
* optional_compiler_flag, cxx_flag, optional_cxx_flag.
*/
public static final String LEGACY_COMPILE_FLAGS_VARIABLE_NAME = "legacy_compile_flags";
/**
* Build variable for all flags coming from copt rule attribute, and from --copt, --cxxopt, or
* --conlyopt options.
*/
public static final String USER_COMPILE_FLAGS_VARIABLE_NAME = "user_compile_flags";
/** Build variable for flags coming from unfiltered_cxx_flag CROSSTOOL fields. */
public static final String UNFILTERED_COMPILE_FLAGS_VARIABLE_NAME = "unfiltered_compile_flags";
/** Name of the build variable for the sysroot path variable name. */
public static final String SYSROOT_VARIABLE_NAME = "sysroot";
private final CppSemantics semantics;
private final RuleContext ruleContext;
private final BuildConfiguration configuration;
private final CppConfiguration cppConfiguration;
// compile model
private CppCompilationContext context;
private final Set<CppSource> sourceFiles = new LinkedHashSet<>();
private final List<Artifact> mandatoryInputs = new ArrayList<>();
private final ImmutableList<String> copts;
private final Predicate<String> coptsFilter;
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<>();
private final CcToolchainProvider ccToolchain;
private final FdoSupportProvider fdoSupport;
private String linkedArtifactNameSuffix = "";
private final ImmutableSet<String> features;
public CppModel(
RuleContext ruleContext,
CppSemantics semantics,
CcToolchainProvider ccToolchain,
FdoSupportProvider fdoSupport,
ImmutableList<String> copts) {
this(
ruleContext,
semantics,
ccToolchain,
fdoSupport,
ruleContext.getConfiguration(),
copts,
Predicates.alwaysTrue());
}
public CppModel(
RuleContext ruleContext,
CppSemantics semantics,
CcToolchainProvider ccToolchain,
FdoSupportProvider fdoSupport,
ImmutableList<String> copts,
Predicate<String> coptsFilter) {
this(
ruleContext,
semantics,
ccToolchain,
fdoSupport,
ruleContext.getConfiguration(),
copts,
coptsFilter);
}
public CppModel(
RuleContext ruleContext,
CppSemantics semantics,
CcToolchainProvider ccToolchain,
FdoSupportProvider fdoSupport,
BuildConfiguration configuration,
ImmutableList<String> copts,
Predicate<String> coptsFilter) {
this.ruleContext = Preconditions.checkNotNull(ruleContext);
this.semantics = semantics;
this.ccToolchain = Preconditions.checkNotNull(ccToolchain);
this.fdoSupport = Preconditions.checkNotNull(fdoSupport);
this.configuration = configuration;
this.copts = copts;
this.coptsFilter = Preconditions.checkNotNull(coptsFilter);
cppConfiguration = ruleContext.getFragment(CppConfiguration.class);
features = ruleContext.getFeatures();
}
private Artifact getDwoFile(Artifact outputFile) {
return ruleContext.getRelatedArtifact(outputFile.getRootRelativePath(), ".dwo");
}
private Artifact getLtoIndexingFile(Artifact outputFile) {
String ext = Iterables.getOnlyElement(CppFileTypes.LTO_INDEXING_OBJECT_FILE.getExtensions());
return ruleContext.getRelatedArtifact(outputFile.getRootRelativePath(), ext);
}
/**
* 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<Artifact> sourceFiles, Label sourceLabel, Map<String, String> 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<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 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;
}
/*
* Adds a suffix for paths of linked artifacts. Normally their paths are derived solely from rule
* labels. In the case of multiple callers (e.g., aspects) acting on a single rule, they may
* generate the same linked artifact and therefore lead to artifact conflicts. This method
* provides a way to avoid this artifact conflict by allowing different callers acting on the same
* rule to provide a suffix that will be used to scope their own linked artifacts.
*/
public CppModel setLinkedArtifactNameSuffix(String suffix) {
this.linkedArtifactNameSuffix = suffix;
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) {
CppCompileActionBuilder builder = createCompileActionBuilder(sourceArtifact);
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();
}
/**
* Supplier that computes unfiltered_compile_flags lazily at the execution phase.
*
* <p>Dear friends of the lambda, this method exists to limit the scope of captured variables
* only to arguments (to prevent accidental capture of enclosing instance which could regress
* memory).
*/
public static Supplier<ImmutableList<String>> getUnfilteredCompileFlagsSupplier(
CcToolchainProvider ccToolchain, ImmutableSet<String> features) {
return () -> ccToolchain.getUnfilteredCompilerOptions(features);
}
/**
* Supplier that computes legacy_compile_flags lazily at the execution phase.
*
* <p>Dear friends of the lambda, this method exists to limit the scope of captured variables
* only to arguments (to prevent accidental capture of enclosing instance which could regress
* memory).
*/
public static Supplier<ImmutableList<String>> getLegacyCompileFlagsSupplier(
CppConfiguration cppConfiguration, String sourceFilename, ImmutableSet<String> features) {
return () -> cppConfiguration.collectLegacyCompileFlags(sourceFilename, features);
}
private void setupCompileBuildVariables(
CppCompileActionBuilder builder,
Label sourceLabel,
boolean usePic,
PathFragment ccRelativeName,
PathFragment autoFdoImportPath,
Artifact gcnoFile,
Artifact dwoFile,
Artifact ltoIndexingFile,
CppModuleMap cppModuleMap,
Map<String, String> sourceSpecificBuildVariables) {
CcToolchainFeatures.Variables.Builder buildVariables =
new CcToolchainFeatures.Variables.Builder(ccToolchain.getBuildVariables());
CppCompilationContext builderContext = builder.getContext();
Artifact sourceFile = builder.getSourceFile();
Artifact outputFile = builder.getOutputFile();
String realOutputFilePath;
buildVariables.addStringVariable(SOURCE_FILE_VARIABLE_NAME, sourceFile.getExecPathString());
buildVariables.addStringVariable(OUTPUT_FILE_VARIABLE_NAME, outputFile.getExecPathString());
buildVariables.addStringSequenceVariable(
USER_COMPILE_FLAGS_VARIABLE_NAME,
ImmutableList.<String>builder()
.addAll(copts)
.addAll(collectPerFileCopts(sourceFile, sourceLabel))
.build());
String sourceFilename = sourceFile.getExecPathString();
buildVariables.addLazyStringSequenceVariable(
LEGACY_COMPILE_FLAGS_VARIABLE_NAME,
getLegacyCompileFlagsSupplier(cppConfiguration, sourceFilename, features));
if (!CppFileTypes.OBJC_SOURCE.matches(sourceFilename)
&& !CppFileTypes.OBJCPP_SOURCE.matches(sourceFilename)) {
buildVariables.addLazyStringSequenceVariable(
UNFILTERED_COMPILE_FLAGS_VARIABLE_NAME,
getUnfilteredCompileFlagsSupplier(ccToolchain, features));
}
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_VARIABLE_NAME, 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_VARIABLE_NAME, realOutputFilePath);
} else {
buildVariables.addStringVariable(OUTPUT_OBJECT_FILE_VARIABLE_NAME, realOutputFilePath);
}
DotdFile dotdFile =
isGenerateDotdFile(sourceFile) ? Preconditions.checkNotNull(builder.getDotdFile()) : null;
// Set dependency_file to enable <object>.d file generation.
if (dotdFile != null) {
buildVariables.addStringVariable(
DEPENDENCY_FILE_VARIABLE_NAME, 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_VARIABLE_NAME, cppModuleMap.getName());
buildVariables.addStringVariable(
MODULE_MAP_FILE_VARIABLE_NAME, cppModuleMap.getArtifact().getExecPathString());
StringSequenceBuilder sequence = new StringSequenceBuilder();
for (Artifact artifact : builderContext.getDirectModuleMaps()) {
sequence.addValue(artifact.getExecPathString());
}
buildVariables.addCustomBuiltVariable(DEPENDENT_MODULE_MAP_FILES_VARIABLE_NAME, sequence);
}
if (featureConfiguration.isEnabled(CppRuleClasses.USE_HEADER_MODULES)) {
// Module inputs will be set later when the action is executed.
buildVariables.addStringSequenceVariable(MODULE_FILES_VARIABLE_NAME, ImmutableSet.of());
}
if (featureConfiguration.isEnabled(CppRuleClasses.INCLUDE_PATHS)) {
buildVariables.addStringSequenceVariable(
INCLUDE_PATHS_VARIABLE_NAME, getSafePathStrings(builderContext.getIncludeDirs()));
buildVariables.addStringSequenceVariable(
QUOTE_INCLUDE_PATHS_VARIABLE_NAME,
getSafePathStrings(builderContext.getQuoteIncludeDirs()));
buildVariables.addStringSequenceVariable(
SYSTEM_INCLUDE_PATHS_VARIABLE_NAME,
getSafePathStrings(builderContext.getSystemIncludeDirs()));
}
if (featureConfiguration.isEnabled(CppRuleClasses.PREPROCESSOR_DEFINES)) {
String fdoBuildStamp = CppHelper.getFdoBuildStamp(ruleContext, fdoSupport.getFdoSupport());
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, fdoSupport.getFdoSupport()) + "\"")
.build();
} else {
defines = builderContext.getDefines();
}
buildVariables.addStringSequenceVariable(PREPROCESSOR_DEFINES_VARIABLE_NAME, defines);
}
if (usePic) {
if (!featureConfiguration.isEnabled(CppRuleClasses.PIC)) {
ruleContext.ruleError("PIC compilation is requested but the toolchain does not support it");
}
buildVariables.addStringVariable(PIC_VARIABLE_NAME, "");
}
if (ccRelativeName != null) {
fdoSupport.getFdoSupport().configureCompilation(
builder, buildVariables, ruleContext, ccRelativeName, autoFdoImportPath, usePic,
featureConfiguration, fdoSupport);
}
if (gcnoFile != null) {
buildVariables.addStringVariable(GCOV_GCNO_FILE_VARIABLE_NAME, gcnoFile.getExecPathString());
}
if (dwoFile != null) {
buildVariables.addStringVariable(
PER_OBJECT_DEBUG_INFO_FILE_VARIABLE_NAME, dwoFile.getExecPathString());
}
if (ltoIndexingFile != null) {
buildVariables.addStringVariable(
LTO_INDEXING_BITCODE_FILE_VARIABLE_NAME, ltoIndexingFile.getExecPathString());
}
buildVariables.addAllStringVariables(sourceSpecificBuildVariables);
for (VariablesExtension extension : variablesExtensions) {
extension.addVariables(buildVariables);
}
CcToolchainFeatures.Variables variables = buildVariables.build();
builder.setVariables(variables);
}
private ImmutableList<String> collectPerFileCopts(Artifact sourceFile, Label sourceLabel) {
return cppConfiguration
.getPerFileCopts()
.stream()
.filter(
perLabelOptions ->
(sourceLabel != null && perLabelOptions.isIncluded(sourceLabel))
|| perLabelOptions.isIncluded(sourceFile))
.map(PerLabelOptions::getOptions)
.flatMap(options -> options.stream())
.collect(ImmutableList.toImmutableList());
}
/** Returns true if Dotd file should be generated. */
private boolean isGenerateDotdFile(Artifact sourceArtifact) {
return CppFileTypes.headerDiscoveryRequired(sourceArtifact)
&& !featureConfiguration.isEnabled(CppRuleClasses.PARSE_SHOWINCLUDES);
}
/**
* 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() throws RuleErrorException {
CcCompilationOutputs.Builder result = new CcCompilationOutputs.Builder();
Preconditions.checkNotNull(context);
AnalysisEnvironment env = ruleContext.getAnalysisEnvironment();
if (shouldProvideHeaderModules()) {
Label moduleMapLabel = Label.parseAbsoluteUnchecked(context.getCppModuleMap().getName());
Collection<Artifact> modules = createModuleAction(result, context.getCppModuleMap());
if (featureConfiguration.isEnabled(CppRuleClasses.HEADER_MODULE_CODEGEN)) {
for (Artifact module : modules) {
// TODO(djasper): Investigate whether we need to use a label separate from that of the
// module map. It is used for per-file-copts.
createModuleCodegenAction(result, moduleMapLabel, module);
}
}
} else if (context.getVerificationModuleMap() != null) {
Collection<Artifact> modules = createModuleAction(result, context.getVerificationModuleMap());
for (Artifact module : modules) {
result.addHeaderTokenFile(module);
}
}
for (CppSource source : sourceFiles) {
Artifact sourceArtifact = source.getSource();
Label sourceLabel = source.getLabel();
String outputName = FileSystemUtils.removeExtension(
semantics.getEffectiveSourcePath(sourceArtifact)).getPathString();
CppCompileActionBuilder builder = initializeCompileAction(sourceArtifact);
builder.setSemantics(semantics);
if (!sourceArtifact.isTreeArtifact()) {
switch (source.getType()) {
case HEADER:
createHeaderAction(
sourceLabel, outputName, result, env, builder, isGenerateDotdFile(sourceArtifact));
break;
case CLIF_INPUT_PROTO:
createClifMatchAction(sourceLabel, outputName, result, env, builder);
break;
default:
boolean bitcodeOutput =
featureConfiguration.isEnabled(CppRuleClasses.THIN_LTO)
&& CppFileTypes.LTO_SOURCE.matches(sourceArtifact.getFilename());
createSourceAction(
sourceLabel,
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,
isGenerateDotdFile(sourceArtifact),
source.getBuildVariables());
break;
}
} else {
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");
}
}
}
compilationOutputs = result.build();
return compilationOutputs;
}
private void createHeaderAction(
Label sourceLabel,
String outputName,
Builder result,
AnalysisEnvironment env,
CppCompileActionBuilder builder,
boolean generateDotd)
throws RuleErrorException {
String outputNameBase = CppHelper.getArtifactNameForCategory(
ruleContext, ccToolchain, ArtifactCategory.GENERATED_HEADER, outputName);
builder
.setOutputs(ruleContext, ArtifactCategory.PROCESSED_HEADER, outputNameBase, generateDotd)
// If we generate pic actions, we prefer the header actions to use the pic artifacts.
.setPicMode(getGeneratePicActions());
setupCompileBuildVariables(
builder,
sourceLabel,
this.getGeneratePicActions(),
/* ccRelativeName= */ null,
/* autoFdoImportPath= */ null,
/* gcnoFile= */ null,
/* dwoFile= */ null,
/* ltoIndexingFile= */ null,
builder.getContext().getCppModuleMap(),
/* sourceSpecificBuildVariables= */ ImmutableMap.of());
semantics.finalizeCompileActionBuilder(
ruleContext,
builder,
featureConfiguration.getFeatureSpecification(),
coptsFilter,
features);
CppCompileAction compileAction = builder.buildOrThrowRuleError(ruleContext);
env.registerAction(compileAction);
Artifact tokenFile = compileAction.getOutputFile();
result.addHeaderTokenFile(tokenFile);
}
private void createModuleCodegenAction(
CcCompilationOutputs.Builder result, Label sourceLabel, Artifact module)
throws RuleErrorException {
if (fake) {
// We can't currently foresee a situation where we'd want nocompile tests for module codegen.
// If we find one, support needs to be added here.
return;
}
String outputName = semantics.getEffectiveSourcePath(module).getPathString();
// TODO(djasper): Make this less hacky after refactoring how the PIC/noPIC actions are created.
boolean pic = module.getFilename().contains(".pic.");
CppCompileActionBuilder builder = initializeCompileAction(module);
builder.setSemantics(semantics);
builder.setPicMode(pic);
builder.setOutputs(
ruleContext, ArtifactCategory.OBJECT_FILE, outputName, isGenerateDotdFile(module));
PathFragment ccRelativeName = semantics.getEffectiveSourcePath(module);
String gcnoFileName =
CppHelper.getArtifactNameForCategory(
ruleContext, ccToolchain, ArtifactCategory.COVERAGE_DATA_FILE, outputName);
// TODO(djasper): This is now duplicated. Refactor the various create..Action functions.
Artifact gcnoFile =
isCodeCoverageEnabled() && !cppConfiguration.isLipoOptimization()
? CppHelper.getCompileOutputArtifact(ruleContext, gcnoFileName, configuration)
: null;
boolean generateDwo = cppConfiguration.useFission();
Artifact dwoFile = generateDwo ? getDwoFile(builder.getOutputFile()) : null;
// TODO(tejohnson): Add support for ThinLTO if needed.
boolean bitcodeOutput =
featureConfiguration.isEnabled(CppRuleClasses.THIN_LTO)
&& CppFileTypes.LTO_SOURCE.matches(module.getFilename());
Preconditions.checkState(!bitcodeOutput);
setupCompileBuildVariables(
builder,
sourceLabel,
/* usePic= */ pic,
ccRelativeName,
module.getExecPath(),
gcnoFile,
dwoFile,
/* ltoIndexingFile= */ null,
builder.getContext().getCppModuleMap(),
/* sourceSpecificBuildVariables= */ ImmutableMap.of());
builder.setGcnoFile(gcnoFile);
builder.setDwoFile(dwoFile);
semantics.finalizeCompileActionBuilder(
ruleContext,
builder,
featureConfiguration.getFeatureSpecification(),
coptsFilter,
features);
CppCompileAction compileAction = builder.buildOrThrowRuleError(ruleContext);
AnalysisEnvironment env = ruleContext.getAnalysisEnvironment();
env.registerAction(compileAction);
Artifact objectFile = compileAction.getOutputFile();
if (pic) {
result.addPicObjectFile(objectFile);
} else {
result.addObjectFile(objectFile);
}
}
private Collection<Artifact> createModuleAction(
CcCompilationOutputs.Builder result, CppModuleMap cppModuleMap) throws RuleErrorException {
AnalysisEnvironment env = ruleContext.getAnalysisEnvironment();
Artifact moduleMapArtifact = cppModuleMap.getArtifact();
CppCompileActionBuilder builder = initializeCompileAction(moduleMapArtifact);
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).
return createSourceAction(
Label.parseAbsoluteUnchecked(cppModuleMap.getName()),
FileSystemUtils.removeExtension(semantics.getEffectiveSourcePath(moduleMapArtifact))
.getPathString(),
result,
env,
moduleMapArtifact,
builder,
ArtifactCategory.CPP_MODULE,
cppModuleMap,
/* addObject= */ false,
/* enableCoverage= */ false,
/* generateDwo= */ false,
isGenerateDotdFile(moduleMapArtifact),
ImmutableMap.of());
}
private void createClifMatchAction(
Label sourceLabel,
String outputName,
Builder result,
AnalysisEnvironment env,
CppCompileActionBuilder builder)
throws RuleErrorException {
builder
.setOutputs(
ruleContext, ArtifactCategory.CLIF_OUTPUT_PROTO, outputName, /* generateDotd= */ true)
.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,
sourceLabel,
/* usePic= */ false,
/* ccRelativeName= */ null,
/* autoFdoImportPath= */ null,
/* gcnoFile= */ null,
/* dwoFile= */ null,
/* ltoIndexingFile= */ null,
builder.getContext().getCppModuleMap(),
/* sourceSpecificBuildVariables= */ ImmutableMap.of());
semantics.finalizeCompileActionBuilder(
ruleContext,
builder,
featureConfiguration.getFeatureSpecification(),
coptsFilter,
features);
CppCompileAction compileAction = builder.buildOrThrowRuleError(ruleContext);
env.registerAction(compileAction);
Artifact tokenFile = compileAction.getOutputFile();
result.addHeaderTokenFile(tokenFile);
}
private Collection<Artifact> createSourceAction(
Label sourceLabel,
String outputName,
CcCompilationOutputs.Builder result,
AnalysisEnvironment env,
Artifact sourceArtifact,
CppCompileActionBuilder builder,
ArtifactCategory outputCategory,
CppModuleMap cppModuleMap,
boolean addObject,
boolean enableCoverage,
boolean generateDwo,
boolean generateDotd,
Map<String, String> sourceSpecificBuildVariables)
throws RuleErrorException {
ImmutableList.Builder<Artifact> directOutputs = new ImmutableList.Builder<>();
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();
boolean generateNoPicAction = getGenerateNoPicActions();
Preconditions.checkState(generatePicAction || generateNoPicAction);
if (fake) {
boolean usePic = !generateNoPicAction;
createFakeSourceAction(
sourceLabel,
outputName,
result,
env,
builder,
outputCategory,
addObject,
ccRelativeName,
sourceArtifact.getExecPath(),
usePic,
generateDotd);
} else {
boolean bitcodeOutput =
featureConfiguration.isEnabled(CppRuleClasses.THIN_LTO)
&& CppFileTypes.LTO_SOURCE.matches(sourceArtifact.getFilename());
// 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,
ccToolchain, ArtifactCategory.PIC_FILE, outputName);
CppCompileActionBuilder picBuilder = copyAsPicBuilder(
builder, picOutputBase, outputCategory, generateDotd);
String gcnoFileName = CppHelper.getArtifactNameForCategory(ruleContext,
ccToolchain, ArtifactCategory.COVERAGE_DATA_FILE, picOutputBase);
Artifact gcnoFile = enableCoverage
? CppHelper.getCompileOutputArtifact(ruleContext, gcnoFileName, configuration)
: null;
Artifact dwoFile = generateDwo ? getDwoFile(picBuilder.getOutputFile()) : null;
Artifact ltoIndexingFile =
bitcodeOutput ? getLtoIndexingFile(picBuilder.getOutputFile()) : null;
setupCompileBuildVariables(
picBuilder,
sourceLabel,
/* usePic= */ true,
ccRelativeName,
sourceArtifact.getExecPath(),
gcnoFile,
dwoFile,
ltoIndexingFile,
cppModuleMap,
sourceSpecificBuildVariables);
if (maySaveTemps) {
result.addTemps(
createTempsActions(
sourceArtifact,
sourceLabel,
outputName,
picBuilder,
/* usePic= */ true,
/* generateDotd= */ generateDotd,
ccRelativeName));
}
picBuilder.setGcnoFile(gcnoFile);
picBuilder.setDwoFile(dwoFile);
picBuilder.setLtoIndexingFile(ltoIndexingFile);
semantics.finalizeCompileActionBuilder(
ruleContext,
picBuilder,
featureConfiguration.getFeatureSpecification(),
coptsFilter,
features);
CppCompileAction picAction = picBuilder.buildOrThrowRuleError(ruleContext);
env.registerAction(picAction);
directOutputs.add(picAction.getOutputFile());
if (addObject) {
result.addPicObjectFile(picAction.getOutputFile());
if (bitcodeOutput) {
result.addLtoBitcodeFile(picAction.getOutputFile(), ltoIndexingFile);
}
}
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, ccToolchain, outputCategory, outputName),
configuration);
builder.setOutputs(ruleContext, outputCategory, outputName, generateDotd);
String gcnoFileName = CppHelper.getArtifactNameForCategory(ruleContext,
ccToolchain, ArtifactCategory.COVERAGE_DATA_FILE, outputName);
// Create non-PIC compile actions
Artifact gcnoFile =
!cppConfiguration.isLipoOptimization() && enableCoverage
? CppHelper.getCompileOutputArtifact(ruleContext, gcnoFileName, configuration)
: null;
Artifact noPicDwoFile = generateDwo ? getDwoFile(noPicOutputFile) : null;
Artifact ltoIndexingFile =
bitcodeOutput ? getLtoIndexingFile(builder.getOutputFile()) : null;
setupCompileBuildVariables(
builder,
sourceLabel,
/* usePic= */ false,
ccRelativeName,
sourceArtifact.getExecPath(),
gcnoFile,
noPicDwoFile,
ltoIndexingFile,
cppModuleMap,
sourceSpecificBuildVariables);
if (maySaveTemps) {
result.addTemps(
createTempsActions(
sourceArtifact,
sourceLabel,
outputName,
builder,
/* usePic= */ false,
generateDotd,
ccRelativeName));
}
builder.setGcnoFile(gcnoFile);
builder.setDwoFile(noPicDwoFile);
builder.setLtoIndexingFile(ltoIndexingFile);
semantics.finalizeCompileActionBuilder(
ruleContext,
builder,
featureConfiguration.getFeatureSpecification(),
coptsFilter,
features);
CppCompileAction compileAction = builder.buildOrThrowRuleError(ruleContext);
env.registerAction(compileAction);
Artifact objectFile = compileAction.getOutputFile();
directOutputs.add(objectFile);
if (addObject) {
result.addObjectFile(objectFile);
if (bitcodeOutput) {
result.addLtoBitcodeFile(objectFile, ltoIndexingFile);
}
}
if (noPicDwoFile != null) {
// Host targets don't produce .dwo files.
result.addDwoFile(noPicDwoFile);
}
if (cppConfiguration.isLipoContextCollector()) {
result.addLipoScannable(compileAction);
}
}
}
return directOutputs.build();
}
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, /* dotdFile= */ null);
setupCompileBuildVariables(
builder,
source.getLabel(),
/* usePic= */ false,
/* ccRelativeName= */ null,
/* autoFdoImportPath= */ null,
/* gcnoFile= */ null,
/* dwoFile= */ null,
/* ltoIndexingFile= */ null,
builder.getContext().getCppModuleMap(),
source.getBuildVariables());
semantics.finalizeCompileActionBuilder(
ruleContext,
builder,
featureConfiguration.getFeatureSpecification(),
coptsFilter,
features);
// Make sure this builder doesn't reference ruleContext outside of analysis phase.
CppCompileActionTemplate actionTemplate =
new CppCompileActionTemplate(
sourceArtifact,
outputFiles,
builder,
ccToolchain,
outputCategories,
ruleContext.getActionOwner());
env.registerAction(actionTemplate);
return outputFiles;
}
String getOutputNameBaseWith(String base, boolean usePic) {
return usePic
? CppHelper.getArtifactNameForCategory(
ruleContext, ccToolchain, ArtifactCategory.PIC_FILE, base)
: base;
}
private void createFakeSourceAction(
Label sourceLabel,
String outputName,
CcCompilationOutputs.Builder result,
AnalysisEnvironment env,
CppCompileActionBuilder builder,
ArtifactCategory outputCategory,
boolean addObject,
PathFragment ccRelativeName,
PathFragment execPath,
boolean usePic,
boolean generateDotd)
throws RuleErrorException {
String outputNameBase = getOutputNameBaseWith(outputName, usePic);
String tempOutputName = ruleContext.getConfiguration().getBinFragment()
.getRelative(CppHelper.getObjDirectory(ruleContext.getLabel()))
.getRelative(
CppHelper.getArtifactNameForCategory(ruleContext, ccToolchain, outputCategory,
getOutputNameBaseWith(outputName + ".temp", usePic)))
.getPathString();
builder
.setPicMode(usePic)
.setOutputs(ruleContext, outputCategory, outputNameBase, generateDotd)
.setTempOutputFile(PathFragment.create(tempOutputName));
setupCompileBuildVariables(
builder,
sourceLabel,
usePic,
ccRelativeName,
execPath,
/* gcnoFile= */ null,
/* dwoFile= */ null,
/* ltoIndexingFile= */ null,
builder.getContext().getCppModuleMap(),
/* sourceSpecificBuildVariables= */ ImmutableMap.of());
semantics.finalizeCompileActionBuilder(
ruleContext,
builder,
featureConfiguration.getFeatureSpecification(),
coptsFilter,
features);
CppCompileAction action = builder.buildOrThrowRuleError(ruleContext);
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, configuration, linkTargetType, linkedArtifactNameSuffix);
try {
String maybePicName = ruleContext.getLabel().getName() + linkedArtifactNameSuffix;
if (linkTargetType.picness() == Picness.PIC) {
maybePicName = CppHelper.getArtifactNameForCategory(
ruleContext, ccToolchain, ArtifactCategory.PIC_FILE, maybePicName);
}
String linkedName = CppHelper.getArtifactNameForCategory(
ruleContext, ccToolchain, linkTargetType.getLinkerOutput(), maybePicName);
PathFragment artifactFragment = PathFragment.create(ruleContext.getLabel().getName())
.getParentDirectory().getRelative(linkedName);
result = ruleContext.getPackageRelativeArtifact(
artifactFragment, configuration.getBinDirectory(ruleContext.getRule().getRepository()));
} 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, /* forBinary= */ true);
boolean usePicForSharedLibs = CppHelper.usePic(ruleContext, /* forBinary= */ 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 = PathFragment.create(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)
.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(/* usePic= */ true))
.addLtoBitcodeFiles(ccOutputs.getLtoBitcodeFiles())
.setLinkType(picLinkType)
.setLinkStaticness(LinkStaticness.FULLY_STATIC)
.addActionInputs(linkActionInputs)
.setLibraryIdentifier(libraryIdentifier)
.addVariablesExtensions(variablesExtensions)
.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,
configuration,
LinkTargetType.INTERFACE_DYNAMIC_LIBRARY,
linkedArtifactNameSuffix);
// TODO(b/28946988): Remove this hard-coded flag.
if (!featureConfiguration.isEnabled(CppRuleClasses.TARGETS_WINDOWS)) {
sonameLinkopts =
ImmutableList.of(
"-Wl,-soname="
+ SolibSymlinkAction.getDynamicLibrarySoname(
soImpl.getRootRelativePath(), /* preserveName= */ false));
}
}
CppLinkActionBuilder dynamicLinkActionBuilder =
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,
ccToolchain.getDynamicRuntimeLinkMiddleman(),
ccToolchain.getDynamicRuntimeLinkInputs())
.addVariablesExtensions(variablesExtensions);
if (CppHelper.shouldUseDefFile(featureConfiguration)) {
Artifact defFile =
CppHelper.createDefFileActions(
ruleContext,
ccToolchain.getDefParserTool(),
ccOutputs.getObjectFiles(false),
SolibSymlinkAction.getDynamicLibrarySoname(soImpl.getRootRelativePath(), true));
dynamicLinkActionBuilder.setDefFile(defFile);
}
// On Windows, we cannot build a shared library with symbols unresolved, so here we dynamically
// link to all it's dependencies.
if (featureConfiguration.isEnabled(CppRuleClasses.TARGETS_WINDOWS)) {
CcLinkParams.Builder ccLinkParamsBuilder =
CcLinkParams.builder(/* linkingStatically= */ false, /* linkShared= */ true);
ccLinkParamsBuilder.addCcLibrary(ruleContext);
dynamicLinkActionBuilder.addLinkParams(ccLinkParamsBuilder.build(), ruleContext);
}
if (!ccOutputs.getLtoBitcodeFiles().isEmpty()
&& featureConfiguration.isEnabled(CppRuleClasses.THIN_LTO)) {
dynamicLinkActionBuilder.setLtoIndexing(true);
dynamicLinkActionBuilder.setUsePicForLtoBackendActions(usePicForSharedLibs);
CppLinkAction indexAction = dynamicLinkActionBuilder.build();
env.registerAction(indexAction);
dynamicLinkActionBuilder.setLtoIndexing(false);
}
CppLinkAction dynamicLinkAction = dynamicLinkActionBuilder.build();
env.registerAction(dynamicLinkAction);
if (linkType == LinkTargetType.EXECUTABLE) {
return result.build();
}
LibraryToLink dynamicLibrary = dynamicLinkAction.getOutputLibrary();
LibraryToLink interfaceLibrary = dynamicLinkAction.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.
//
// When COPY_DYNAMIC_LIBRARIES_TO_BINARY is enabled, we don't need to create the special
// solibDir, instead we use the original interface library and dynamic library.
if (neverLink
|| featureConfiguration.isEnabled(CppRuleClasses.COPY_DYNAMIC_LIBRARIES_TO_BINARY)) {
result.addDynamicLibrary(interfaceLibrary);
result.addExecutionDynamicLibrary(dynamicLibrary);
} else {
Artifact libraryLink =
SolibSymlinkAction.getDynamicLibrarySymlink(
ruleContext,
ccToolchain.getSolibDirectory(),
interfaceLibrary.getArtifact(),
/* preserveName= */ false,
/* prefixConsumer= */ false,
ruleContext.getConfiguration());
result.addDynamicLibrary(LinkerInputs.solibLibraryToLink(
libraryLink, interfaceLibrary.getArtifact(), libraryIdentifier));
Artifact implLibraryLink =
SolibSymlinkAction.getDynamicLibrarySymlink(
ruleContext,
ccToolchain.getSolibDirectory(),
dynamicLibrary.getArtifact(),
/* preserveName= */ false,
/* prefixConsumer= */ false,
ruleContext.getConfiguration());
result.addExecutionDynamicLibrary(LinkerInputs.solibLibraryToLink(
implLibraryLink, dynamicLibrary.getArtifact(), libraryIdentifier));
}
return result.build();
}
private CppLinkActionBuilder newLinkActionBuilder(Artifact outputArtifact) {
return new CppLinkActionBuilder(
ruleContext, outputArtifact, ccToolchain, fdoSupport, featureConfiguration, semantics)
.setCrosstoolInputs(ccToolchain.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) {
CppCompileActionBuilder builder =
new CppCompileActionBuilder(ruleContext, ccToolchain, configuration);
builder.setSourceFile(source);
builder.setContext(context);
builder.addEnvironment(ccToolchain.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(ruleContext, outputCategory, outputName, generateDotd);
return picBuilder;
}
/** Create the actions for "--save_temps". */
private ImmutableList<Artifact> createTempsActions(
Artifact source,
Label sourceLabel,
String outputName,
CppCompileActionBuilder builder,
boolean usePic,
boolean generateDotd,
PathFragment ccRelativeName)
throws RuleErrorException {
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(ruleContext, category, outputArtifactNameBase, generateDotd);
setupCompileBuildVariables(
dBuilder,
sourceLabel,
usePic,
ccRelativeName,
source.getExecPath(),
/* gcnoFile= */ null,
/* dwoFile= */ null,
/* ltoIndexingFile= */ null,
builder.getContext().getCppModuleMap(),
/* sourceSpecificBuildVariables= */ ImmutableMap.of());
semantics.finalizeCompileActionBuilder(
ruleContext,
dBuilder,
featureConfiguration.getFeatureSpecification(),
coptsFilter,
features);
CppCompileAction dAction = dBuilder.buildOrThrowRuleError(ruleContext);
ruleContext.registerAction(dAction);
CppCompileActionBuilder sdBuilder = new CppCompileActionBuilder(builder);
sdBuilder.setOutputs(
ruleContext, ArtifactCategory.GENERATED_ASSEMBLY, outputArtifactNameBase, generateDotd);
setupCompileBuildVariables(
sdBuilder,
sourceLabel,
usePic,
ccRelativeName,
source.getExecPath(),
/* gcnoFile= */ null,
/* dwoFile= */ null,
/* ltoIndexingFile= */ null,
builder.getContext().getCppModuleMap(),
/* sourceSpecificBuildVariables= */ ImmutableMap.of());
semantics.finalizeCompileActionBuilder(
ruleContext,
sdBuilder,
featureConfiguration.getFeatureSpecification(),
coptsFilter,
features);
CppCompileAction sdAction = sdBuilder.buildOrThrowRuleError(ruleContext);
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;
}
}