diff options
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationHelper.java')
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationHelper.java | 1153 |
1 files changed, 1095 insertions, 58 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationHelper.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationHelper.java index a7c1615d92..f9149ea2fd 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationHelper.java +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationHelper.java @@ -20,10 +20,13 @@ 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; @@ -33,20 +36,27 @@ 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; @@ -61,7 +71,7 @@ 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 CppModel. + * lower-level APIs in CppHelper and CppCompileActionBuilder. * * <p>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 @@ -74,6 +84,97 @@ public final class CcCompilationHelper { + "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 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 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 @@ -155,6 +256,7 @@ public final class CcCompilationHelper { private final RuleContext ruleContext; private final CppSemantics semantics; private final BuildConfiguration configuration; + private final CppConfiguration cppConfiguration; private final List<Artifact> publicHeaders = new ArrayList<>(); private final List<Artifact> nonModuleMapHeaders = new ArrayList<>(); @@ -190,11 +292,15 @@ public final class CcCompilationHelper { private final FeatureConfiguration featureConfiguration; private final CcToolchainProvider ccToolchain; private final FdoSupportProvider fdoSupport; + private final ImmutableSet<String> features; private boolean useDeps = true; private boolean generateModuleMap = true; private String purpose = null; private boolean generateNoPic = true; + // TODO(plf): Pull out of class. + private CppCompilationContext cppCompilationContext; + /** * Creates a CcCompilationHelper. * @@ -248,6 +354,9 @@ public final class CcCompilationHelper { 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(); } /** @@ -645,9 +754,7 @@ public final class CcCompilationHelper { } } - CppModel model = initializeCppModel(); - CppCompilationContext cppCompilationContext = initializeCppCompilationContext(model); - model.setContext(cppCompilationContext); + cppCompilationContext = initializeCppCompilationContext(); boolean compileHeaderModules = featureConfiguration.isEnabled(CppRuleClasses.HEADER_MODULES); Preconditions.checkState( @@ -655,7 +762,7 @@ public final class CcCompilationHelper { "All cc rules must support module maps."); // Create compile actions (both PIC and non-PIC). - CcCompilationOutputs ccOutputs = model.createCcCompileActions(); + CcCompilationOutputs ccOutputs = createCcCompileActions(); if (!objectFiles.isEmpty() || !picObjectFiles.isEmpty()) { // Merge the pre-compiled object files into the compiler outputs. ccOutputs = @@ -703,22 +810,6 @@ public final class CcCompilationHelper { return new CompilationInfo(providers.build(), outputGroups, ccOutputs, cppCompilationContext); } - /** Creates the C/C++ compilation action creator. */ - private CppModel initializeCppModel() { - return new CppModel( - ruleContext, semantics, ccToolchain, fdoSupport, configuration, copts, coptsFilter) - .addCompilationUnitSources(compilationUnitSources) - .addCompilationMandatoryInputs(compilationMandatoryInputs) - .addAdditionalIncludeScanningRoots(additionalIncludeScanningRoots) - .setFake(fake) - .setGenerateNoPic(generateNoPic) - // Note: this doesn't actually save the temps, it just makes the CppModel use the - // configurations --save_temps setting to decide whether to actually save the temps. - .setSaveTemps(true) - .setFeatureConfiguration(featureConfiguration) - .addVariablesExtension(variablesExtensions); - } - @Immutable private static class PublicHeaders { private final ImmutableList<Artifact> headers; @@ -853,17 +944,12 @@ public final class CcCompilationHelper { .getRelative(ruleContext.getUniqueDirectory("_virtual_includes"))); } - /** Creates context for cc compile action from generated inputs. */ - public CppCompilationContext initializeCppCompilationContext() { - return initializeCppCompilationContext(initializeCppModel()); - } - /** * Create context for cc compile action from generated inputs. * * <p>TODO(plf): Try to pull out CppCompilationContext building out of this class. */ - private CppCompilationContext initializeCppCompilationContext(CppModel model) { + public CppCompilationContext initializeCppCompilationContext() { CppCompilationContext.Builder contextBuilder = new CppCompilationContext.Builder(ruleContext); // Setup the include path; local include directories come before those inherited from deps or @@ -959,11 +1045,11 @@ public final class CcCompilationHelper { ruleContext.registerAction( createModuleMapAction(cppModuleMap, publicHeaders, dependentModuleMaps, compiled)); } - if (model.getGeneratesPicHeaderModule()) { - contextBuilder.setPicHeaderModule(model.getPicHeaderModule(cppModuleMap.getArtifact())); + if (getGeneratesPicHeaderModule()) { + contextBuilder.setPicHeaderModule(getPicHeaderModule(cppModuleMap.getArtifact())); } - if (model.getGeneratesNoPicHeaderModule()) { - contextBuilder.setHeaderModule(model.getHeaderModule(cppModuleMap.getArtifact())); + if (getGeneratesNoPicHeaderModule()) { + contextBuilder.setHeaderModule(getHeaderModule(cppModuleMap.getArtifact())); } if (!compiled && featureConfiguration.isEnabled(CppRuleClasses.PARSE_HEADERS) @@ -986,6 +1072,83 @@ public final class CcCompilationHelper { return contextBuilder.build(); } + /** + * Collects all preprocessed header files (*.h.processed) from dependencies and the current rule. + */ + public static NestedSet<Artifact> collectHeaderTokens( + RuleContext ruleContext, CcCompilationOutputs ccCompilationOutputs) { + NestedSetBuilder<Artifact> 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 context. + * + * @see CppCompilationContext.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. + * + * <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, + CcToolchainProvider toolchain, + String sourceFilename, + ImmutableSet<String> features) { + return () -> { + ImmutableList.Builder<String> 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. + * + * <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); + } + private UmbrellaHeaderAction createUmbrellaHeaderAction( Artifact umbrellaHeader, PublicHeaders publicHeaders) { return new UmbrellaHeaderAction( @@ -1041,19 +1204,6 @@ public final class CcCompilationHelper { return Iterables.filter(result, Predicates.<CppModuleMap>notNull()); } - static NestedSet<Artifact> collectHeaderTokens( - RuleContext ruleContext, CcCompilationOutputs ccCompilationOutputs) { - NestedSetBuilder<Artifact> 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(); - } - private TransitiveLipoInfoProvider collectTransitiveLipoInfo(CcCompilationOutputs outputs) { if (fdoSupport.getFdoSupport().getFdoRoot() == null) { return TransitiveLipoInfoProvider.EMPTY; @@ -1086,25 +1236,912 @@ public final class CcCompilationHelper { : compilationOutputs.getTemps(); } - public void registerAdditionalModuleMap(CppModuleMap cppModuleMap) { - this.additionalCppModuleMaps.add(Preconditions.checkNotNull(cppModuleMap)); + /** @return whether this target needs to generate a pic header module. */ + private boolean getGeneratesPicHeaderModule() { + return shouldProvideHeaderModules() && !fake && getGeneratePicActions(); } - /** Don't generate a module map for this target if a custom module map is provided. */ - public CcCompilationHelper doNotGenerateModuleMap() { - generateModuleMap = false; - return this; + /** @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"); } /** - * Sets the purpose for the context. - * - * @see CppCompilationContext.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. + * 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 CcCompilationHelper setPurpose(@Nullable String purpose) { - this.purpose = purpose; - return this; + private CcCompilationOutputs createCcCompileActions() throws RuleErrorException { + CcCompilationOutputs.Builder result = new CcCompilationOutputs.Builder(); + Preconditions.checkNotNull(cppCompilationContext); + AnalysisEnvironment env = ruleContext.getAnalysisEnvironment(); + + if (shouldProvideHeaderModules()) { + Label moduleMapLabel = + Label.parseAbsoluteUnchecked(cppCompilationContext.getCppModuleMap().getName()); + Collection<Artifact> modules = + createModuleAction(result, cppCompilationContext.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 (cppCompilationContext.getVerificationModuleMap() != null) { + Collection<Artifact> modules = + createModuleAction(result, cppCompilationContext.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, + cppCompilationContext.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<ArtifactCategory> 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.getContext().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()); + + 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, 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 <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_CONFIGURATION_ERROR); + } + 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()); + } + + for (VariablesExtension extension : variablesExtensions) { + extension.addVariables(buildVariables); + } + + CcToolchainFeatures.Variables variables = buildVariables.build(); + builder.setVariables(variables); + } + + /** + * 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; + } + + /** + * 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(cppCompilationContext); + builder.addEnvironment(ccToolchain.getEnvironment()); + builder.setCoptsFilter(coptsFilter); + return builder; + } + + 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 = module.getRootRelativePath().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 = module.getRootRelativePath(); + + 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() && !CppHelper.isLipoOptimization(cppConfiguration, ccToolchain) + ? CppHelper.getCompileOutputArtifact(ruleContext, gcnoFileName, configuration) + : null; + + boolean generateDwo = + CppHelper.shouldCreatePerObjectDebugInfo( + cppConfiguration, ccToolchain, featureConfiguration); + 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()); + + builder.setGcnoFile(gcnoFile); + builder.setDwoFile(dwoFile); + + semantics.finalizeCompileActionBuilder(ruleContext, builder); + 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); + } + } + + /** Returns true if Dotd file should be generated. */ + private boolean isGenerateDotdFile(Artifact sourceArtifact) { + return CppFileTypes.headerDiscoveryRequired(sourceArtifact) + && !featureConfiguration.isEnabled(CppRuleClasses.PARSE_SHOWINCLUDES); + } + + private void createHeaderAction( + Label sourceLabel, + String outputName, + CcCompilationOutputs.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()); + semantics.finalizeCompileActionBuilder(ruleContext, builder); + CppCompileAction compileAction = builder.buildOrThrowRuleError(ruleContext); + env.registerAction(compileAction); + Artifact tokenFile = compileAction.getOutputFile(); + result.addHeaderTokenFile(tokenFile); + } + + 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(moduleMapArtifact.getRootRelativePath()).getPathString(), + result, + env, + moduleMapArtifact, + builder, + ArtifactCategory.CPP_MODULE, + cppModuleMap, + /* addObject= */ false, + /* enableCoverage= */ false, + /* generateDwo= */ false, + isGenerateDotdFile(moduleMapArtifact)); + } + + 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) + throws RuleErrorException { + ImmutableList.Builder<Artifact> directOutputs = new ImmutableList.Builder<>(); + PathFragment ccRelativeName = sourceArtifact.getRootRelativePath(); + if (CppHelper.isLipoOptimization(cppConfiguration, ccToolchain)) { + // 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(), cppCompilationContext)); + } + 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); + + 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); + 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 = + !CppHelper.isLipoOptimization(cppConfiguration, ccToolchain) && 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); + + result.addTemps( + createTempsActions( + sourceArtifact, + sourceLabel, + outputName, + builder, + /* usePic= */ false, + generateDotd, + ccRelativeName)); + + builder.setGcnoFile(gcnoFile); + builder.setDwoFile(noPicDwoFile); + builder.setLtoIndexingFile(ltoIndexingFile); + + semantics.finalizeCompileActionBuilder(ruleContext, builder); + 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(); + } + + /** + * 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) + throws RuleErrorException { + CppCompileActionBuilder picBuilder = new CppCompileActionBuilder(builder); + picBuilder.setPicMode(true).setOutputs(ruleContext, outputCategory, outputName, generateDotd); + + return picBuilder; + } + + String getOutputNameBaseWith(String base, boolean usePic) throws RuleErrorException { + 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()); + semantics.finalizeCompileActionBuilder(ruleContext, builder); + CppCompileAction action = builder.buildOrThrowRuleError(ruleContext); + env.registerAction(action); + if (addObject) { + if (usePic) { + result.addPicObjectFile(action.getOutputFile()); + } else { + result.addObjectFile(action.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; + } + + 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()); + } + + /** Get the safe path strings for a list of paths to use in the build variables. */ + private ImmutableSet<String> getSafePathStrings(Collection<PathFragment> paths) { + ImmutableSet.Builder<String> result = ImmutableSet.builder(); + for (PathFragment path : paths) { + result.add(path.getSafePathString()); + } + return result.build(); + } + + private 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); + } + + /** 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()); + semantics.finalizeCompileActionBuilder(ruleContext, dBuilder); + 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()); + semantics.finalizeCompileActionBuilder(ruleContext, sdBuilder); + CppCompileAction sdAction = sdBuilder.buildOrThrowRuleError(ruleContext); + ruleContext.registerAction(sdAction); + + return ImmutableList.of(dAction.getOutputFile(), sdAction.getOutputFile()); } } |