// 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 static java.util.stream.Collectors.toCollection;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicates;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList;
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.Artifact.SpecialArtifact;
import com.google.devtools.build.lib.analysis.AnalysisEnvironment;
import com.google.devtools.build.lib.analysis.AnalysisUtils;
import com.google.devtools.build.lib.analysis.LanguageDependentFragment;
import com.google.devtools.build.lib.analysis.OutputGroupInfo;
import com.google.devtools.build.lib.analysis.RuleContext;
import com.google.devtools.build.lib.analysis.TransitiveInfoCollection;
import com.google.devtools.build.lib.analysis.TransitiveInfoProviderMap;
import com.google.devtools.build.lib.analysis.TransitiveInfoProviderMapBuilder;
import com.google.devtools.build.lib.analysis.actions.SymlinkAction;
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.collect.nestedset.NestedSet;
import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
import com.google.devtools.build.lib.collect.nestedset.Order;
import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
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.CcCommon.CoptsFilter;
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.CppConfiguration.HeadersCheckingMode;
import com.google.devtools.build.lib.syntax.Type;
import com.google.devtools.build.lib.util.FileType;
import com.google.devtools.build.lib.util.FileTypeSet;
import com.google.devtools.build.lib.util.Pair;
import com.google.devtools.build.lib.vfs.FileSystemUtils;
import com.google.devtools.build.lib.vfs.PathFragment;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import javax.annotation.Nullable;
/**
* A class to create C/C++ compile actions in a way that is consistent with cc_library. Rules that
* generate source files and emulate cc_library on top of that should use this class instead of the
* lower-level APIs in CppHelper and CppCompileActionBuilder.
*
*
Rules that want to use this class are required to have implicit dependencies on the toolchain,
* the STL, the lipo context, and so on. Optionally, they can also have copts, and malloc
* attributes, but note that these require explicit calls to the corresponding setter methods.
*/
public final class CcCompilationHelper {
/** Similar to {@code OutputGroupInfo.HIDDEN_TOP_LEVEL}, but specific to header token files. */
public static final String HIDDEN_HEADER_TOKENS =
OutputGroupInfo.HIDDEN_OUTPUT_GROUP_PREFIX
+ "hidden_header_tokens"
+ OutputGroupInfo.INTERNAL_SUFFIX;
private static final String PIC_CONFIGURATION_ERROR =
"PIC compilation is requested but the toolchain does not support it";
/** 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";
/**
* 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 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 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 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 collection of include paths.
*
* @see CcCompilationInfo#getIncludeDirs().
*/
public static final String INCLUDE_PATHS_VARIABLE_NAME = "include_paths";
/**
* Name of the build variable for the collection of quote include paths.
*
* @see CcCompilationInfo#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 CcCompilationInfo#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 path to the compilation output file in case of assembly
* source.
*/
private static final String OUTPUT_ASSEMBLY_FILE_VARIABLE_NAME = "output_assembly_file";
/** Name of the build variable for the dependency file path */
private static final String DEPENDENCY_FILE_VARIABLE_NAME = "dependency_file";
/** Name of the build variable for the module file name. */
private static final String MODULE_NAME_VARIABLE_NAME = "module_name";
/** Name of the build variable for the module map file name. */
private static final String MODULE_MAP_FILE_VARIABLE_NAME = "module_map_file";
/** Name of the build variable for the dependent module map file name. */
private static final String DEPENDENT_MODULE_MAP_FILES_VARIABLE_NAME =
"dependent_module_map_files";
/** Name of the build variable for the collection of module files. */
private static final String MODULE_FILES_VARIABLE_NAME = "module_files";
/** Name of the build variable for the gcov coverage file path. */
private static final String GCOV_GCNO_FILE_VARIABLE_NAME = "gcov_gcno_file";
/** Name of the build variable for the per object debug info file. */
private 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. */
private static final String LTO_INDEXING_BITCODE_FILE_VARIABLE_NAME = "lto_indexing_bitcode_file";
/**
* A group of source file types and action names for builds controlled by CcCompilationHelper.
* Determines what file types CcCompilationHelper considers sources and what action configs are
* configured in the CROSSTOOL.
*/
public enum SourceCategory {
CC(
FileTypeSet.of(
CppFileTypes.CPP_SOURCE,
CppFileTypes.CPP_HEADER,
CppFileTypes.C_SOURCE,
CppFileTypes.ASSEMBLER,
CppFileTypes.ASSEMBLER_WITH_C_PREPROCESSOR,
CppFileTypes.CLIF_INPUT_PROTO)),
CC_AND_OBJC(
FileTypeSet.of(
CppFileTypes.CPP_SOURCE,
CppFileTypes.CPP_HEADER,
CppFileTypes.OBJC_SOURCE,
CppFileTypes.OBJCPP_SOURCE,
CppFileTypes.C_SOURCE,
CppFileTypes.ASSEMBLER,
CppFileTypes.ASSEMBLER_WITH_C_PREPROCESSOR));
private final FileTypeSet sourceTypeSet;
private SourceCategory(FileTypeSet sourceTypeSet) {
this.sourceTypeSet = sourceTypeSet;
}
/** Returns the set of file types that are valid for this category. */
public FileTypeSet getSourceTypes() {
return sourceTypeSet;
}
}
/** Function for extracting module maps from CppCompilationDependencies. */
private static final Function CPP_DEPS_TO_MODULES =
dep -> {
CcCompilationInfo ccCompilationInfo = dep.get(CcCompilationInfo.PROVIDER);
return ccCompilationInfo == null ? null : ccCompilationInfo.getCppModuleMap();
};
/**
* Contains the providers as well as the {@code CcCompilationOutputs} and the {@code
* CcCompilationInfo}.
*/
public static final class CompilationInfo {
private final TransitiveInfoProviderMap providers;
private final Map> outputGroups;
private final CcCompilationOutputs compilationOutputs;
private final CcCompilationInfo ccCompilationInfo;
private CompilationInfo(
TransitiveInfoProviderMap providers,
Map> outputGroups,
CcCompilationOutputs compilationOutputs,
CcCompilationInfo ccCompilationInfo) {
this.providers = providers;
this.outputGroups = outputGroups;
this.compilationOutputs = compilationOutputs;
this.ccCompilationInfo = ccCompilationInfo;
}
public TransitiveInfoProviderMap getProviders() {
return providers;
}
public Map> getOutputGroups() {
return outputGroups;
}
public CcCompilationOutputs getCcCompilationOutputs() {
return compilationOutputs;
}
public CcCompilationInfo getCcCompilationInfo() {
return ccCompilationInfo;
}
}
private final RuleContext ruleContext;
private final CppSemantics semantics;
private final BuildConfiguration configuration;
private final CppConfiguration cppConfiguration;
private final List publicHeaders = new ArrayList<>();
private final List nonModuleMapHeaders = new ArrayList<>();
private final List publicTextualHeaders = new ArrayList<>();
private final List privateHeaders = new ArrayList<>();
private final List additionalInputs = new ArrayList<>();
private final List compilationMandatoryInputs = new ArrayList<>();
private final List additionalIncludeScanningRoots = new ArrayList<>();
private final List additionalExportedHeaders = new ArrayList<>();
private final List additionalCppModuleMaps = new ArrayList<>();
private final Set compilationUnitSources = new LinkedHashSet<>();
private final List objectFiles = new ArrayList<>();
private final List picObjectFiles = new ArrayList<>();
private ImmutableList copts = ImmutableList.of();
private CoptsFilter coptsFilter = CoptsFilter.alwaysPasses();
private final Set defines = new LinkedHashSet<>();
private final List deps = new ArrayList<>();
private final List depCcCompilationInfos = new ArrayList<>();
private final List looseIncludeDirs = new ArrayList<>();
private final List systemIncludeDirs = new ArrayList<>();
private final List includeDirs = new ArrayList<>();
private HeadersCheckingMode headersCheckingMode = HeadersCheckingMode.LOOSE;
private boolean fake;
private boolean checkDepsGenerateCpp = true;
private boolean emitCompileProviders;
private final SourceCategory sourceCategory;
private final List variablesExtensions = new ArrayList<>();
@Nullable private CppModuleMap cppModuleMap;
private boolean propagateModuleMapToCompileAction = true;
private final FeatureConfiguration featureConfiguration;
private final CcToolchainProvider ccToolchain;
private final FdoSupportProvider fdoSupport;
private final ImmutableSet features;
private boolean useDeps = true;
private boolean generateModuleMap = true;
private String purpose = null;
private boolean generateNoPic = true;
// TODO(plf): Pull out of class.
private CcCompilationInfo ccCompilationInfo;
/**
* Creates a CcCompilationHelper.
*
* @param ruleContext the RuleContext for the rule being built
* @param semantics CppSemantics for the build
* @param featureConfiguration activated features and action configs for the build
* @param sourceCatagory the candidate source types for the build
* @param ccToolchain the C++ toolchain provider for the build
* @param fdoSupport the C++ FDO optimization support provider for the build
*/
public CcCompilationHelper(
RuleContext ruleContext,
CppSemantics semantics,
FeatureConfiguration featureConfiguration,
SourceCategory sourceCatagory,
CcToolchainProvider ccToolchain,
FdoSupportProvider fdoSupport) {
this(
ruleContext,
semantics,
featureConfiguration,
sourceCatagory,
ccToolchain,
fdoSupport,
ruleContext.getConfiguration());
}
/**
* Creates a CcCompilationHelper that outputs artifacts in a given configuration.
*
* @param ruleContext the RuleContext for the rule being built
* @param semantics CppSemantics for the build
* @param featureConfiguration activated features and action configs for the build
* @param sourceCatagory the candidate source types for the build
* @param ccToolchain the C++ toolchain provider for the build
* @param fdoSupport the C++ FDO optimization support provider for the build
* @param configuration the configuration that gives the directory of output artifacts
*/
public CcCompilationHelper(
RuleContext ruleContext,
CppSemantics semantics,
FeatureConfiguration featureConfiguration,
SourceCategory sourceCatagory,
CcToolchainProvider ccToolchain,
FdoSupportProvider fdoSupport,
BuildConfiguration configuration) {
this.ruleContext = Preconditions.checkNotNull(ruleContext);
this.semantics = Preconditions.checkNotNull(semantics);
this.featureConfiguration = Preconditions.checkNotNull(featureConfiguration);
this.sourceCategory = Preconditions.checkNotNull(sourceCatagory);
this.ccToolchain = Preconditions.checkNotNull(ccToolchain);
this.fdoSupport = Preconditions.checkNotNull(fdoSupport);
this.configuration = Preconditions.checkNotNull(configuration);
this.cppConfiguration =
Preconditions.checkNotNull(ruleContext.getFragment(CppConfiguration.class));
this.features = ruleContext.getFeatures();
}
/**
* Creates a CcCompilationHelper for cpp source files.
*
* @param ruleContext the RuleContext for the rule being built
* @param semantics CppSemantics for the build
* @param featureConfiguration activated features and action configs for the build
* @param ccToolchain the C++ toolchain provider for the build
* @param fdoSupport the C++ FDO optimization support provider for the build
*/
public CcCompilationHelper(
RuleContext ruleContext,
CppSemantics semantics,
FeatureConfiguration featureConfiguration,
CcToolchainProvider ccToolchain,
FdoSupportProvider fdoSupport) {
this(ruleContext, semantics, featureConfiguration, SourceCategory.CC, ccToolchain, fdoSupport);
}
/** Sets fields that overlap for cc_library and cc_binary rules. */
public CcCompilationHelper fromCommon(CcCommon common) {
setCopts(common.getCopts());
addDefines(common.getDefines());
addDeps(ruleContext.getPrerequisites("deps", Mode.TARGET));
addLooseIncludeDirs(common.getLooseIncludeDirs());
addSystemIncludeDirs(common.getSystemIncludeDirs());
setCoptsFilter(common.getCoptsFilter());
setHeadersCheckingMode(semantics.determineHeadersCheckingMode(ruleContext));
return this;
}
/**
* Adds {@code headers} as public header files. These files will be made visible to dependent
* rules. They may be parsed/preprocessed or compiled into a header module depending on the
* configuration.
*/
public CcCompilationHelper addPublicHeaders(Collection headers) {
for (Artifact header : headers) {
addHeader(header, ruleContext.getLabel());
}
return this;
}
/**
* Adds {@code headers} as public header files. These files will be made visible to dependent
* rules. They may be parsed/preprocessed or compiled into a header module depending on the
* configuration.
*/
public CcCompilationHelper addPublicHeaders(Artifact... headers) {
addPublicHeaders(Arrays.asList(headers));
return this;
}
/**
* Adds {@code headers} as public header files. These files will be made visible to dependent
* rules. They may be parsed/preprocessed or compiled into a header module depending on the
* configuration.
*/
public CcCompilationHelper addPublicHeaders(Iterable> headers) {
for (Pair header : headers) {
addHeader(header.first, header.second);
}
return this;
}
/**
* Add the corresponding files as public header files, i.e., these files will not be compiled, but
* are made visible as includes to dependent rules in module maps.
*/
public CcCompilationHelper addAdditionalExportedHeaders(
Iterable additionalExportedHeaders) {
Iterables.addAll(this.additionalExportedHeaders, additionalExportedHeaders);
return this;
}
/**
* Add the corresponding files as public textual header files. These files will not be compiled
* into a target's header module, but will be made visible as textual includes to dependent rules.
*/
public CcCompilationHelper addPublicTextualHeaders(Iterable textualHeaders) {
Iterables.addAll(this.publicTextualHeaders, textualHeaders);
for (Artifact header : textualHeaders) {
this.additionalExportedHeaders.add(header.getExecPath());
}
return this;
}
/**
* Add the corresponding files as source files. These may also be header files, in which case they
* will not be compiled, but also not made visible as includes to dependent rules. The given build
* variables will be added to those used for compiling this source.
*/
public CcCompilationHelper addSources(Collection sources) {
for (Artifact source : sources) {
addSource(source, ruleContext.getLabel());
}
return this;
}
/**
* Add the corresponding files as source files. These may also be header files, in which case they
* will not be compiled, but also not made visible as includes to dependent rules.
*/
public CcCompilationHelper addSources(Iterable> sources) {
for (Pair source : sources) {
addSource(source.first, source.second);
}
return this;
}
/**
* Add the corresponding files as source files. These may also be header files, in which case they
* will not be compiled, but also not made visible as includes to dependent rules.
*/
public CcCompilationHelper addSources(Artifact... sources) {
return addSources(Arrays.asList(sources));
}
/** Add the corresponding files as non-header, non-source input files. */
public CcCompilationHelper addAdditionalInputs(Collection inputs) {
Iterables.addAll(additionalInputs, inputs);
return this;
}
/**
* Adds a header to {@code publicHeaders} and in case header processing is switched on for the
* file type also to compilationUnitSources.
*/
private void addHeader(Artifact header, Label label) {
// We assume TreeArtifacts passed in are directories containing proper headers.
boolean isHeader =
CppFileTypes.CPP_HEADER.matches(header.getExecPath()) || header.isTreeArtifact();
boolean isTextualInclude = CppFileTypes.CPP_TEXTUAL_INCLUDE.matches(header.getExecPath());
publicHeaders.add(header);
if (isTextualInclude || !isHeader || !shouldProcessHeaders()) {
return;
}
compilationUnitSources.add(CppSource.create(header, label, CppSource.Type.HEADER));
}
/** Adds a header to {@code publicHeaders}, but not to this target's module map. */
public CcCompilationHelper addNonModuleMapHeader(Artifact header) {
Preconditions.checkNotNull(header);
nonModuleMapHeaders.add(header);
return this;
}
/**
* Adds a source to {@code compilationUnitSources} if it is a compiled file type (including
* parsed/preprocessed header) and to {@code privateHeaders} if it is a header.
*/
private void addSource(Artifact source, Label label) {
Preconditions.checkNotNull(featureConfiguration);
boolean isHeader = CppFileTypes.CPP_HEADER.matches(source.getExecPath());
boolean isTextualInclude = CppFileTypes.CPP_TEXTUAL_INCLUDE.matches(source.getExecPath());
// We assume TreeArtifacts passed in are directories containing proper sources for compilation.
boolean isCompiledSource =
sourceCategory.getSourceTypes().matches(source.getExecPathString())
|| source.isTreeArtifact();
if (isHeader || isTextualInclude) {
privateHeaders.add(source);
}
if (isTextualInclude || !isCompiledSource || (isHeader && !shouldProcessHeaders())) {
return;
}
boolean isClifInputProto = CppFileTypes.CLIF_INPUT_PROTO.matches(source.getExecPathString());
CppSource.Type type;
if (isHeader) {
type = CppSource.Type.HEADER;
} else if (isClifInputProto) {
type = CppSource.Type.CLIF_INPUT_PROTO;
} else {
type = CppSource.Type.SOURCE;
}
compilationUnitSources.add(CppSource.create(source, label, type));
}
private boolean shouldProcessHeaders() {
CppConfiguration cppConfiguration = ruleContext.getFragment(CppConfiguration.class);
// If parse_headers_verifies_modules is switched on, we verify that headers are
// self-contained by building the module instead.
return !cppConfiguration.getParseHeadersVerifiesModules()
&& (featureConfiguration.isEnabled(CppRuleClasses.PREPROCESS_HEADERS)
|| featureConfiguration.isEnabled(CppRuleClasses.PARSE_HEADERS));
}
/**
* Returns the compilation unit sources. That includes all compiled source files as well as
* headers that will be parsed or preprocessed. Each source file contains the label it arises from
* in the build graph as well as {@code FeatureConfiguration} that should be used during its
* compilation.
*/
public ImmutableSet getCompilationUnitSources() {
return ImmutableSet.copyOf(this.compilationUnitSources);
}
/**
* Add the corresponding files as linker inputs for non-PIC links. If the corresponding files are
* compiled with PIC, the final link may or may not fail. Note that the final link may not happen
* here, if {@code --start_end_lib} is enabled, but instead at any binary that transitively
* depends on the current rule.
*/
public CcCompilationHelper addObjectFiles(Iterable objectFiles) {
for (Artifact objectFile : objectFiles) {
Preconditions.checkArgument(Link.OBJECT_FILETYPES.matches(objectFile.getFilename()));
}
Iterables.addAll(this.objectFiles, objectFiles);
return this;
}
/**
* Add the corresponding files as linker inputs for PIC links. If the corresponding files are not
* compiled with PIC, the final link may or may not fail. Note that the final link may not happen
* here, if {@code --start_end_lib} is enabled, but instead at any binary that transitively
* depends on the current rule.
*/
public CcCompilationHelper addPicObjectFiles(Iterable picObjectFiles) {
for (Artifact objectFile : objectFiles) {
Preconditions.checkArgument(Link.OBJECT_FILETYPES.matches(objectFile.getFilename()));
}
Iterables.addAll(this.picObjectFiles, picObjectFiles);
return this;
}
public CcCompilationHelper setCopts(ImmutableList copts) {
this.copts = Preconditions.checkNotNull(copts);
return this;
}
/** Sets a pattern that is used to filter copts; set to {@code null} for no filtering. */
private void setCoptsFilter(CoptsFilter coptsFilter) {
this.coptsFilter = Preconditions.checkNotNull(coptsFilter);
}
/** Adds the given defines to the compiler command line. */
public CcCompilationHelper addDefines(Iterable defines) {
Iterables.addAll(this.defines, defines);
return this;
}
/**
* Adds the given targets as dependencies - this can include explicit dependencies on other rules
* (like from a "deps" attribute) and also implicit dependencies on runtime libraries.
*/
public CcCompilationHelper addDeps(Iterable extends TransitiveInfoCollection> deps) {
for (TransitiveInfoCollection dep : deps) {
this.deps.add(dep);
}
return this;
}
public CcCompilationHelper addDepCcCompilationInfo(CcCompilationInfo ccCompilationInfo) {
this.depCcCompilationInfos.add(Preconditions.checkNotNull(ccCompilationInfo));
return this;
}
/**
* Adds the given precompiled files to this helper. Shared and static libraries are added as
* compilation prerequisites, and object files are added as pic or non-pic object files
* respectively.
*/
public CcCompilationHelper addPrecompiledFiles(PrecompiledFiles precompiledFiles) {
addObjectFiles(precompiledFiles.getObjectFiles(false));
addPicObjectFiles(precompiledFiles.getObjectFiles(true));
return this;
}
/**
* Adds the given directories to the loose include directories that are only allowed to be
* referenced when headers checking is {@link HeadersCheckingMode#LOOSE} or {@link
* HeadersCheckingMode#WARN}.
*/
private void addLooseIncludeDirs(Iterable looseIncludeDirs) {
Iterables.addAll(this.looseIncludeDirs, looseIncludeDirs);
}
/**
* Adds the given directories to the system include directories (they are passed with {@code
* "-isystem"} to the compiler); these are also passed to dependent rules.
*/
public CcCompilationHelper addSystemIncludeDirs(Iterable systemIncludeDirs) {
Iterables.addAll(this.systemIncludeDirs, systemIncludeDirs);
return this;
}
/**
* Adds the given directories to the include directories (they are passed with {@code "-I"} to the
* compiler); these are also passed to dependent rules.
*/
public CcCompilationHelper addIncludeDirs(Iterable includeDirs) {
Iterables.addAll(this.includeDirs, includeDirs);
return this;
}
/** Adds a variableExtension to template the crosstool. */
public CcCompilationHelper addVariableExtension(VariablesExtension variableExtension) {
Preconditions.checkNotNull(variableExtension);
this.variablesExtensions.add(variableExtension);
return this;
}
/** Sets a module map artifact for this build. */
public CcCompilationHelper setCppModuleMap(CppModuleMap cppModuleMap) {
Preconditions.checkNotNull(cppModuleMap);
this.cppModuleMap = cppModuleMap;
return this;
}
/** Signals that this target's module map should not be an input to c++ compile actions. */
public CcCompilationHelper setPropagateModuleMapToCompileAction(boolean propagatesModuleMap) {
this.propagateModuleMapToCompileAction = propagatesModuleMap;
return this;
}
/** Sets the given headers checking mode. The default is {@link HeadersCheckingMode#LOOSE}. */
public CcCompilationHelper setHeadersCheckingMode(HeadersCheckingMode headersCheckingMode) {
this.headersCheckingMode = Preconditions.checkNotNull(headersCheckingMode);
return this;
}
/**
* Marks the resulting code as fake, i.e., the code will not actually be compiled or linked, but
* instead, the compile command is written to a file and added to the runfiles. This is currently
* used for non-compilation tests. Unfortunately, the design is problematic, so please don't add
* any further uses.
*/
public CcCompilationHelper setFake(boolean fake) {
this.fake = fake;
return this;
}
/**
* Disables checking that the deps actually are C++ rules. By default, the {@link #compile} method
* uses {@link LanguageDependentFragment.Checker#depSupportsLanguage} to check that all deps
* provide C++ providers.
*/
public CcCompilationHelper setCheckDepsGenerateCpp(boolean checkDepsGenerateCpp) {
this.checkDepsGenerateCpp = checkDepsGenerateCpp;
return this;
}
/**
* Enables the output of the {@code files_to_compile} and {@code compilation_prerequisites} output
* groups.
*/
// TODO(bazel-team): We probably need to adjust this for the multi-language rules.
public CcCompilationHelper enableCompileProviders() {
this.emitCompileProviders = true;
return this;
}
/**
* Causes actions generated from this CcCompilationHelper not to use build semantics (includes,
* headers, srcs) from dependencies.
*/
public CcCompilationHelper doNotUseDeps() {
this.useDeps = false;
return this;
}
/** non-PIC actions won't be generated. */
public CcCompilationHelper setGenerateNoPic(boolean generateNoPic) {
this.generateNoPic = generateNoPic;
return this;
}
/** Adds mandatory inputs for the compilation action. */
public CcCompilationHelper addCompilationMandatoryInputs(
Collection compilationMandatoryInputs) {
this.compilationMandatoryInputs.addAll(compilationMandatoryInputs);
return this;
}
/** Adds additional includes to be scanned. */
// TODO(plf): This is only needed for CLIF. Investigate whether this is strictly necessary or
// there is a way to avoid include scanning for CLIF rules.
public CcCompilationHelper addAditionalIncludeScanningRoots(
Collection additionalIncludeScanningRoots) {
this.additionalIncludeScanningRoots.addAll(additionalIncludeScanningRoots);
return this;
}
/**
* Create the C++ compile actions, and the corresponding compilation related providers.
*
* @throws RuleErrorException
*/
public CompilationInfo compile() throws RuleErrorException {
if (checkDepsGenerateCpp) {
for (LanguageDependentFragment dep :
AnalysisUtils.getProviders(deps, LanguageDependentFragment.class)) {
LanguageDependentFragment.Checker.depSupportsLanguage(
ruleContext, dep, CppRuleClasses.LANGUAGE, "deps");
}
}
ccCompilationInfo = initializeCcCompilationInfo();
boolean compileHeaderModules = featureConfiguration.isEnabled(CppRuleClasses.HEADER_MODULES);
Preconditions.checkState(
!compileHeaderModules || ccCompilationInfo.getCppModuleMap() != null,
"All cc rules must support module maps.");
// Create compile actions (both PIC and non-PIC).
CcCompilationOutputs ccOutputs = createCcCompileActions();
if (!objectFiles.isEmpty() || !picObjectFiles.isEmpty()) {
// Merge the pre-compiled object files into the compiler outputs.
ccOutputs =
new CcCompilationOutputs.Builder()
.merge(ccOutputs)
.addLtoBitcodeFile(ccOutputs.getLtoBitcodeFiles())
.addObjectFiles(objectFiles)
.addPicObjectFiles(picObjectFiles)
.build();
}
DwoArtifactsCollector dwoArtifacts =
DwoArtifactsCollector.transitiveCollector(
ccOutputs,
deps,
/*generateDwo=*/ false,
/*ltoBackendArtifactsUsePic=*/ false,
/*ltoBackendArtifacts=*/ ImmutableList.of());
// Be very careful when adding new providers here - it can potentially affect a lot of rules.
// We should consider merging most of these providers into a single provider.
TransitiveInfoProviderMapBuilder providers =
new TransitiveInfoProviderMapBuilder()
.add(
new CppDebugFileProvider(
dwoArtifacts.getDwoArtifacts(), dwoArtifacts.getPicDwoArtifacts()),
collectTransitiveLipoInfo(ccOutputs));
providers.put(ccCompilationInfo);
Map> outputGroups = new TreeMap<>();
outputGroups.put(OutputGroupInfo.TEMP_FILES, getTemps(ccOutputs));
CppConfiguration cppConfiguration = ruleContext.getFragment(CppConfiguration.class);
if (emitCompileProviders) {
boolean isLipoCollector = cppConfiguration.isLipoContextCollector();
boolean processHeadersInDependencies = cppConfiguration.processHeadersInDependencies();
boolean usePic = CppHelper.usePic(ruleContext, ccToolchain, false);
outputGroups.put(
OutputGroupInfo.FILES_TO_COMPILE,
ccOutputs.getFilesToCompile(isLipoCollector, processHeadersInDependencies, usePic));
outputGroups.put(
OutputGroupInfo.COMPILATION_PREREQUISITES,
CcCommon.collectCompilationPrerequisites(ruleContext, ccCompilationInfo));
}
return new CompilationInfo(providers.build(), outputGroups, ccOutputs, ccCompilationInfo);
}
@Immutable
private static class PublicHeaders {
private final ImmutableList headers;
private final ImmutableList moduleMapHeaders;
private final @Nullable PathFragment virtualIncludePath;
private PublicHeaders(
ImmutableList headers,
ImmutableList moduleMapHeaders,
PathFragment virtualIncludePath) {
this.headers = headers;
this.moduleMapHeaders = moduleMapHeaders;
this.virtualIncludePath = virtualIncludePath;
}
private ImmutableList getHeaders() {
return headers;
}
private ImmutableList getModuleMapHeaders() {
return moduleMapHeaders;
}
@Nullable
private PathFragment getVirtualIncludePath() {
return virtualIncludePath;
}
}
private PublicHeaders computePublicHeaders() {
if (!ruleContext.attributes().has("strip_include_prefix", Type.STRING)
|| !ruleContext.attributes().has("include_prefix", Type.STRING)) {
return new PublicHeaders(
ImmutableList.copyOf(Iterables.concat(publicHeaders, nonModuleMapHeaders)),
ImmutableList.copyOf(publicHeaders),
null);
}
PathFragment prefix = null;
if (ruleContext.attributes().isAttributeValueExplicitlySpecified("include_prefix")) {
String prefixAttr = ruleContext.attributes().get("include_prefix", Type.STRING);
prefix = PathFragment.create(prefixAttr);
if (PathFragment.containsUplevelReferences(prefixAttr)) {
ruleContext.attributeError("include_prefix", "should not contain uplevel references");
}
if (prefix.isAbsolute()) {
ruleContext.attributeError("include_prefix", "should be a relative path");
}
}
PathFragment stripPrefix;
if (ruleContext.attributes().isAttributeValueExplicitlySpecified("strip_include_prefix")) {
String stripPrefixAttr = ruleContext.attributes().get("strip_include_prefix", Type.STRING);
if (PathFragment.containsUplevelReferences(stripPrefixAttr)) {
ruleContext.attributeError("strip_include_prefix", "should not contain uplevel references");
}
stripPrefix = PathFragment.create(stripPrefixAttr);
if (stripPrefix.isAbsolute()) {
stripPrefix =
ruleContext
.getLabel()
.getPackageIdentifier()
.getRepository()
.getSourceRoot()
.getRelative(stripPrefix.toRelative());
} else {
stripPrefix = ruleContext.getPackageDirectory().getRelative(stripPrefix);
}
} else if (prefix != null) {
stripPrefix = ruleContext.getPackageDirectory();
} else {
stripPrefix = null;
}
if (stripPrefix == null && prefix == null) {
// Simple case, no magic needed
return new PublicHeaders(
ImmutableList.copyOf(Iterables.concat(publicHeaders, nonModuleMapHeaders)),
ImmutableList.copyOf(publicHeaders),
null);
}
if (ruleContext.hasErrors()) {
return new PublicHeaders(ImmutableList.of(), ImmutableList.of(), null);
}
ImmutableList.Builder moduleHeadersBuilder = ImmutableList.builder();
for (Artifact originalHeader : publicHeaders) {
if (!originalHeader.getRootRelativePath().startsWith(stripPrefix)) {
ruleContext.ruleError(
String.format(
"header '%s' is not under the specified strip prefix '%s'",
originalHeader.getExecPathString(), stripPrefix.getPathString()));
continue;
}
PathFragment includePath = originalHeader.getRootRelativePath().relativeTo(stripPrefix);
if (prefix != null) {
includePath = prefix.getRelative(includePath);
}
if (!originalHeader.getExecPath().equals(includePath)) {
Artifact virtualHeader =
ruleContext.getUniqueDirectoryArtifact(
"_virtual_includes", includePath, ruleContext.getBinOrGenfilesDirectory());
ruleContext.registerAction(
new SymlinkAction(
ruleContext.getActionOwner(),
originalHeader,
virtualHeader,
"Symlinking virtual headers for " + ruleContext.getLabel()));
moduleHeadersBuilder.add(virtualHeader);
} else {
moduleHeadersBuilder.add(originalHeader);
}
}
ImmutableList moduleMapHeaders = moduleHeadersBuilder.build();
ImmutableList virtualHeaders =
ImmutableList.builder()
.addAll(moduleMapHeaders)
.addAll(nonModuleMapHeaders)
.build();
return new PublicHeaders(
virtualHeaders,
moduleMapHeaders,
ruleContext
.getBinOrGenfilesDirectory()
.getExecPath()
.getRelative(ruleContext.getUniqueDirectory("_virtual_includes")));
}
/**
* Create {@code CcCompilationInfo} for cc compile action from generated inputs.
*
*
TODO(plf): Try to pull out CcCompilationInfo building out of this class.
*/
public CcCompilationInfo initializeCcCompilationInfo() {
CcCompilationInfo.Builder ccCompilationInfoBuilder = new CcCompilationInfo.Builder(ruleContext);
// Setup the include path; local include directories come before those inherited from deps or
// from the toolchain; in case of aliasing (same include file found on different entries),
// prefer the local include rather than the inherited one.
// Add in the roots for well-formed include names for source files and
// generated files. It is important that the execRoot (EMPTY_FRAGMENT) comes
// before the genfilesFragment to preferably pick up source files. Otherwise
// we might pick up stale generated files.
PathFragment repositoryPath =
ruleContext.getLabel().getPackageIdentifier().getRepository().getPathUnderExecRoot();
ccCompilationInfoBuilder.addQuoteIncludeDir(repositoryPath);
ccCompilationInfoBuilder.addQuoteIncludeDir(
ruleContext.getConfiguration().getGenfilesFragment().getRelative(repositoryPath));
for (PathFragment systemIncludeDir : systemIncludeDirs) {
ccCompilationInfoBuilder.addSystemIncludeDir(systemIncludeDir);
}
for (PathFragment includeDir : includeDirs) {
ccCompilationInfoBuilder.addIncludeDir(includeDir);
}
PublicHeaders publicHeaders = computePublicHeaders();
if (publicHeaders.getVirtualIncludePath() != null) {
ccCompilationInfoBuilder.addIncludeDir(publicHeaders.getVirtualIncludePath());
}
if (useDeps) {
ccCompilationInfoBuilder.mergeDependentCcCompilationInfos(
AnalysisUtils.getProviders(deps, CcCompilationInfo.PROVIDER));
ccCompilationInfoBuilder.mergeDependentCcCompilationInfos(depCcCompilationInfos);
}
CppHelper.mergeToolchainDependentCcCompilationInfo(
ruleContext, ccToolchain, ccCompilationInfoBuilder);
// But defines come after those inherited from deps.
ccCompilationInfoBuilder.addDefines(defines);
// There are no ordering constraints for declared include dirs/srcs, or the pregrepped headers.
ccCompilationInfoBuilder.addDeclaredIncludeSrcs(publicHeaders.getHeaders());
ccCompilationInfoBuilder.addDeclaredIncludeSrcs(publicTextualHeaders);
ccCompilationInfoBuilder.addDeclaredIncludeSrcs(privateHeaders);
ccCompilationInfoBuilder.addDeclaredIncludeSrcs(additionalInputs);
ccCompilationInfoBuilder.addNonCodeInputs(additionalInputs);
ccCompilationInfoBuilder.addModularHdrs(publicHeaders.getHeaders());
ccCompilationInfoBuilder.addModularHdrs(privateHeaders);
ccCompilationInfoBuilder.addTextualHdrs(publicTextualHeaders);
ccCompilationInfoBuilder.addPregreppedHeaders(
CppHelper.createExtractInclusions(ruleContext, semantics, publicHeaders.getHeaders()));
ccCompilationInfoBuilder.addPregreppedHeaders(
CppHelper.createExtractInclusions(ruleContext, semantics, publicTextualHeaders));
ccCompilationInfoBuilder.addPregreppedHeaders(
CppHelper.createExtractInclusions(ruleContext, semantics, privateHeaders));
// Add this package's dir to declaredIncludeDirs, & this rule's headers to declaredIncludeSrcs
// Note: no include dir for STRICT mode.
if (headersCheckingMode == HeadersCheckingMode.WARN) {
ccCompilationInfoBuilder.addDeclaredIncludeWarnDir(
ruleContext.getLabel().getPackageFragment());
for (PathFragment looseIncludeDir : looseIncludeDirs) {
ccCompilationInfoBuilder.addDeclaredIncludeWarnDir(looseIncludeDir);
}
} else if (headersCheckingMode == HeadersCheckingMode.LOOSE) {
ccCompilationInfoBuilder.addDeclaredIncludeDir(ruleContext.getLabel().getPackageFragment());
for (PathFragment looseIncludeDir : looseIncludeDirs) {
ccCompilationInfoBuilder.addDeclaredIncludeDir(looseIncludeDir);
}
}
if (featureConfiguration.isEnabled(CppRuleClasses.MODULE_MAPS)) {
if (cppModuleMap == null) {
cppModuleMap = CppHelper.createDefaultCppModuleMap(ruleContext, /*suffix=*/ "");
}
ccCompilationInfoBuilder.setPropagateCppModuleMapAsActionInput(
propagateModuleMapToCompileAction);
ccCompilationInfoBuilder.setCppModuleMap(cppModuleMap);
// There are different modes for module compilation:
// 1. We create the module map and compile the module so that libraries depending on us can
// use the resulting module artifacts in their compilation (compiled is true).
// 2. We create the module map so that libraries depending on us will include the headers
// textually (compiled is false).
boolean compiled =
featureConfiguration.isEnabled(CppRuleClasses.HEADER_MODULES)
|| featureConfiguration.isEnabled(CppRuleClasses.COMPILE_ALL_MODULES);
Iterable dependentModuleMaps = collectModuleMaps();
if (generateModuleMap) {
Optional umbrellaHeader = cppModuleMap.getUmbrellaHeader();
if (umbrellaHeader.isPresent()) {
ruleContext.registerAction(
createUmbrellaHeaderAction(umbrellaHeader.get(), publicHeaders));
}
ruleContext.registerAction(
createModuleMapAction(cppModuleMap, publicHeaders, dependentModuleMaps, compiled));
}
if (getGeneratesPicHeaderModule()) {
ccCompilationInfoBuilder.setPicHeaderModule(getPicHeaderModule(cppModuleMap.getArtifact()));
}
if (getGeneratesNoPicHeaderModule()) {
ccCompilationInfoBuilder.setHeaderModule(getHeaderModule(cppModuleMap.getArtifact()));
}
if (!compiled
&& featureConfiguration.isEnabled(CppRuleClasses.PARSE_HEADERS)
&& featureConfiguration.isEnabled(CppRuleClasses.USE_HEADER_MODULES)
&& ruleContext.getFragment(CppConfiguration.class).getParseHeadersVerifiesModules()) {
// Here, we are creating a compiled module to verify that headers are self-contained and
// modules ready, but we don't use the corresponding module map or compiled file anywhere
// else.
CppModuleMap verificationMap =
CppHelper.createDefaultCppModuleMap(ruleContext, /*suffix=*/ ".verify");
ruleContext.registerAction(
createModuleMapAction(
verificationMap, publicHeaders, dependentModuleMaps, /*compiledModule=*/ true));
ccCompilationInfoBuilder.setVerificationModuleMap(verificationMap);
}
}
ccCompilationInfoBuilder.setPurpose(purpose);
semantics.setupCcCompilationInfo(ruleContext, ccCompilationInfoBuilder);
return ccCompilationInfoBuilder.build();
}
/**
* Collects all preprocessed header files (*.h.processed) from dependencies and the current rule.
*/
public static NestedSet collectHeaderTokens(
RuleContext ruleContext, CcCompilationOutputs ccCompilationOutputs) {
NestedSetBuilder headerTokens = NestedSetBuilder.stableOrder();
for (OutputGroupInfo dep :
ruleContext.getPrerequisites("deps", Mode.TARGET, OutputGroupInfo.SKYLARK_CONSTRUCTOR)) {
headerTokens.addTransitive(dep.getOutputGroup(CcCompilationHelper.HIDDEN_HEADER_TOKENS));
}
if (ruleContext.getFragment(CppConfiguration.class).processHeadersInDependencies()) {
headerTokens.addAll(ccCompilationOutputs.getHeaderTokenFiles());
}
return headerTokens.build();
}
public void registerAdditionalModuleMap(CppModuleMap cppModuleMap) {
this.additionalCppModuleMaps.add(Preconditions.checkNotNull(cppModuleMap));
}
/** Don't generate a module map for this target if a custom module map is provided. */
public CcCompilationHelper doNotGenerateModuleMap() {
generateModuleMap = false;
return this;
}
/**
* Sets the purpose for the {@code CcCompilationInfo}.
*
* @see CcCompilationInfo.Builder#setPurpose
* @param purpose must be a string which is suitable for use as a filename. A single rule may have
* many middlemen with distinct purposes.
*/
public CcCompilationHelper setPurpose(@Nullable String purpose) {
this.purpose = purpose;
return this;
}
/**
* Supplier that computes legacy_compile_flags lazily at the execution phase.
*
*
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> getLegacyCompileFlagsSupplier(
CppConfiguration cppConfiguration,
CcToolchainProvider toolchain,
String sourceFilename,
ImmutableSet features) {
return () -> {
ImmutableList.Builder legacyCompileFlags = ImmutableList.builder();
legacyCompileFlags.addAll(
CppHelper.getCompilerOptions(cppConfiguration, toolchain, features));
if (CppFileTypes.C_SOURCE.matches(sourceFilename)) {
legacyCompileFlags.addAll(cppConfiguration.getCOptions());
}
if (CppFileTypes.CPP_SOURCE.matches(sourceFilename)
|| CppFileTypes.CPP_HEADER.matches(sourceFilename)
|| CppFileTypes.CPP_MODULE_MAP.matches(sourceFilename)
|| CppFileTypes.CLIF_INPUT_PROTO.matches(sourceFilename)) {
legacyCompileFlags.addAll(CppHelper.getCxxOptions(cppConfiguration, toolchain, features));
}
return legacyCompileFlags.build();
};
}
/**
* Supplier that computes unfiltered_compile_flags lazily at the execution phase.
*
*
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> getUnfilteredCompileFlagsSupplier(
CcToolchainProvider ccToolchain, ImmutableSet features) {
return () -> ccToolchain.getUnfilteredCompilerOptions(features);
}
private UmbrellaHeaderAction createUmbrellaHeaderAction(
Artifact umbrellaHeader, PublicHeaders publicHeaders) {
return new UmbrellaHeaderAction(
ruleContext.getActionOwner(),
umbrellaHeader,
featureConfiguration.isEnabled(CppRuleClasses.ONLY_DOTH_HEADERS_IN_MODULE_MAPS)
? Iterables.filter(publicHeaders.getModuleMapHeaders(), CppFileTypes.MODULE_MAP_HEADER)
: publicHeaders.getModuleMapHeaders(),
additionalExportedHeaders);
}
private CppModuleMapAction createModuleMapAction(
CppModuleMap moduleMap,
PublicHeaders publicHeaders,
Iterable dependentModuleMaps,
boolean compiledModule) {
return new CppModuleMapAction(
ruleContext.getActionOwner(),
moduleMap,
featureConfiguration.isEnabled(CppRuleClasses.EXCLUDE_PRIVATE_HEADERS_IN_MODULE_MAPS)
? ImmutableList.of()
: privateHeaders,
featureConfiguration.isEnabled(CppRuleClasses.ONLY_DOTH_HEADERS_IN_MODULE_MAPS)
? Iterables.filter(publicHeaders.getModuleMapHeaders(), CppFileTypes.MODULE_MAP_HEADER)
: publicHeaders.getModuleMapHeaders(),
dependentModuleMaps,
additionalExportedHeaders,
compiledModule,
featureConfiguration.isEnabled(CppRuleClasses.MODULE_MAP_HOME_CWD),
featureConfiguration.isEnabled(CppRuleClasses.GENERATE_SUBMODULES),
!featureConfiguration.isEnabled(CppRuleClasses.MODULE_MAP_WITHOUT_EXTERN_MODULE));
}
private Iterable collectModuleMaps() {
// Cpp module maps may be null for some rules. We filter the nulls out at the end.
List result =
deps.stream().map(CPP_DEPS_TO_MODULES).collect(toCollection(ArrayList::new));
if (ruleContext.getRule().getAttributeDefinition(":stl") != null) {
CcCompilationInfo stl =
ruleContext.getPrerequisite(":stl", Mode.TARGET, CcCompilationInfo.PROVIDER);
if (stl != null) {
result.add(stl.getCppModuleMap());
}
}
if (ccToolchain != null) {
result.add(ccToolchain.getCcCompilationInfo().getCppModuleMap());
}
for (CppModuleMap additionalCppModuleMap : additionalCppModuleMaps) {
result.add(additionalCppModuleMap);
}
return Iterables.filter(result, Predicates.notNull());
}
private TransitiveLipoInfoProvider collectTransitiveLipoInfo(CcCompilationOutputs outputs) {
if (fdoSupport.getFdoSupport().getFdoRoot() == null) {
return TransitiveLipoInfoProvider.EMPTY;
}
NestedSetBuilder scannableBuilder = NestedSetBuilder.stableOrder();
// TODO(bazel-team): Only fetch the STL prerequisite in one place.
TransitiveInfoCollection stl = ruleContext.getPrerequisite(":stl", Mode.TARGET);
if (stl != null) {
TransitiveLipoInfoProvider provider = stl.getProvider(TransitiveLipoInfoProvider.class);
if (provider != null) {
scannableBuilder.addTransitive(provider.getTransitiveIncludeScannables());
}
}
for (TransitiveLipoInfoProvider dep :
AnalysisUtils.getProviders(deps, TransitiveLipoInfoProvider.class)) {
scannableBuilder.addTransitive(dep.getTransitiveIncludeScannables());
}
for (IncludeScannable scannable : outputs.getLipoScannables()) {
Preconditions.checkState(scannable.getIncludeScannerSources().size() == 1);
scannableBuilder.add(scannable);
}
return new TransitiveLipoInfoProvider(scannableBuilder.build());
}
private NestedSet getTemps(CcCompilationOutputs compilationOutputs) {
return ruleContext.getFragment(CppConfiguration.class).isLipoContextCollector()
? NestedSetBuilder.emptySet(Order.STABLE_ORDER)
: compilationOutputs.getTemps();
}
/** @return whether this target needs to generate a pic header module. */
private boolean getGeneratesPicHeaderModule() {
return shouldProvideHeaderModules() && !fake && getGeneratePicActions();
}
/** @return whether this target needs to generate a non-pic header module. */
private boolean getGeneratesNoPicHeaderModule() {
return shouldProvideHeaderModules() && !fake && getGenerateNoPicActions();
}
/** @return whether we want to provide header modules for the current target. */
private boolean shouldProvideHeaderModules() {
return featureConfiguration.isEnabled(CppRuleClasses.HEADER_MODULES)
&& !cppConfiguration.isLipoContextCollector();
}
/** @return whether this target needs to generate non-pic actions. */
private boolean getGenerateNoPicActions() {
if (!generateNoPic) {
return false;
}
boolean picFeatureEnabled = featureConfiguration.isEnabled(CppRuleClasses.PIC);
boolean usePicForBinaries = CppHelper.usePic(ruleContext, ccToolchain, true);
boolean usePicForNonBinaries = CppHelper.usePic(ruleContext, ccToolchain, false);
if (!usePicForNonBinaries) {
// This means you have to be prepared to use non-pic output for dynamic libraries.
return true;
}
// Either you're only making a dynamic library (onlySingleOutput) or pic should be used
// in all cases.
if (usePicForBinaries) {
if (picFeatureEnabled) {
return false;
}
ruleContext.ruleError(PIC_CONFIGURATION_ERROR);
}
return true;
}
/** @return whether this target needs to generate pic actions. */
private boolean getGeneratePicActions() {
return featureConfiguration.isEnabled(CppRuleClasses.PIC)
&& CppHelper.usePic(ruleContext, ccToolchain, false);
}
/** @return the non-pic header module artifact for the current target. */
private Artifact getHeaderModule(Artifact moduleMapArtifact) {
PathFragment objectDir = CppHelper.getObjDirectory(ruleContext.getLabel());
PathFragment outputName = objectDir.getRelative(moduleMapArtifact.getRootRelativePath());
return ruleContext.getRelatedArtifact(outputName, ".pcm");
}
/** @return the pic header module artifact for the current target. */
private Artifact getPicHeaderModule(Artifact moduleMapArtifact) {
PathFragment objectDir = CppHelper.getObjDirectory(ruleContext.getLabel());
PathFragment outputName = objectDir.getRelative(moduleMapArtifact.getRootRelativePath());
return ruleContext.getRelatedArtifact(outputName, ".pic.pcm");
}
/**
* 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.
*/
private CcCompilationOutputs createCcCompileActions() throws RuleErrorException {
CcCompilationOutputs.Builder result = new CcCompilationOutputs.Builder();
Preconditions.checkNotNull(ccCompilationInfo);
AnalysisEnvironment env = ruleContext.getAnalysisEnvironment();
if (shouldProvideHeaderModules()) {
Label moduleMapLabel =
Label.parseAbsoluteUnchecked(ccCompilationInfo.getCppModuleMap().getName());
Collection modules =
createModuleAction(result, ccCompilationInfo.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 (ccCompilationInfo.getVerificationModuleMap() != null) {
Collection modules =
createModuleAction(result, ccCompilationInfo.getVerificationModuleMap());
for (Artifact module : modules) {
result.addHeaderTokenFile(module);
}
}
for (CppSource source : compilationUnitSources) {
Artifact sourceArtifact = source.getSource();
Label sourceLabel = source.getLabel();
String outputName =
FileSystemUtils.removeExtension(sourceArtifact.getRootRelativePath()).getPathString();
CppCompileActionBuilder builder = initializeCompileAction(sourceArtifact);
builder
.setSemantics(semantics)
.addMandatoryInputs(compilationMandatoryInputs)
.addAdditionalIncludeScanningRoots(additionalIncludeScanningRoots);
boolean bitcodeOutput =
featureConfiguration.isEnabled(CppRuleClasses.THIN_LTO)
&& CppFileTypes.LTO_SOURCE.matches(sourceArtifact.getFilename());
if (!sourceArtifact.isTreeArtifact()) {
switch (source.getType()) {
case HEADER:
createHeaderAction(
sourceLabel, outputName, result, env, builder, isGenerateDotdFile(sourceArtifact));
break;
default:
createSourceAction(
sourceLabel,
outputName,
result,
env,
sourceArtifact,
builder,
// TODO(plf): Continue removing CLIF logic from C++. Follow up changes would include
// refactoring CppSource.Type and ArtifactCategory to be classes instead of enums
// that could be instantiated with arbitrary values.
source.getType() == CppSource.Type.CLIF_INPUT_PROTO
? ArtifactCategory.CLIF_OUTPUT_PROTO
: ArtifactCategory.OBJECT_FILE,
ccCompilationInfo.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.
CppHelper.shouldCreatePerObjectDebugInfo(
cppConfiguration, ccToolchain, featureConfiguration)
&& !bitcodeOutput,
isGenerateDotdFile(sourceArtifact));
break;
}
} else {
switch (source.getType()) {
case HEADER:
Artifact headerTokenFile =
createCompileActionTemplate(
env,
source,
builder,
ImmutableList.of(
ArtifactCategory.GENERATED_HEADER, ArtifactCategory.PROCESSED_HEADER),
false);
result.addHeaderTokenFile(headerTokenFile);
break;
case SOURCE:
Artifact objectFile =
createCompileActionTemplate(
env, source, builder, ImmutableList.of(ArtifactCategory.OBJECT_FILE), false);
result.addObjectFile(objectFile);
if (getGeneratePicActions()) {
Artifact picObjectFile =
createCompileActionTemplate(
env,
source,
builder,
ImmutableList.of(ArtifactCategory.PIC_OBJECT_FILE),
true);
result.addPicObjectFile(picObjectFile);
}
break;
default:
throw new IllegalStateException(
"Encountered invalid source types when creating CppCompileActionTemplates");
}
}
}
return result.build();
}
private Artifact createCompileActionTemplate(
AnalysisEnvironment env,
CppSource source,
CppCompileActionBuilder builder,
Iterable outputCategories,
boolean usePic) {
SpecialArtifact sourceArtifact = (SpecialArtifact) source.getSource();
SpecialArtifact outputFiles =
CppHelper.getCompileOutputTreeArtifact(ruleContext, sourceArtifact, usePic);
// TODO(rduan): Dotd file output is not supported yet.
builder.setOutputs(outputFiles, /* dotdFile= */ null);
setupCompileBuildVariables(
builder,
source.getLabel(),
usePic,
/* ccRelativeName= */ null,
/* autoFdoImportPath= */ null,
/* gcnoFile= */ null,
/* dwoFile= */ null,
/* ltoIndexingFile= */ null,
builder.getCcCompilationInfo().getCppModuleMap());
semantics.finalizeCompileActionBuilder(ruleContext, builder);
// 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;
}
private void setupCompileBuildVariables(
CppCompileActionBuilder builder,
Label sourceLabel,
boolean usePic,
PathFragment ccRelativeName,
PathFragment autoFdoImportPath,
Artifact gcnoFile,
Artifact dwoFile,
Artifact ltoIndexingFile,
CppModuleMap cppModuleMap) {
CcToolchainFeatures.Variables.Builder buildVariables =
new CcToolchainFeatures.Variables.Builder(ccToolchain.getBuildVariables());
CcCompilationInfo builderCcCompilationInfo = builder.getCcCompilationInfo();
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.builder()
.addAll(copts)
.addAll(collectPerFileCopts(sourceFile, sourceLabel))
.build());
String sourceFilename = sourceFile.getExecPathString();
buildVariables.addLazyStringSequenceVariable(
LEGACY_COMPILE_FLAGS_VARIABLE_NAME,
getLegacyCompileFlagsSupplier(cppConfiguration, ccToolchain, 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