// 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.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.RuleConfiguredTarget.Mode;
import com.google.devtools.build.lib.analysis.RuleContext;
import com.google.devtools.build.lib.analysis.TransitiveInfoCollection;
import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.packages.BuildType;
import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException;
import com.google.devtools.build.lib.rules.cpp.CcCompilationOutputs.Builder;
import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.ExpansionException;
import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.FeatureConfiguration;
import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.Variables.StringSequenceBuilder;
import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.Variables.VariablesExtension;
import com.google.devtools.build.lib.rules.cpp.CppCompileAction.DotdFile;
import com.google.devtools.build.lib.rules.cpp.Link.LinkStaticness;
import com.google.devtools.build.lib.rules.cpp.Link.LinkTargetType;
import com.google.devtools.build.lib.rules.cpp.Link.Picness;
import com.google.devtools.build.lib.rules.cpp.Link.Staticness;
import com.google.devtools.build.lib.rules.cpp.LinkerInputs.LibraryToLink;
import com.google.devtools.build.lib.rules.test.InstrumentedFilesCollector;
import com.google.devtools.build.lib.util.FileType;
import com.google.devtools.build.lib.util.Preconditions;
import com.google.devtools.build.lib.vfs.FileSystemUtils;
import com.google.devtools.build.lib.vfs.PathFragment;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import javax.annotation.Nullable;
/**
* Representation of a C/C++ compilation. Its purpose is to share the code that creates compilation
* actions between all classes that need to do so. It follows the builder pattern - load up the
* necessary settings and then call {@link #createCcCompileActions}.
*
*
This class is not thread-safe, and it should only be used once for each set of source files,
* i.e. calling {@link #createCcCompileActions} will throw an Exception if called twice.
*/
public final class CppModel {
/** 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 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 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 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";
private final CppSemantics semantics;
private final RuleContext ruleContext;
private final BuildConfiguration configuration;
private final CppConfiguration cppConfiguration;
// compile model
private CppCompilationContext context;
private final Set sourceFiles = new LinkedHashSet<>();
private final List mandatoryInputs = new ArrayList<>();
private final List copts = new ArrayList<>();
@Nullable private Pattern nocopts;
private boolean fake;
private boolean maySaveTemps;
private boolean onlySingleOutput;
private CcCompilationOutputs compilationOutputs;
// link model
private final List linkopts = new ArrayList<>();
private LinkTargetType linkType = LinkTargetType.STATIC_LIBRARY;
private boolean neverLink;
private final List linkActionInputs = new ArrayList<>();
private boolean allowInterfaceSharedObjects;
private boolean createDynamicLibrary = true;
private Artifact soImplArtifact;
private FeatureConfiguration featureConfiguration;
private List variablesExtensions = new ArrayList<>();
private final CcToolchainProvider ccToolchain;
private final FdoSupportProvider fdoSupport;
private String linkedArtifactNameSuffix = "";
public CppModel(RuleContext ruleContext, CppSemantics semantics,
CcToolchainProvider ccToolchain, FdoSupportProvider fdoSupport) {
this(ruleContext, semantics, ccToolchain, fdoSupport, ruleContext.getConfiguration());
}
public CppModel(RuleContext ruleContext, CppSemantics semantics,
CcToolchainProvider ccToolchain, FdoSupportProvider fdoSupport,
BuildConfiguration configuration) {
this.ruleContext = Preconditions.checkNotNull(ruleContext);
this.semantics = semantics;
this.ccToolchain = Preconditions.checkNotNull(ccToolchain);
this.fdoSupport = Preconditions.checkNotNull(fdoSupport);
this.configuration = configuration;
cppConfiguration = ruleContext.getFragment(CppConfiguration.class);
}
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 sourceFiles, Label sourceLabel, Map buildVariables,
CppSource.Type type) {
for (Artifact sourceFile : sourceFiles) {
this.sourceFiles.add(CppSource.create(sourceFile, sourceLabel, buildVariables, type));
}
return this;
}
/**
* Adds all the source files. Note that this should only be called for primary compilation units,
* including module files or headers to be parsed or preprocessed.
*/
public CppModel addCompilationUnitSources(Set sources) {
this.sourceFiles.addAll(sources);
return this;
}
/** Adds mandatory inputs. */
public CppModel addMandatoryInputs(Collection artifacts) {
this.mandatoryInputs.addAll(artifacts);
return this;
}
/**
* Adds the given copts.
*/
public CppModel addCopts(Collection copts) {
this.copts.addAll(copts);
return this;
}
/**
* Sets the nocopts pattern. This is used to filter out flags from the system defined set of
* flags. By default no filter is applied.
*/
public CppModel setNoCopts(@Nullable Pattern nocopts) {
this.nocopts = nocopts;
return this;
}
/**
* Adds the given linkopts to the optional dynamic library link command.
*/
public CppModel addLinkopts(Collection linkopts) {
this.linkopts.addAll(linkopts);
return this;
}
/**
* Adds the given variablesExensions for templating the crosstool.
*
*
In general, we prefer the build variables (especially those that derive strictly from
* the configuration) be learned by inspecting the CcToolchain, as passed to the rule in the
* CcToolchainProvider. However, for build variables that must be injected into the rule
* implementation (ex. build variables learned from the BUILD file), should be added using the
* VariablesExtension abstraction. This allows the injection to construct non-trivial build
* variables (lists, ect.).
*/
public CppModel addVariablesExtension(Collection variablesExtensions) {
this.variablesExtensions.addAll(variablesExtensions);
return this;
}
/**
* Sets the link type used for the link actions. Note that only static links are supported at this
* time.
*/
public CppModel setLinkTargetType(LinkTargetType linkType) {
this.linkType = linkType;
return this;
}
public CppModel setNeverLink(boolean neverLink) {
this.neverLink = neverLink;
return this;
}
/**
* Adds an artifact to the inputs of any link actions created by this CppModel.
*/
public CppModel addLinkActionInputs(Collection inputs) {
this.linkActionInputs.addAll(inputs);
return this;
}
/**
* Whether to allow interface dynamic libraries. Note that setting this to true only has an effect
* if the configuration allows it. Defaults to false.
*/
public CppModel setAllowInterfaceSharedObjects(boolean allowInterfaceSharedObjects) {
// TODO(bazel-team): Set the default to true, and require explicit action to disable it.
this.allowInterfaceSharedObjects = allowInterfaceSharedObjects;
return this;
}
public CppModel setCreateDynamicLibrary(boolean createDynamicLibrary) {
this.createDynamicLibrary = createDynamicLibrary;
return this;
}
public CppModel setDynamicLibrary(Artifact soImplFilename) {
this.soImplArtifact = soImplFilename;
return this;
}
/** Sets the feature configuration to be used for C/C++ actions. */
public CppModel setFeatureConfiguration(FeatureConfiguration featureConfiguration) {
this.featureConfiguration = featureConfiguration;
return this;
}
/*
* 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, Label sourceLabel) {
CppCompileActionBuilder builder = createCompileActionBuilder(sourceArtifact, sourceLabel);
if (nocopts != null) {
builder.addNocopts(nocopts);
}
builder.setFeatureConfiguration(featureConfiguration);
return builder;
}
/** Get the safe path strings for a list of paths to use in the build variables. */
private ImmutableSet getSafePathStrings(Collection paths) {
ImmutableSet.Builder result = ImmutableSet.builder();
for (PathFragment path : paths) {
result.add(path.getSafePathString());
}
return result.build();
}
private void setupCompileBuildVariables(
CppCompileActionBuilder builder,
boolean usePic,
PathFragment ccRelativeName,
PathFragment autoFdoImportPath,
Artifact gcnoFile,
Artifact dwoFile,
Artifact ltoIndexingFile,
CppModuleMap cppModuleMap,
Map sourceSpecificBuildVariables) {
CcToolchainFeatures.Variables.Builder buildVariables =
new CcToolchainFeatures.Variables.Builder();
// TODO(bazel-team): Pull out string constants for all build variables.
CppCompilationContext builderContext = builder.getContext();
Artifact sourceFile = builder.getSourceFile();
Artifact outputFile = builder.getOutputFile();
String realOutputFilePath;
buildVariables.addStringVariable(SOURCE_FILE_VARIABLE_NAME, sourceFile.getExecPathString());
buildVariables.addStringVariable(OUTPUT_FILE_VARIABLE_NAME, 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_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