diff options
author | 2018-02-15 02:12:58 -0800 | |
---|---|---|
committer | 2018-02-15 02:14:44 -0800 | |
commit | f58dc51a225139fabeb452eb4b88022558f29f55 (patch) | |
tree | 5e06cdb7a39059dac51cd678f151e0a56bd26e9e /src/main/java/com/google/devtools/build | |
parent | 9d8ff35aac5006fb799584120166f8ecea1d391c (diff) |
C++: Removes CppModel class
The logic is split between CcCompilationHelper and CcLinkingHelper.
RELNOTES:none
PiperOrigin-RevId: 185809915
Diffstat (limited to 'src/main/java/com/google/devtools/build')
11 files changed, 1512 insertions, 1831 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcBinary.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcBinary.java index 727db97d60..49a3264140 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcBinary.java +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcBinary.java @@ -259,7 +259,6 @@ public abstract class CcBinary implements RuleConfiguredTargetFactory { ruleContext.getConfiguration()) .fromCommon(common) .addDeps(ImmutableList.of(CppHelper.mallocForTarget(ruleContext))) - .setFake(fake) .enableInterfaceSharedObjects(); linkingHelper.setStaticLinkType(LinkTargetType.STATIC_LIBRARY); ccLinkingOutputs = diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCommon.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCommon.java index e9ae312128..69c3f6061d 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCommon.java +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCommon.java @@ -70,6 +70,21 @@ import javax.annotation.Nullable; */ public final class CcCommon { + /** Name of the build variable for the sysroot path variable name. */ + public static final String SYSROOT_VARIABLE_NAME = "sysroot"; + + /** Name of the build variable for the path to the input file being processed. */ + public static final String INPUT_FILE_VARIABLE_NAME = "input_file"; + + /** + * Name of the build variable for includes that compiler needs to include into sources to be + * compiled. + */ + public static final String INCLUDES_VARIABLE_NAME = "includes"; + + /** Name of the build variable for stripopts for the strip action. */ + public static final String STRIPOPTS_VARIABLE_NAME = "stripopts"; + private static final String NO_COPTS_ATTRIBUTE = "nocopts"; /** 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()); } } diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLibrary.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLibrary.java index d07dd05769..660eff3045 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLibrary.java +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLibrary.java @@ -202,7 +202,7 @@ public abstract class CcLibrary implements RuleConfiguredTargetFactory { + "Did you mean to use 'linkstatic=1' instead?"); } - linkingHelper.setCreateDynamicLibrary(createDynamicLibrary); + linkingHelper.setShouldCreateDynamicLibrary(createDynamicLibrary); linkingHelper.setDynamicLibrary(soImplArtifact); // If the reason we're not creating a dynamic library is that the toolchain diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLinkingHelper.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLinkingHelper.java index 77dc80f562..0d96ff1b27 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLinkingHelper.java +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLinkingHelper.java @@ -14,6 +14,7 @@ package com.google.devtools.build.lib.rules.cpp; +import static com.google.devtools.build.lib.packages.BuildType.LABEL; import static java.util.stream.Collectors.joining; import com.google.common.base.Preconditions; @@ -23,6 +24,8 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Sets; import com.google.common.collect.Streams; import com.google.devtools.build.lib.actions.Artifact; +import com.google.devtools.build.lib.actions.FailAction; +import com.google.devtools.build.lib.analysis.AnalysisEnvironment; import com.google.devtools.build.lib.analysis.AnalysisUtils; import com.google.devtools.build.lib.analysis.FileProvider; import com.google.devtools.build.lib.analysis.LanguageDependentFragment; @@ -38,13 +41,17 @@ 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.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException; -import com.google.devtools.build.lib.rules.cpp.CcCommon.CoptsFilter; +import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.ExpansionException; import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.FeatureConfiguration; import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.Variables.VariablesExtension; +import com.google.devtools.build.lib.rules.cpp.Link.LinkStaticness; import com.google.devtools.build.lib.rules.cpp.Link.LinkTargetType; +import com.google.devtools.build.lib.rules.cpp.Link.Picness; import com.google.devtools.build.lib.rules.cpp.Link.Staticness; import com.google.devtools.build.lib.rules.cpp.LinkerInputs.LibraryToLink; import com.google.devtools.build.lib.syntax.Type; +import com.google.devtools.build.lib.vfs.FileSystemUtils; +import com.google.devtools.build.lib.vfs.PathFragment; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -54,7 +61,7 @@ import javax.annotation.Nullable; /** * A class to create C/C++ link 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 CppLinkActionBuilder. * * <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 @@ -133,17 +140,18 @@ public final class CcLinkingHelper { private final RuleContext ruleContext; private final CppSemantics semantics; private final BuildConfiguration configuration; + private final CppConfiguration cppConfiguration; private final List<Artifact> nonCodeLinkerInputs = new ArrayList<>(); private final List<String> linkopts = new ArrayList<>(); private final List<TransitiveInfoCollection> deps = new ArrayList<>(); private final NestedSetBuilder<Artifact> linkstamps = NestedSetBuilder.stableOrder(); private final List<Artifact> linkActionInputs = new ArrayList<>(); + private CppCompilationContext cppCompilationContext; @Nullable private Artifact dynamicLibrary; private LinkTargetType linkType = LinkTargetType.STATIC_LIBRARY; private boolean neverlink; - private boolean fake; private final List<LibraryToLink> staticLibraries = new ArrayList<>(); private final List<LibraryToLink> picStaticLibraries = new ArrayList<>(); @@ -155,8 +163,8 @@ public final class CcLinkingHelper { private boolean emitCcNativeLibrariesProvider; private boolean emitCcSpecificLinkParamsProvider; private boolean emitInterfaceSharedObjects; - private boolean createDynamicLibrary = true; - private boolean createStaticLibraries = true; + private boolean shouldCreateDynamicLibrary = true; + private boolean shouldCreateStaticLibraries = true; private final List<VariablesExtension> variablesExtensions = new ArrayList<>(); private final FeatureConfiguration featureConfiguration; @@ -187,6 +195,8 @@ public final class CcLinkingHelper { this.ccToolchain = Preconditions.checkNotNull(ccToolchain); this.fdoSupport = Preconditions.checkNotNull(fdoSupport); this.configuration = Preconditions.checkNotNull(configuration); + this.cppConfiguration = + Preconditions.checkNotNull(ruleContext.getFragment(CppConfiguration.class)); } /** Sets fields that overlap for cc_library and cc_binary rules. */ @@ -338,17 +348,6 @@ public final class CcLinkingHelper { 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 CcLinkingHelper setFake(boolean fake) { - this.fake = fake; - return this; - } - /* * Adds a suffix for paths of linked artifacts. Normally their paths are derived solely from rule * labels. In the case of multiple callers (e.g., aspects) acting on a single rule, they may @@ -403,14 +402,16 @@ public final class CcLinkingHelper { * generate a dynamic library. Note that the selection between dynamic or static linking is * performed at the binary rule level. */ - public CcLinkingHelper setCreateDynamicLibrary(boolean emitDynamicLibrary) { - this.createDynamicLibrary = emitDynamicLibrary; + public CcLinkingHelper setShouldCreateDynamicLibrary(boolean emitDynamicLibrary) { + this.shouldCreateDynamicLibrary = emitDynamicLibrary; return this; } - /** When createStaticLibraries is true, there are no actions created for static libraries. */ - public CcLinkingHelper setCreateStaticLibraries(boolean emitStaticLibraries) { - this.createStaticLibraries = emitStaticLibraries; + /** + * When shouldCreateStaticLibraries is true, there are no actions created for static libraries. + */ + public CcLinkingHelper setShouldCreateStaticLibraries(boolean emitStaticLibraries) { + this.shouldCreateStaticLibraries = emitStaticLibraries; return this; } @@ -438,8 +439,7 @@ public final class CcLinkingHelper { } } - CppModel model = initializeCppModel(); - model.setContext(cppCompilationContext); + this.cppCompilationContext = cppCompilationContext; // Create link actions (only if there are object files or if explicitly requested). CcLinkingOutputs ccLinkingOutputs = CcLinkingOutputs.EMPTY; @@ -452,7 +452,7 @@ public final class CcLinkingHelper { // generate any link actions, effectively disabling header checking in some cases. if (linkType.staticness() == Staticness.STATIC) { // TODO(bazel-team): This can't create the link action for a cc_binary yet. - ccLinkingOutputs = model.createCcLinkActions(ccOutputs, nonCodeLinkerInputs); + ccLinkingOutputs = createCcLinkActions(ccOutputs, nonCodeLinkerInputs); } } CcLinkingOutputs originalLinkingOutputs = ccLinkingOutputs; @@ -606,34 +606,6 @@ public final class CcLinkingHelper { outputGroups.put(DYNAMIC_LIBRARY_OUTPUT_GROUP_NAME, dynamicLibrary.build()); } - /** Creates the C/C++ compilation action creator. */ - private CppModel initializeCppModel() { - // TODO(plf): Split CppModel into compilation and linking and stop passing last two arguments. - return new CppModel( - ruleContext, - semantics, - ccToolchain, - fdoSupport, - configuration, - ImmutableList.of(), - CoptsFilter.alwaysPasses()) - .setLinkTargetType(linkType) - .setNeverLink(neverlink) - .addLinkActionInputs(linkActionInputs) - .setFake(fake) - .setAllowInterfaceSharedObjects(emitInterfaceSharedObjects) - .setCreateDynamicLibrary(createDynamicLibrary) - .setCreateStaticLibraries(createStaticLibraries) - // 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) - .setDynamicLibrary(dynamicLibrary) - .addLinkopts(linkopts) - .setFeatureConfiguration(featureConfiguration) - .addVariablesExtension(variablesExtensions) - .setLinkedArtifactNameSuffix(linkedArtifactNameSuffix); - } - private Runfiles collectCppRunfiles( CcLinkingOutputs ccLinkingOutputs, boolean linkingStatically) { Runfiles.Builder builder = @@ -703,4 +675,356 @@ public final class CcLinkingHelper { ? CcExecutionDynamicLibrariesProvider.EMPTY : new CcExecutionDynamicLibrariesProvider(builder.build()); } + + /** + * Constructs the C++ linker actions. It generally generates two actions, one for a static library + * and one for a dynamic library. If PIC is required for shared libraries, but not for binaries, + * it additionally creates a third action to generate a PIC static library. If PIC is required for + * shared libraries and binaries, then only PIC actions are registered. + * + * <p>For dynamic libraries, this method can additionally create an interface shared library that + * can be used for linking, but doesn't contain any executable code. This increases the number of + * cache hits for link actions. Call {@link #enableInterfaceSharedObjects()} to enable this + * behavior. + * + * @throws RuleErrorException + */ + private CcLinkingOutputs createCcLinkActions( + CcCompilationOutputs ccOutputs, Iterable<Artifact> nonCodeLinkerInputs) + throws RuleErrorException, InterruptedException { + // For now only handle static links. Note that the dynamic library link below ignores linkType. + // TODO(bazel-team): Either support non-static links or move this check to setStaticLinkType(). + Preconditions.checkState( + linkType.staticness() == Staticness.STATIC, "can only handle static links"); + + CcLinkingOutputs.Builder result = new CcLinkingOutputs.Builder(); + if (cppConfiguration.isLipoContextCollector()) { + // Don't try to create LIPO link actions in collector mode, + // because it needs some data that's not available at this point. + return result.build(); + } + AnalysisEnvironment env = ruleContext.getAnalysisEnvironment(); + boolean usePicForBinaries = CppHelper.usePic(ruleContext, ccToolchain, /* forBinary= */ true); + boolean usePicForSharedLibs = + CppHelper.usePic(ruleContext, ccToolchain, /* forBinary= */ false); + + PathFragment labelName = PathFragment.create(ruleContext.getLabel().getName()); + String libraryIdentifier = + ruleContext + .getPackageDirectory() + .getRelative(labelName.replaceName("lib" + labelName.getBaseName())) + .getPathString(); + + if (shouldCreateStaticLibraries) { + createStaticLibraries( + result, + env, + usePicForBinaries, + usePicForSharedLibs, + libraryIdentifier, + ccOutputs, + nonCodeLinkerInputs); + } + + if (shouldCreateDynamicLibrary) { + createDynamicLibrary(result, env, usePicForSharedLibs, libraryIdentifier, ccOutputs); + } + + return result.build(); + } + + private void createStaticLibraries( + CcLinkingOutputs.Builder result, + AnalysisEnvironment env, + boolean usePicForBinaries, + boolean usePicForSharedLibs, + String libraryIdentifier, + CcCompilationOutputs ccOutputs, + Iterable<Artifact> nonCodeLinkerInputs) + throws RuleErrorException, InterruptedException { + // Create static library (.a). The linkType only reflects whether the library is alwayslink or + // not. The PIC-ness is determined by whether we need to use PIC or not. There are three cases + // for (usePicForSharedLibs usePicForBinaries): + // + // (1) (false false) -> no pic code + // (2) (true false) -> shared libraries as pic, but not binaries + // (3) (true true) -> both shared libraries and binaries as pic + // + // In case (3), we always need PIC, so only create one static library containing the PIC + // object + // files. The name therefore does not match the content. + // + // Presumably, it is done this way because the .a file is an implicit output of every + // cc_library + // rule, so we can't use ".pic.a" that in the always-PIC case. + + // If the crosstool is configured to select an output artifact, we use that selection. + // Otherwise, we use linux defaults. + Artifact linkedArtifact = getLinkedArtifact(linkType); + + CppLinkAction maybePicAction = + newLinkActionBuilder(linkedArtifact) + .addObjectFiles(ccOutputs.getObjectFiles(usePicForBinaries)) + .addNonCodeInputs(nonCodeLinkerInputs) + .addLtoBitcodeFiles(ccOutputs.getLtoBitcodeFiles()) + .setLinkType(linkType) + .setLinkStaticness(LinkStaticness.FULLY_STATIC) + .addActionInputs(linkActionInputs) + .setLibraryIdentifier(libraryIdentifier) + .addVariablesExtensions(variablesExtensions) + .build(); + env.registerAction(maybePicAction); + if (usePicForBinaries) { + result.addPicStaticLibrary(maybePicAction.getOutputLibrary()); + } else { + result.addStaticLibrary(maybePicAction.getOutputLibrary()); + // Create a second static library (.pic.a). Only in case (2) do we need both PIC and non-PIC + // static libraries. In that case, the first static library contains the non-PIC code, and + // this + // one contains the PIC code, so the names match the content. + if (usePicForSharedLibs) { + LinkTargetType picLinkType = + (linkType == LinkTargetType.ALWAYS_LINK_STATIC_LIBRARY) + ? LinkTargetType.ALWAYS_LINK_PIC_STATIC_LIBRARY + : LinkTargetType.PIC_STATIC_LIBRARY; + + // If the crosstool is configured to select an output artifact, we use that selection. + // Otherwise, we use linux defaults. + Artifact picArtifact = getLinkedArtifact(picLinkType); + CppLinkAction picAction = + newLinkActionBuilder(picArtifact) + .addObjectFiles(ccOutputs.getObjectFiles(/* usePic= */ true)) + .addLtoBitcodeFiles(ccOutputs.getLtoBitcodeFiles()) + .setLinkType(picLinkType) + .setLinkStaticness(LinkStaticness.FULLY_STATIC) + .addActionInputs(linkActionInputs) + .setLibraryIdentifier(libraryIdentifier) + .addVariablesExtensions(variablesExtensions) + .build(); + env.registerAction(picAction); + result.addPicStaticLibrary(picAction.getOutputLibrary()); + } + } + } + + private void createDynamicLibrary( + CcLinkingOutputs.Builder result, + AnalysisEnvironment env, + boolean usePicForSharedLibs, + String libraryIdentifier, + CcCompilationOutputs ccOutputs) + throws RuleErrorException, InterruptedException { + // Create dynamic library. + Artifact soImpl; + String mainLibraryIdentifier; + if (dynamicLibrary == null) { + // If the crosstool is configured to select an output artifact, we use that selection. + // Otherwise, we use linux defaults. + soImpl = getLinkedArtifact(LinkTargetType.DYNAMIC_LIBRARY); + mainLibraryIdentifier = libraryIdentifier; + } else { + // This branch is only used for vestigial Google-internal rules where the name of the output + // file is explicitly specified in the BUILD file and as such, is platform-dependent. Thus, + // we just hardcode some reasonable logic to compute the library identifier and hope that this + // will eventually go away. + soImpl = dynamicLibrary; + mainLibraryIdentifier = + FileSystemUtils.removeExtension(soImpl.getRootRelativePath().getPathString()); + } + + List<String> sonameLinkopts = ImmutableList.of(); + Artifact soInterface = null; + if (CppHelper.useInterfaceSharedObjects(cppConfiguration, ccToolchain) + && emitInterfaceSharedObjects) { + soInterface = + CppHelper.getLinuxLinkedArtifact( + ruleContext, + configuration, + LinkTargetType.INTERFACE_DYNAMIC_LIBRARY, + linkedArtifactNameSuffix); + // TODO(b/28946988): Remove this hard-coded flag. + if (!featureConfiguration.isEnabled(CppRuleClasses.TARGETS_WINDOWS)) { + sonameLinkopts = + ImmutableList.of( + "-Wl,-soname=" + + SolibSymlinkAction.getDynamicLibrarySoname( + soImpl.getRootRelativePath(), /* preserveName= */ false)); + } + } + + CppLinkActionBuilder dynamicLinkActionBuilder = + newLinkActionBuilder(soImpl) + .setInterfaceOutput(soInterface) + .addObjectFiles(ccOutputs.getObjectFiles(usePicForSharedLibs)) + .addNonCodeInputs(ccOutputs.getHeaderTokenFiles()) + .addLtoBitcodeFiles(ccOutputs.getLtoBitcodeFiles()) + .setLinkType(LinkTargetType.DYNAMIC_LIBRARY) + .setLinkStaticness(LinkStaticness.DYNAMIC) + .addActionInputs(linkActionInputs) + .setLibraryIdentifier(mainLibraryIdentifier) + .addLinkopts(linkopts) + .addLinkopts(sonameLinkopts) + .setRuntimeInputs( + ArtifactCategory.DYNAMIC_LIBRARY, + ccToolchain.getDynamicRuntimeLinkMiddleman(), + ccToolchain.getDynamicRuntimeLinkInputs()) + .addVariablesExtensions(variablesExtensions); + + if (featureConfiguration.isEnabled(CppRuleClasses.TARGETS_WINDOWS)) { + // On Windows, we cannot build a shared library with symbols unresolved, so here we + // dynamically + // link to all it's dependencies. + CcLinkParams.Builder ccLinkParamsBuilder = + CcLinkParams.builder(/* linkingStatically= */ false, /* linkShared= */ true); + ccLinkParamsBuilder.addCcLibrary(ruleContext); + dynamicLinkActionBuilder.addLinkParams(ccLinkParamsBuilder.build(), ruleContext); + + // If windows_export_all_symbols feature is enabled, bazel parses object files to generate + // DEF file and use it to export symbols. The generated DEF file won't be used if a custom + // DEF file is specified by win_def_file attribute. + if (CppHelper.shouldUseGeneratedDefFile(ruleContext, featureConfiguration)) { + Artifact generatedDefFile = + CppHelper.createDefFileActions( + ruleContext, + ruleContext.getPrerequisiteArtifact("$def_parser", Mode.HOST), + ccOutputs.getObjectFiles(false), + SolibSymlinkAction.getDynamicLibrarySoname(soImpl.getRootRelativePath(), true)); + dynamicLinkActionBuilder.setDefFile(generatedDefFile); + } + + // If user specifies a custom DEF file, then we use this one instead of the generated one. + Artifact customDefFile = null; + if (ruleContext.isAttrDefined("win_def_file", LABEL)) { + customDefFile = ruleContext.getPrerequisiteArtifact("win_def_file", Mode.TARGET); + } + if (customDefFile != null) { + dynamicLinkActionBuilder.setDefFile(customDefFile); + } + } + + if (!ccOutputs.getLtoBitcodeFiles().isEmpty() + && featureConfiguration.isEnabled(CppRuleClasses.THIN_LTO)) { + dynamicLinkActionBuilder.setLtoIndexing(true); + dynamicLinkActionBuilder.setUsePicForLtoBackendActions(usePicForSharedLibs); + CppLinkAction indexAction = dynamicLinkActionBuilder.build(); + if (indexAction != null) { + env.registerAction(indexAction); + } + + dynamicLinkActionBuilder.setLtoIndexing(false); + } + + CppLinkAction dynamicLinkAction = dynamicLinkActionBuilder.build(); + env.registerAction(dynamicLinkAction); + + LibraryToLink dynamicLibrary = dynamicLinkAction.getOutputLibrary(); + LibraryToLink interfaceLibrary = dynamicLinkAction.getInterfaceOutputLibrary(); + + // If shared library has neverlink=1, then leave it untouched. Otherwise, + // create a mangled symlink for it and from now on reference it through + // mangled name only. + // + // When COPY_DYNAMIC_LIBRARIES_TO_BINARY is enabled, we don't need to create the special + // solibDir, instead we use the original interface library and dynamic library. + if (neverlink + || featureConfiguration.isEnabled(CppRuleClasses.COPY_DYNAMIC_LIBRARIES_TO_BINARY)) { + result.addDynamicLibrary(interfaceLibrary == null ? dynamicLibrary : interfaceLibrary); + result.addExecutionDynamicLibrary(dynamicLibrary); + } else { + Artifact implLibraryLinkArtifact = + SolibSymlinkAction.getDynamicLibrarySymlink( + ruleContext, + ccToolchain.getSolibDirectory(), + dynamicLibrary.getArtifact(), + /* preserveName= */ false, + /* prefixConsumer= */ false, + ruleContext.getConfiguration()); + LibraryToLink implLibraryLink = + LinkerInputs.solibLibraryToLink( + implLibraryLinkArtifact, dynamicLibrary.getArtifact(), libraryIdentifier); + result.addExecutionDynamicLibrary(implLibraryLink); + + LibraryToLink libraryLink; + if (interfaceLibrary == null) { + libraryLink = implLibraryLink; + } else { + Artifact libraryLinkArtifact = + SolibSymlinkAction.getDynamicLibrarySymlink( + ruleContext, + ccToolchain.getSolibDirectory(), + interfaceLibrary.getArtifact(), + /* preserveName= */ false, + /* prefixConsumer= */ false, + ruleContext.getConfiguration()); + libraryLink = + LinkerInputs.solibLibraryToLink( + libraryLinkArtifact, interfaceLibrary.getArtifact(), libraryIdentifier); + } + result.addDynamicLibrary(libraryLink); + } + } + + private CppLinkActionBuilder newLinkActionBuilder(Artifact outputArtifact) { + return new CppLinkActionBuilder( + ruleContext, outputArtifact, ccToolchain, fdoSupport, featureConfiguration, semantics) + .setCrosstoolInputs(ccToolchain.getLink()) + .addNonCodeInputs(cppCompilationContext.getTransitiveCompilationPrerequisites()); + } + + /** + * Returns the linked artifact resulting from a linking of the given type. Consults the feature + * configuration to obtain an action_config that provides the artifact. If the feature + * configuration provides no artifact, uses a default. + * + * <p>We cannot assume that the feature configuration contains an action_config for the link + * action, because the linux link action depends on hardcoded values in + * LinkCommandLine.getRawLinkArgv(), which are applied on the condition that an action_config is + * not present. TODO(b/30393154): Assert that the given link action has an action_config. + * + * @throws RuleErrorException + */ + private Artifact getLinkedArtifact(LinkTargetType linkTargetType) throws RuleErrorException { + Artifact result = null; + Artifact linuxDefault = + CppHelper.getLinuxLinkedArtifact( + ruleContext, configuration, linkTargetType, linkedArtifactNameSuffix); + + try { + String maybePicName = ruleContext.getLabel().getName() + linkedArtifactNameSuffix; + if (linkTargetType.picness() == Picness.PIC) { + maybePicName = + CppHelper.getArtifactNameForCategory( + ruleContext, ccToolchain, ArtifactCategory.PIC_FILE, maybePicName); + } + String linkedName = + CppHelper.getArtifactNameForCategory( + ruleContext, ccToolchain, linkTargetType.getLinkerOutput(), maybePicName); + PathFragment artifactFragment = + PathFragment.create(ruleContext.getLabel().getName()) + .getParentDirectory() + .getRelative(linkedName); + + result = + ruleContext.getPackageRelativeArtifact( + artifactFragment, + configuration.getBinDirectory(ruleContext.getRule().getRepository())); + } catch (ExpansionException e) { + ruleContext.throwWithRuleError(e.getMessage()); + } + + // If the linked artifact is not the linux default, then a FailAction is generated for the + // linux default to satisfy the requirement of the implicit output. + // TODO(b/30132703): Remove the implicit outputs of cc_library. + if (!result.equals(linuxDefault)) { + ruleContext.registerAction( + new FailAction( + ruleContext.getActionOwner(), + ImmutableList.of(linuxDefault), + String.format( + "the given toolchain supports creation of %s instead of %s", + linuxDefault.getExecPathString(), result.getExecPathString()))); + } + + return result; + } } diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchain.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchain.java index f52a13c4a6..885f031fac 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchain.java +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchain.java @@ -792,7 +792,7 @@ public class CcToolchain implements RuleConfiguredTargetFactory { PathFragment sysroot = calculateSysroot(ruleContext, defaultSysroot); if (sysroot != null) { - variables.addStringVariable(CppModel.SYSROOT_VARIABLE_NAME, sysroot.getPathString()); + variables.addStringVariable(CcCommon.SYSROOT_VARIABLE_NAME, sysroot.getPathString()); } addBuildVariables(ruleContext, variables); diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CompileCommandLine.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CompileCommandLine.java index 37960deb86..76d9e47223 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CompileCommandLine.java +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CompileCommandLine.java @@ -153,8 +153,9 @@ public final class CompileCommandLine { * explicit attribute, not using platform-dependent garbage bag that copts is). */ public ImmutableList<String> getCopts() { - if (variables.isAvailable(CppModel.USER_COMPILE_FLAGS_VARIABLE_NAME)) { - return Variables.toStringList(variables, CppModel.USER_COMPILE_FLAGS_VARIABLE_NAME); + if (variables.isAvailable(CcCompilationHelper.USER_COMPILE_FLAGS_VARIABLE_NAME)) { + return Variables.toStringList( + variables, CcCompilationHelper.USER_COMPILE_FLAGS_VARIABLE_NAME); } else { return ImmutableList.of(); } diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppHelper.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppHelper.java index 126fda003d..3d0b2c0666 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppHelper.java +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppHelper.java @@ -945,10 +945,11 @@ public class CppHelper { featureConfiguration.getToolForAction(CppCompileAction.STRIP_ACTION_NAME)); Variables variables = new Variables.Builder(toolchain.getBuildVariables()) - .addStringVariable(CppModel.OUTPUT_FILE_VARIABLE_NAME, output.getExecPathString()) + .addStringVariable( + CcCompilationHelper.OUTPUT_FILE_VARIABLE_NAME, output.getExecPathString()) .addStringSequenceVariable( - CppModel.STRIPOPTS_VARIABLE_NAME, cppConfiguration.getStripOpts()) - .addStringVariable(CppModel.INPUT_FILE_VARIABLE_NAME, input.getExecPathString()) + CcCommon.STRIPOPTS_VARIABLE_NAME, cppConfiguration.getStripOpts()) + .addStringVariable(CcCommon.INPUT_FILE_VARIABLE_NAME, input.getExecPathString()) .build(); ImmutableList<String> commandLine = ImmutableList.copyOf( diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppLinkstampCompileHelper.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppLinkstampCompileHelper.java index dad73d3227..49be968256 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppLinkstampCompileHelper.java +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppLinkstampCompileHelper.java @@ -138,39 +138,42 @@ public class CppLinkstampCompileHelper { Variables.Builder variables = new Variables.Builder(ccToolchainProvider.getBuildVariables()); // We need to force inclusion of build_info headers variables.addStringSequenceVariable( - CppModel.INCLUDES_VARIABLE_NAME, + CcCommon.INCLUDES_VARIABLE_NAME, buildInfoHeaderArtifacts .stream() .map(Artifact::getExecPathString) .collect(ImmutableList.toImmutableList())); // Input/Output files. - variables.addStringVariable(CppModel.SOURCE_FILE_VARIABLE_NAME, sourceFile.getExecPathString()); - variables.addStringVariable(CppModel.OUTPUT_FILE_VARIABLE_NAME, outputFile.getExecPathString()); variables.addStringVariable( - CppModel.OUTPUT_OBJECT_FILE_VARIABLE_NAME, outputFile.getExecPathString()); + CcCompilationHelper.SOURCE_FILE_VARIABLE_NAME, sourceFile.getExecPathString()); + variables.addStringVariable( + CcCompilationHelper.OUTPUT_FILE_VARIABLE_NAME, outputFile.getExecPathString()); + variables.addStringVariable( + CcCompilationHelper.OUTPUT_OBJECT_FILE_VARIABLE_NAME, outputFile.getExecPathString()); // Include directories for (normal includes with ".", empty quote- and system- includes). variables.addStringSequenceVariable( - CppModel.INCLUDE_PATHS_VARIABLE_NAME, ImmutableList.of(".")); + CcCompilationHelper.INCLUDE_PATHS_VARIABLE_NAME, ImmutableList.of(".")); variables.addStringSequenceVariable( - CppModel.QUOTE_INCLUDE_PATHS_VARIABLE_NAME, ImmutableList.of()); + CcCompilationHelper.QUOTE_INCLUDE_PATHS_VARIABLE_NAME, ImmutableList.of()); variables.addStringSequenceVariable( - CppModel.SYSTEM_INCLUDE_PATHS_VARIABLE_NAME, ImmutableList.of()); + CcCompilationHelper.SYSTEM_INCLUDE_PATHS_VARIABLE_NAME, ImmutableList.of()); // Legacy flags coming from fields such as compiler_flag variables.addLazyStringSequenceVariable( - CppModel.LEGACY_COMPILE_FLAGS_VARIABLE_NAME, - CppModel.getLegacyCompileFlagsSupplier( + CcCompilationHelper.LEGACY_COMPILE_FLAGS_VARIABLE_NAME, + CcCompilationHelper.getLegacyCompileFlagsSupplier( cppConfiguration, ccToolchainProvider, sourceFile.getExecPathString(), ImmutableSet.of())); // Unfilterable flags coming from unfiltered_cxx_flag fields variables.addLazyStringSequenceVariable( - CppModel.UNFILTERED_COMPILE_FLAGS_VARIABLE_NAME, - CppModel.getUnfilteredCompileFlagsSupplier(ccToolchainProvider, ImmutableSet.of())); + CcCompilationHelper.UNFILTERED_COMPILE_FLAGS_VARIABLE_NAME, + CcCompilationHelper.getUnfilteredCompileFlagsSupplier( + ccToolchainProvider, ImmutableSet.of())); // Collect all preprocessor defines, and in each replace ${LABEL} by labelReplacements, and // ${OUTPUT_PATH} with outputPathReplacement. variables.addStringSequenceVariable( - CppModel.PREPROCESSOR_DEFINES_VARIABLE_NAME, + CcCompilationHelper.PREPROCESSOR_DEFINES_VARIABLE_NAME, computeAllLinkstampDefines( labelReplacement, outputReplacement, @@ -180,7 +183,7 @@ public class CppLinkstampCompileHelper { codeCoverageEnabled)); // For dynamic libraries, produce position independent code. if (needsPic) { - variables.addStringVariable(CppModel.PIC_VARIABLE_NAME, ""); + variables.addStringVariable(CcCompilationHelper.PIC_VARIABLE_NAME, ""); } return variables.build(); diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppModel.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppModel.java deleted file mode 100644 index 34c625b5b9..0000000000 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppModel.java +++ /dev/null @@ -1,1699 +0,0 @@ -// 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 com.google.devtools.build.lib.packages.BuildType.LABEL; - -import com.google.common.base.Preconditions; -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.actions.FailAction; -import com.google.devtools.build.lib.analysis.AnalysisEnvironment; -import com.google.devtools.build.lib.analysis.RuleContext; -import com.google.devtools.build.lib.analysis.TransitiveInfoCollection; -import com.google.devtools.build.lib.analysis.config.BuildConfiguration; -import com.google.devtools.build.lib.analysis.config.PerLabelOptions; -import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget.Mode; -import com.google.devtools.build.lib.analysis.test.InstrumentedFilesCollector; -import com.google.devtools.build.lib.cmdline.Label; -import com.google.devtools.build.lib.packages.BuildType; -import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException; -import com.google.devtools.build.lib.rules.cpp.CcCommon.CoptsFilter; -import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.ExpansionException; -import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.FeatureConfiguration; -import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.Variables.StringSequenceBuilder; -import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.Variables.VariablesExtension; -import com.google.devtools.build.lib.rules.cpp.CppCompileAction.DotdFile; -import com.google.devtools.build.lib.rules.cpp.Link.LinkStaticness; -import com.google.devtools.build.lib.rules.cpp.Link.LinkTargetType; -import com.google.devtools.build.lib.rules.cpp.Link.Picness; -import com.google.devtools.build.lib.rules.cpp.Link.Staticness; -import com.google.devtools.build.lib.rules.cpp.LinkerInputs.LibraryToLink; -import com.google.devtools.build.lib.util.FileType; -import com.google.devtools.build.lib.vfs.FileSystemUtils; -import com.google.devtools.build.lib.vfs.PathFragment; -import java.util.ArrayList; -import java.util.Collection; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Set; - -/** - * Representation of a C/C++ compilation. Its purpose is to share the code that creates compilation - * actions between all classes that need to do so. It follows the builder pattern - load up the - * necessary settings and then call {@link #createCcCompileActions}. - * - * <p>This class is not thread-safe, and it should only be used once for each set of source files, - * i.e. calling {@link #createCcCompileActions} will throw an Exception if called twice. - */ -public final class CppModel { - - /** Name of the build variable for the path to the source file being compiled. */ - public static final String SOURCE_FILE_VARIABLE_NAME = "source_file"; - - /** Name of the build variable for the path to the input file being processed. */ - public static final String INPUT_FILE_VARIABLE_NAME = "input_file"; - - /** Name of the build variable for the path to the compilation output file. */ - public static final String OUTPUT_FILE_VARIABLE_NAME = "output_file"; - - /** - * Name of the build variable for the path to the compilation output file in case of assembly - * source. - */ - public static final String OUTPUT_ASSEMBLY_FILE_VARIABLE_NAME = "output_assembly_file"; - - /** - * Name of the build variable for the path to the compilation output file in case of preprocessed - * source. - */ - public static final String OUTPUT_PREPROCESS_FILE_VARIABLE_NAME = "output_preprocess_file"; - - /** Name of the build variable for the path to the output file when output is an object file. */ - public static final String OUTPUT_OBJECT_FILE_VARIABLE_NAME = "output_object_file"; - - /** Name of the build variable for the module file name. */ - public static final String MODULE_NAME_VARIABLE_NAME = "module_name"; - - /** Name of the build variable for the module map file name. */ - public static final String MODULE_MAP_FILE_VARIABLE_NAME = "module_map_file"; - - /** Name of the build variable for the dependent module map file name. */ - public static final String DEPENDENT_MODULE_MAP_FILES_VARIABLE_NAME = - "dependent_module_map_files"; - - /** Name of the build variable for the collection of module files. */ - public static final String MODULE_FILES_VARIABLE_NAME = "module_files"; - - /** - * Name of the build variable for includes that compiler needs to include into sources to be - * compiled. - */ - public static final String INCLUDES_VARIABLE_NAME = "includes"; - - /** - * Name of the build variable for the collection of include paths. - * - * @see CppCompilationContext#getIncludeDirs(). - */ - public static final String INCLUDE_PATHS_VARIABLE_NAME = "include_paths"; - - /** - * Name of the build variable for the collection of quote include paths. - * - * @see CppCompilationContext#getIncludeDirs(). - */ - public static final String QUOTE_INCLUDE_PATHS_VARIABLE_NAME = "quote_include_paths"; - - /** - * Name of the build variable for the collection of system include paths. - * - * @see CppCompilationContext#getIncludeDirs(). - */ - public static final String SYSTEM_INCLUDE_PATHS_VARIABLE_NAME = "system_include_paths"; - - /** Name of the build variable for the dependency file path */ - public static final String DEPENDENCY_FILE_VARIABLE_NAME = "dependency_file"; - - /** Name of the build variable for the collection of macros defined for preprocessor. */ - public static final String PREPROCESSOR_DEFINES_VARIABLE_NAME = "preprocessor_defines"; - - /** Name of the build variable present when the output is compiled as position independent. */ - public static final String PIC_VARIABLE_NAME = "pic"; - - /** Name of the build variable for the gcov coverage file path. */ - public static final String GCOV_GCNO_FILE_VARIABLE_NAME = "gcov_gcno_file"; - - /** Name of the build variable for the per object debug info file. */ - public static final String PER_OBJECT_DEBUG_INFO_FILE_VARIABLE_NAME = - "per_object_debug_info_file"; - - /** Name of the build variable for the LTO indexing bitcode file. */ - public static final String LTO_INDEXING_BITCODE_FILE_VARIABLE_NAME = "lto_indexing_bitcode_file"; - - /** Name of the build variable for stripopts for the strip action. */ - public static final String STRIPOPTS_VARIABLE_NAME = "stripopts"; - - /** - * Build variable for all flags coming from legacy crosstool fields, such as compiler_flag, - * optional_compiler_flag, cxx_flag, optional_cxx_flag. - */ - public static final String LEGACY_COMPILE_FLAGS_VARIABLE_NAME = "legacy_compile_flags"; - - /** - * Build variable for all flags coming from copt rule attribute, and from --copt, --cxxopt, or - * --conlyopt options. - */ - public static final String USER_COMPILE_FLAGS_VARIABLE_NAME = "user_compile_flags"; - - /** Build variable for flags coming from unfiltered_cxx_flag CROSSTOOL fields. */ - public static final String UNFILTERED_COMPILE_FLAGS_VARIABLE_NAME = "unfiltered_compile_flags"; - - /** Name of the build variable for the sysroot path variable name. */ - public static final String SYSROOT_VARIABLE_NAME = "sysroot"; - - private static final String PIC_CONFIGURATION_ERROR = - "PIC compilation is requested but the toolchain does not support it"; - - private final CppSemantics semantics; - private final RuleContext ruleContext; - private final BuildConfiguration configuration; - private final CppConfiguration cppConfiguration; - - // compile model - private CppCompilationContext context; - private final Set<CppSource> sourceFiles = new LinkedHashSet<>(); - private final List<Artifact> compilationMandatoryInputs = new ArrayList<>(); - private final List<Artifact> additionalIncludeScanningRoots = new ArrayList<>(); - private final ImmutableList<String> copts; - private final CoptsFilter coptsFilter; - private boolean fake; - private boolean maySaveTemps; - private CcCompilationOutputs compilationOutputs; - - // link model - private final List<String> linkopts = new ArrayList<>(); - private LinkTargetType linkType = LinkTargetType.STATIC_LIBRARY; - private boolean neverLink; - private final List<Artifact> linkActionInputs = new ArrayList<>(); - private boolean allowInterfaceSharedObjects; - private boolean createDynamicLibrary = true; - private boolean createStaticLibraries = true; - private Artifact soImplArtifact; - private FeatureConfiguration featureConfiguration; - private List<VariablesExtension> variablesExtensions = new ArrayList<>(); - private final CcToolchainProvider ccToolchain; - private final FdoSupportProvider fdoSupport; - private String linkedArtifactNameSuffix = ""; - private final ImmutableSet<String> features; - private boolean generateNoPic = true; - - public CppModel( - RuleContext ruleContext, - CppSemantics semantics, - CcToolchainProvider ccToolchain, - FdoSupportProvider fdoSupport, - ImmutableList<String> copts) { - this( - ruleContext, - semantics, - ccToolchain, - fdoSupport, - ruleContext.getConfiguration(), - copts, - CoptsFilter.alwaysPasses()); - } - - public CppModel( - RuleContext ruleContext, - CppSemantics semantics, - CcToolchainProvider ccToolchain, - FdoSupportProvider fdoSupport, - ImmutableList<String> copts, - CoptsFilter coptsFilter) { - this( - ruleContext, - semantics, - ccToolchain, - fdoSupport, - ruleContext.getConfiguration(), - copts, - coptsFilter); - } - - public CppModel( - RuleContext ruleContext, - CppSemantics semantics, - CcToolchainProvider ccToolchain, - FdoSupportProvider fdoSupport, - BuildConfiguration configuration, - ImmutableList<String> copts, - CoptsFilter coptsFilter) { - this.ruleContext = Preconditions.checkNotNull(ruleContext); - this.semantics = semantics; - this.ccToolchain = Preconditions.checkNotNull(ccToolchain); - this.fdoSupport = Preconditions.checkNotNull(fdoSupport); - this.configuration = configuration; - this.copts = copts; - this.coptsFilter = Preconditions.checkNotNull(coptsFilter); - cppConfiguration = ruleContext.getFragment(CppConfiguration.class); - features = ruleContext.getFeatures(); - } - - private Artifact getDwoFile(Artifact outputFile) { - return ruleContext.getRelatedArtifact(outputFile.getRootRelativePath(), ".dwo"); - } - - private Artifact getLtoIndexingFile(Artifact outputFile) { - String ext = Iterables.getOnlyElement(CppFileTypes.LTO_INDEXING_OBJECT_FILE.getExtensions()); - return ruleContext.getRelatedArtifact(outputFile.getRootRelativePath(), ext); - } - - /** - * If the cpp compilation is a fake, then it creates only a single compile action without PIC. - * Defaults to false. - */ - public CppModel setFake(boolean fake) { - this.fake = fake; - return this; - } - - /** - * Whether to create actions for temps. This defaults to false. - */ - public CppModel setSaveTemps(boolean maySaveTemps) { - this.maySaveTemps = maySaveTemps; - return this; - } - - /** - * Sets the compilation context, i.e. include directories and allowed header files inclusions. - */ - public CppModel setContext(CppCompilationContext context) { - this.context = context; - return this; - } - - /** - * Adds a single source file to be compiled. Note that this should only be called for primary - * compilation units, including module files or headers to be parsed or preprocessed. - */ - public CppModel addCompilationUnitSources( - Iterable<Artifact> sourceFiles, Label sourceLabel, CppSource.Type type) { - for (Artifact sourceFile : sourceFiles) { - this.sourceFiles.add(CppSource.create(sourceFile, sourceLabel, type)); - } - return this; - } - - /** - * Adds all the source files. Note that this should only be called for primary compilation units, - * including module files or headers to be parsed or preprocessed. - */ - public CppModel addCompilationUnitSources(Set<CppSource> sources) { - this.sourceFiles.addAll(sources); - return this; - } - - /** Adds compilation mandatory inputs. */ - public CppModel addCompilationMandatoryInputs(Collection<Artifact> compilationMandatoryInputs) { - this.compilationMandatoryInputs.addAll(compilationMandatoryInputs); - return this; - } - - /** Adds additional includes to be scanned. */ - public CppModel addAdditionalIncludeScanningRoots( - Collection<Artifact> additionalIncludeScanningRoots) { - this.additionalIncludeScanningRoots.addAll(additionalIncludeScanningRoots); - return this; - } - - /** - * Adds the given linkopts to the optional dynamic library link command. - */ - public CppModel addLinkopts(Collection<String> linkopts) { - this.linkopts.addAll(linkopts); - return this; - } - - /** - * Adds the given variablesExensions for templating the crosstool. - * - * <p>In general, we prefer the build variables (especially those that derive strictly from - * the configuration) be learned by inspecting the CcToolchain, as passed to the rule in the - * CcToolchainProvider. However, for build variables that must be injected into the rule - * implementation (ex. build variables learned from the BUILD file), should be added using the - * VariablesExtension abstraction. This allows the injection to construct non-trivial build - * variables (lists, ect.). - */ - public CppModel addVariablesExtension(Collection<VariablesExtension> variablesExtensions) { - this.variablesExtensions.addAll(variablesExtensions); - return this; - } - - /** - * Sets the link type used for the link actions. Note that only static links are supported at this - * time. - */ - public CppModel setLinkTargetType(LinkTargetType linkType) { - this.linkType = linkType; - return this; - } - - public CppModel setNeverLink(boolean neverLink) { - this.neverLink = neverLink; - return this; - } - - /** - * Adds an artifact to the inputs of any link actions created by this CppModel. - */ - public CppModel addLinkActionInputs(Collection<Artifact> inputs) { - this.linkActionInputs.addAll(inputs); - return this; - } - - /** - * Whether to allow interface dynamic libraries. Note that setting this to true only has an effect - * if the configuration allows it. Defaults to false. - */ - public CppModel setAllowInterfaceSharedObjects(boolean allowInterfaceSharedObjects) { - // TODO(bazel-team): Set the default to true, and require explicit action to disable it. - this.allowInterfaceSharedObjects = allowInterfaceSharedObjects; - return this; - } - - public CppModel setCreateDynamicLibrary(boolean createDynamicLibrary) { - this.createDynamicLibrary = createDynamicLibrary; - return this; - } - - public CppModel setCreateStaticLibraries(boolean createStaticLibraries) { - this.createStaticLibraries = createStaticLibraries; - return this; - } - - public CppModel setDynamicLibrary(Artifact soImplFilename) { - this.soImplArtifact = soImplFilename; - return this; - } - - /** Sets the feature configuration to be used for C/C++ actions. */ - public CppModel setFeatureConfiguration(FeatureConfiguration featureConfiguration) { - this.featureConfiguration = featureConfiguration; - return this; - } - - /* - * Adds a suffix for paths of linked artifacts. Normally their paths are derived solely from rule - * labels. In the case of multiple callers (e.g., aspects) acting on a single rule, they may - * generate the same linked artifact and therefore lead to artifact conflicts. This method - * provides a way to avoid this artifact conflict by allowing different callers acting on the same - * rule to provide a suffix that will be used to scope their own linked artifacts. - */ - public CppModel setLinkedArtifactNameSuffix(String suffix) { - this.linkedArtifactNameSuffix = suffix; - return this; - } - - /** no-PIC actions won't be generated. */ - public CppModel setGenerateNoPic(boolean generateNoPic) { - this.generateNoPic = generateNoPic; - return this; - } - - /** - * @returns whether we want to provide header modules for the current target. - */ - private boolean shouldProvideHeaderModules() { - return featureConfiguration.isEnabled(CppRuleClasses.HEADER_MODULES) - && !cppConfiguration.isLipoContextCollector(); - } - - /** - * @return the non-pic header module artifact for the current target. - */ - public Artifact getHeaderModule(Artifact moduleMapArtifact) { - PathFragment objectDir = CppHelper.getObjDirectory(ruleContext.getLabel()); - PathFragment outputName = objectDir.getRelative(moduleMapArtifact.getRootRelativePath()); - return ruleContext.getRelatedArtifact(outputName, ".pcm"); - } - - /** - * @return the pic header module artifact for the current target. - */ - public Artifact getPicHeaderModule(Artifact moduleMapArtifact) { - PathFragment objectDir = CppHelper.getObjDirectory(ruleContext.getLabel()); - PathFragment outputName = objectDir.getRelative(moduleMapArtifact.getRootRelativePath()); - return ruleContext.getRelatedArtifact(outputName, ".pic.pcm"); - } - - /** @return whether this target needs to generate pic actions. */ - private boolean getGeneratePicActions() { - return featureConfiguration.isEnabled(CppRuleClasses.PIC) - && CppHelper.usePic(ruleContext, ccToolchain, false); - } - - /** @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 a pic header module. - */ - public boolean getGeneratesPicHeaderModule() { - return shouldProvideHeaderModules() && !fake && getGeneratePicActions(); - } - - /** - * @return whether this target needs to generate a non-pic header module. - */ - public boolean getGeneratesNoPicHeaderModule() { - return shouldProvideHeaderModules() && !fake && getGenerateNoPicActions(); - } - - /** - * Returns a {@code CppCompileActionBuilder} with the common fields for a C++ compile action being - * initialized. - */ - private CppCompileActionBuilder initializeCompileAction(Artifact sourceArtifact) { - CppCompileActionBuilder builder = createCompileActionBuilder(sourceArtifact); - builder.setFeatureConfiguration(featureConfiguration); - - return builder; - } - - /** Get the safe path strings for a list of paths to use in the build variables. */ - private ImmutableSet<String> getSafePathStrings(Collection<PathFragment> paths) { - ImmutableSet.Builder<String> result = ImmutableSet.builder(); - for (PathFragment path : paths) { - result.add(path.getSafePathString()); - } - return result.build(); - } - - /** - * Supplier that computes unfiltered_compile_flags lazily at the execution phase. - * - * <p>Dear friends of the lambda, this method exists to limit the scope of captured variables - * only to arguments (to prevent accidental capture of enclosing instance which could regress - * memory). - */ - public static Supplier<ImmutableList<String>> getUnfilteredCompileFlagsSupplier( - CcToolchainProvider ccToolchain, ImmutableSet<String> features) { - return () -> ccToolchain.getUnfilteredCompilerOptions(features); - } - - /** - * Supplier that computes legacy_compile_flags lazily at the execution phase. - * - * <p>Dear friends of the lambda, this method exists to limit the scope of captured variables only - * to arguments (to prevent accidental capture of enclosing instance which could regress memory). - */ - public static Supplier<ImmutableList<String>> getLegacyCompileFlagsSupplier( - CppConfiguration cppConfiguration, - 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(); - }; - } - - 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); - } - - private ImmutableList<String> collectPerFileCopts(Artifact sourceFile, Label sourceLabel) { - return cppConfiguration - .getPerFileCopts() - .stream() - .filter( - perLabelOptions -> - (sourceLabel != null && perLabelOptions.isIncluded(sourceLabel)) - || perLabelOptions.isIncluded(sourceFile)) - .map(PerLabelOptions::getOptions) - .flatMap(options -> options.stream()) - .collect(ImmutableList.toImmutableList()); - } - - /** Returns true if Dotd file should be generated. */ - private boolean isGenerateDotdFile(Artifact sourceArtifact) { - return CppFileTypes.headerDiscoveryRequired(sourceArtifact) - && !featureConfiguration.isEnabled(CppRuleClasses.PARSE_SHOWINCLUDES); - } - - /** - * Constructs the C++ compiler actions. It generally creates one action for every specified source - * file. It takes into account LIPO, fake-ness, coverage, and PIC, in addition to using the - * settings specified on the current object. This method should only be called once. - */ - public CcCompilationOutputs createCcCompileActions() throws RuleErrorException { - CcCompilationOutputs.Builder result = new CcCompilationOutputs.Builder(); - Preconditions.checkNotNull(context); - AnalysisEnvironment env = ruleContext.getAnalysisEnvironment(); - - if (shouldProvideHeaderModules()) { - Label moduleMapLabel = Label.parseAbsoluteUnchecked(context.getCppModuleMap().getName()); - Collection<Artifact> modules = createModuleAction(result, context.getCppModuleMap()); - if (featureConfiguration.isEnabled(CppRuleClasses.HEADER_MODULE_CODEGEN)) { - for (Artifact module : modules) { - // TODO(djasper): Investigate whether we need to use a label separate from that of the - // module map. It is used for per-file-copts. - createModuleCodegenAction(result, moduleMapLabel, module); - } - } - } else if (context.getVerificationModuleMap() != null) { - Collection<Artifact> modules = createModuleAction(result, context.getVerificationModuleMap()); - for (Artifact module : modules) { - result.addHeaderTokenFile(module); - } - } - - for (CppSource source : sourceFiles) { - Artifact sourceArtifact = source.getSource(); - Label sourceLabel = source.getLabel(); - String outputName = - FileSystemUtils.removeExtension(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, - context.getCppModuleMap(), - /* addObject= */ true, - isCodeCoverageEnabled(), - // The source action does not generate dwo when it has bitcode - // output (since it isn't generating a native object with debug - // info). In that case the LtoBackendAction will generate the dwo. - 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"); - } - } - } - - compilationOutputs = result.build(); - return compilationOutputs; - } - - 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 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); - } - } - - 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(), - context)); - } - boolean generatePicAction = getGeneratePicActions(); - boolean generateNoPicAction = getGenerateNoPicActions(); - Preconditions.checkState(generatePicAction || generateNoPicAction); - if (fake) { - boolean usePic = !generateNoPicAction; - createFakeSourceAction( - sourceLabel, - outputName, - result, - env, - builder, - outputCategory, - addObject, - ccRelativeName, - sourceArtifact.getExecPath(), - usePic, - generateDotd); - } else { - boolean bitcodeOutput = - featureConfiguration.isEnabled(CppRuleClasses.THIN_LTO) - && CppFileTypes.LTO_SOURCE.matches(sourceArtifact.getFilename()); - - // Create PIC compile actions (same as non-PIC, but use -fPIC and - // generate .pic.o, .pic.d, .pic.gcno instead of .o, .d, .gcno.) - if (generatePicAction) { - String picOutputBase = CppHelper.getArtifactNameForCategory(ruleContext, - ccToolchain, ArtifactCategory.PIC_FILE, outputName); - CppCompileActionBuilder picBuilder = copyAsPicBuilder( - builder, picOutputBase, outputCategory, generateDotd); - String gcnoFileName = CppHelper.getArtifactNameForCategory(ruleContext, - ccToolchain, ArtifactCategory.COVERAGE_DATA_FILE, picOutputBase); - Artifact gcnoFile = enableCoverage - ? CppHelper.getCompileOutputArtifact(ruleContext, gcnoFileName, configuration) - : null; - Artifact dwoFile = generateDwo ? getDwoFile(picBuilder.getOutputFile()) : null; - Artifact ltoIndexingFile = - bitcodeOutput ? getLtoIndexingFile(picBuilder.getOutputFile()) : null; - - setupCompileBuildVariables( - picBuilder, - sourceLabel, - /* usePic= */ true, - ccRelativeName, - sourceArtifact.getExecPath(), - gcnoFile, - dwoFile, - ltoIndexingFile, - cppModuleMap); - - if (maySaveTemps) { - result.addTemps( - createTempsActions( - sourceArtifact, - sourceLabel, - outputName, - picBuilder, - /* usePic= */ true, - /* generateDotd= */ generateDotd, - ccRelativeName)); - } - - picBuilder.setGcnoFile(gcnoFile); - picBuilder.setDwoFile(dwoFile); - picBuilder.setLtoIndexingFile(ltoIndexingFile); - - semantics.finalizeCompileActionBuilder(ruleContext, picBuilder); - 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); - - if (maySaveTemps) { - result.addTemps( - createTempsActions( - sourceArtifact, - sourceLabel, - outputName, - builder, - /* usePic= */ false, - generateDotd, - ccRelativeName)); - } - - builder.setGcnoFile(gcnoFile); - builder.setDwoFile(noPicDwoFile); - builder.setLtoIndexingFile(ltoIndexingFile); - - semantics.finalizeCompileActionBuilder(ruleContext, builder); - CppCompileAction compileAction = builder.buildOrThrowRuleError(ruleContext); - env.registerAction(compileAction); - Artifact objectFile = compileAction.getOutputFile(); - directOutputs.add(objectFile); - if (addObject) { - result.addObjectFile(objectFile); - if (bitcodeOutput) { - result.addLtoBitcodeFile(objectFile, ltoIndexingFile); - } - } - if (noPicDwoFile != null) { - // Host targets don't produce .dwo files. - result.addDwoFile(noPicDwoFile); - } - if (cppConfiguration.isLipoContextCollector()) { - result.addLipoScannable(compileAction); - } - } - } - return directOutputs.build(); - } - - private Artifact createCompileActionTemplate( - AnalysisEnvironment env, - CppSource source, - CppCompileActionBuilder builder, - Iterable<ArtifactCategory> outputCategories, - 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; - } - - 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 the linked artifact resulting from a linking of the given type. Consults the feature - * configuration to obtain an action_config that provides the artifact. If the feature - * configuration provides no artifact, uses a default. - * - * <p>We cannot assume that the feature configuration contains an action_config for the link - * action, because the linux link action depends on hardcoded values in - * LinkCommandLine.getRawLinkArgv(), which are applied on the condition that an action_config is - * not present. - * TODO(b/30393154): Assert that the given link action has an action_config. - * - * @throws RuleErrorException - */ - private Artifact getLinkedArtifact(LinkTargetType linkTargetType) throws RuleErrorException { - Artifact result = null; - Artifact linuxDefault = - CppHelper.getLinuxLinkedArtifact( - ruleContext, configuration, linkTargetType, linkedArtifactNameSuffix); - - try { - String maybePicName = ruleContext.getLabel().getName() + linkedArtifactNameSuffix; - if (linkTargetType.picness() == Picness.PIC) { - maybePicName = CppHelper.getArtifactNameForCategory( - ruleContext, ccToolchain, ArtifactCategory.PIC_FILE, maybePicName); - } - String linkedName = CppHelper.getArtifactNameForCategory( - ruleContext, ccToolchain, linkTargetType.getLinkerOutput(), maybePicName); - PathFragment artifactFragment = PathFragment.create(ruleContext.getLabel().getName()) - .getParentDirectory().getRelative(linkedName); - - result = ruleContext.getPackageRelativeArtifact( - artifactFragment, configuration.getBinDirectory(ruleContext.getRule().getRepository())); - } catch (ExpansionException e) { - ruleContext.throwWithRuleError(e.getMessage()); - } - - // If the linked artifact is not the linux default, then a FailAction is generated for the - // linux default to satisfy the requirement of the implicit output. - // TODO(b/30132703): Remove the implicit outputs of cc_library. - if (!result.equals(linuxDefault)) { - ruleContext.registerAction( - new FailAction( - ruleContext.getActionOwner(), - ImmutableList.of(linuxDefault), - String.format( - "the given toolchain supports creation of %s instead of %s", - linuxDefault.getExecPathString(), result.getExecPathString()))); - } - - return result; - } - - /** - * Constructs the C++ linker actions. It generally generates two actions, one for a static library - * and one for a dynamic library. If PIC is required for shared libraries, but not for binaries, - * it additionally creates a third action to generate a PIC static library. - * - * <p>For dynamic libraries, this method can additionally create an interface shared library that - * can be used for linking, but doesn't contain any executable code. This increases the number of - * cache hits for link actions. Call {@link #setAllowInterfaceSharedObjects(boolean)} to enable - * this behavior. - * - * @throws RuleErrorException - */ - public CcLinkingOutputs createCcLinkActions( - CcCompilationOutputs ccOutputs, Iterable<Artifact> nonCodeLinkerInputs) - throws RuleErrorException, InterruptedException { - // For now only handle static links. Note that the dynamic library link below ignores linkType. - // TODO(bazel-team): Either support non-static links or move this check to setStaticLinkType(). - Preconditions.checkState( - linkType.staticness() == Staticness.STATIC, "can only handle static links"); - - CcLinkingOutputs.Builder result = new CcLinkingOutputs.Builder(); - if (cppConfiguration.isLipoContextCollector()) { - // Don't try to create LIPO link actions in collector mode, - // because it needs some data that's not available at this point. - return result.build(); - } - AnalysisEnvironment env = ruleContext.getAnalysisEnvironment(); - boolean usePicForBinaries = CppHelper.usePic(ruleContext, ccToolchain, /* forBinary= */ true); - boolean usePicForSharedLibs = - CppHelper.usePic(ruleContext, ccToolchain, /* forBinary= */ false); - - PathFragment labelName = PathFragment.create(ruleContext.getLabel().getName()); - String libraryIdentifier = - ruleContext - .getPackageDirectory() - .getRelative(labelName.replaceName("lib" + labelName.getBaseName())) - .getPathString(); - - if (createStaticLibraries) { - createStaticLibraries( - result, - env, - usePicForBinaries, - usePicForSharedLibs, - libraryIdentifier, - ccOutputs, - nonCodeLinkerInputs); - } - - if (createDynamicLibrary) { - createDynamicLibrary(result, env, usePicForSharedLibs, libraryIdentifier, ccOutputs); - } - - return result.build(); - } - - private void createStaticLibraries( - CcLinkingOutputs.Builder result, - AnalysisEnvironment env, - boolean usePicForBinaries, - boolean usePicForSharedLibs, - String libraryIdentifier, - CcCompilationOutputs ccOutputs, - Iterable<Artifact> nonCodeLinkerInputs) - throws RuleErrorException, InterruptedException { - // Create static library (.a). The linkType only reflects whether the library is alwayslink or - // not. The PIC-ness is determined by whether we need to use PIC or not. There are three cases - // for (usePicForSharedLibs usePicForBinaries): - // - // (1) (false false) -> no pic code - // (2) (true false) -> shared libraries as pic, but not binaries - // (3) (true true) -> both shared libraries and binaries as pic - // - // In case (3), we always need PIC, so only create one static library containing the PIC - // object - // files. The name therefore does not match the content. - // - // Presumably, it is done this way because the .a file is an implicit output of every - // cc_library - // rule, so we can't use ".pic.a" that in the always-PIC case. - - // If the crosstool is configured to select an output artifact, we use that selection. - // Otherwise, we use linux defaults. - Artifact linkedArtifact = getLinkedArtifact(linkType); - - CppLinkAction maybePicAction = - newLinkActionBuilder(linkedArtifact) - .addObjectFiles(ccOutputs.getObjectFiles(usePicForBinaries)) - .addNonCodeInputs(nonCodeLinkerInputs) - .addLtoBitcodeFiles(ccOutputs.getLtoBitcodeFiles()) - .setLinkType(linkType) - .setLinkStaticness(LinkStaticness.FULLY_STATIC) - .addActionInputs(linkActionInputs) - .setLibraryIdentifier(libraryIdentifier) - .addVariablesExtensions(variablesExtensions) - .build(); - env.registerAction(maybePicAction); - if (usePicForBinaries) { - result.addPicStaticLibrary(maybePicAction.getOutputLibrary()); - } else { - result.addStaticLibrary(maybePicAction.getOutputLibrary()); - // Create a second static library (.pic.a). Only in case (2) do we need both PIC and non-PIC - // static libraries. In that case, the first static library contains the non-PIC code, and - // this - // one contains the PIC code, so the names match the content. - if (usePicForSharedLibs) { - LinkTargetType picLinkType = - (linkType == LinkTargetType.ALWAYS_LINK_STATIC_LIBRARY) - ? LinkTargetType.ALWAYS_LINK_PIC_STATIC_LIBRARY - : LinkTargetType.PIC_STATIC_LIBRARY; - - // If the crosstool is configured to select an output artifact, we use that selection. - // Otherwise, we use linux defaults. - Artifact picArtifact = getLinkedArtifact(picLinkType); - CppLinkAction picAction = - newLinkActionBuilder(picArtifact) - .addObjectFiles(ccOutputs.getObjectFiles(/* usePic= */ true)) - .addLtoBitcodeFiles(ccOutputs.getLtoBitcodeFiles()) - .setLinkType(picLinkType) - .setLinkStaticness(LinkStaticness.FULLY_STATIC) - .addActionInputs(linkActionInputs) - .setLibraryIdentifier(libraryIdentifier) - .addVariablesExtensions(variablesExtensions) - .build(); - env.registerAction(picAction); - result.addPicStaticLibrary(picAction.getOutputLibrary()); - } - } - } - - private void createDynamicLibrary( - CcLinkingOutputs.Builder result, - AnalysisEnvironment env, - boolean usePicForSharedLibs, - String libraryIdentifier, - CcCompilationOutputs ccOutputs) - throws RuleErrorException, InterruptedException { - // Create dynamic library. - Artifact soImpl; - String mainLibraryIdentifier; - if (soImplArtifact == null) { - // If the crosstool is configured to select an output artifact, we use that selection. - // Otherwise, we use linux defaults. - soImpl = getLinkedArtifact(LinkTargetType.DYNAMIC_LIBRARY); - mainLibraryIdentifier = libraryIdentifier; - } else { - // This branch is only used for vestigial Google-internal rules where the name of the output - // file is explicitly specified in the BUILD file and as such, is platform-dependent. Thus, - // we just hardcode some reasonable logic to compute the library identifier and hope that this - // will eventually go away. - soImpl = soImplArtifact; - mainLibraryIdentifier = FileSystemUtils.removeExtension( - soImpl.getRootRelativePath().getPathString()); - - } - - List<String> sonameLinkopts = ImmutableList.of(); - Artifact soInterface = null; - if (CppHelper.useInterfaceSharedObjects(cppConfiguration, ccToolchain) - && allowInterfaceSharedObjects) { - soInterface = - CppHelper.getLinuxLinkedArtifact( - ruleContext, - configuration, - LinkTargetType.INTERFACE_DYNAMIC_LIBRARY, - linkedArtifactNameSuffix); - // TODO(b/28946988): Remove this hard-coded flag. - if (!featureConfiguration.isEnabled(CppRuleClasses.TARGETS_WINDOWS)) { - sonameLinkopts = - ImmutableList.of( - "-Wl,-soname=" - + SolibSymlinkAction.getDynamicLibrarySoname( - soImpl.getRootRelativePath(), /* preserveName= */ false)); - } - } - - CppLinkActionBuilder dynamicLinkActionBuilder = - newLinkActionBuilder(soImpl) - .setInterfaceOutput(soInterface) - .addObjectFiles(ccOutputs.getObjectFiles(usePicForSharedLibs)) - .addNonCodeInputs(ccOutputs.getHeaderTokenFiles()) - .addLtoBitcodeFiles(ccOutputs.getLtoBitcodeFiles()) - .setLinkType(LinkTargetType.DYNAMIC_LIBRARY) - .setLinkStaticness(LinkStaticness.DYNAMIC) - .addActionInputs(linkActionInputs) - .setLibraryIdentifier(mainLibraryIdentifier) - .addLinkopts(linkopts) - .addLinkopts(sonameLinkopts) - .setRuntimeInputs( - ArtifactCategory.DYNAMIC_LIBRARY, - ccToolchain.getDynamicRuntimeLinkMiddleman(), - ccToolchain.getDynamicRuntimeLinkInputs()) - .addVariablesExtensions(variablesExtensions); - - if (featureConfiguration.isEnabled(CppRuleClasses.TARGETS_WINDOWS)) { - // On Windows, we cannot build a shared library with symbols unresolved, so here we - // dynamically - // link to all it's dependencies. - CcLinkParams.Builder ccLinkParamsBuilder = - CcLinkParams.builder(/* linkingStatically= */ false, /* linkShared= */ true); - ccLinkParamsBuilder.addCcLibrary(ruleContext); - dynamicLinkActionBuilder.addLinkParams(ccLinkParamsBuilder.build(), ruleContext); - - // If windows_export_all_symbols feature is enabled, bazel parses object files to generate - // DEF file and use it to export symbols. The generated DEF file won't be used if a custom - // DEF file is specified by win_def_file attribute. - if (CppHelper.shouldUseGeneratedDefFile(ruleContext, featureConfiguration)) { - Artifact generatedDefFile = - CppHelper.createDefFileActions( - ruleContext, - ruleContext.getPrerequisiteArtifact("$def_parser", Mode.HOST), - ccOutputs.getObjectFiles(false), - SolibSymlinkAction.getDynamicLibrarySoname(soImpl.getRootRelativePath(), true)); - dynamicLinkActionBuilder.setDefFile(generatedDefFile); - } - - // If user specifies a custom DEF file, then we use this one instead of the generated one. - Artifact customDefFile = null; - if (ruleContext.isAttrDefined("win_def_file", LABEL)) { - customDefFile = ruleContext.getPrerequisiteArtifact("win_def_file", Mode.TARGET); - } - if (customDefFile != null) { - dynamicLinkActionBuilder.setDefFile(customDefFile); - } - } - - if (!ccOutputs.getLtoBitcodeFiles().isEmpty() - && featureConfiguration.isEnabled(CppRuleClasses.THIN_LTO)) { - dynamicLinkActionBuilder.setLtoIndexing(true); - dynamicLinkActionBuilder.setUsePicForLtoBackendActions(usePicForSharedLibs); - CppLinkAction indexAction = dynamicLinkActionBuilder.build(); - if (indexAction != null) { - env.registerAction(indexAction); - } - - dynamicLinkActionBuilder.setLtoIndexing(false); - } - - CppLinkAction dynamicLinkAction = dynamicLinkActionBuilder.build(); - env.registerAction(dynamicLinkAction); - - LibraryToLink dynamicLibrary = dynamicLinkAction.getOutputLibrary(); - LibraryToLink interfaceLibrary = dynamicLinkAction.getInterfaceOutputLibrary(); - - // If shared library has neverlink=1, then leave it untouched. Otherwise, - // create a mangled symlink for it and from now on reference it through - // mangled name only. - // - // When COPY_DYNAMIC_LIBRARIES_TO_BINARY is enabled, we don't need to create the special - // solibDir, instead we use the original interface library and dynamic library. - if (neverLink - || featureConfiguration.isEnabled(CppRuleClasses.COPY_DYNAMIC_LIBRARIES_TO_BINARY)) { - result.addDynamicLibrary(interfaceLibrary == null ? dynamicLibrary : interfaceLibrary); - result.addExecutionDynamicLibrary(dynamicLibrary); - } else { - Artifact implLibraryLinkArtifact = - SolibSymlinkAction.getDynamicLibrarySymlink( - ruleContext, - ccToolchain.getSolibDirectory(), - dynamicLibrary.getArtifact(), - /* preserveName= */ false, - /* prefixConsumer= */ false, - ruleContext.getConfiguration()); - LibraryToLink implLibraryLink = - LinkerInputs.solibLibraryToLink( - implLibraryLinkArtifact, dynamicLibrary.getArtifact(), libraryIdentifier); - result.addExecutionDynamicLibrary(implLibraryLink); - - LibraryToLink libraryLink; - if (interfaceLibrary == null) { - libraryLink = implLibraryLink; - } else { - Artifact libraryLinkArtifact = - SolibSymlinkAction.getDynamicLibrarySymlink( - ruleContext, - ccToolchain.getSolibDirectory(), - interfaceLibrary.getArtifact(), - /* preserveName= */ false, - /* prefixConsumer= */ false, - ruleContext.getConfiguration()); - libraryLink = - LinkerInputs.solibLibraryToLink( - libraryLinkArtifact, interfaceLibrary.getArtifact(), libraryIdentifier); - } - result.addDynamicLibrary(libraryLink); - } - } - - - private CppLinkActionBuilder newLinkActionBuilder(Artifact outputArtifact) { - return new CppLinkActionBuilder( - ruleContext, outputArtifact, ccToolchain, fdoSupport, featureConfiguration, semantics) - .setCrosstoolInputs(ccToolchain.getLink()) - .addNonCodeInputs(context.getTransitiveCompilationPrerequisites()); - } - - /** - * Creates a basic cpp compile action builder for source file. Configures options, crosstool - * inputs, output and dotd file names, compilation context and copts. - */ - private CppCompileActionBuilder createCompileActionBuilder(Artifact source) { - CppCompileActionBuilder builder = - new CppCompileActionBuilder(ruleContext, ccToolchain, configuration); - builder.setSourceFile(source); - builder.setContext(context); - builder.addEnvironment(ccToolchain.getEnvironment()); - builder.setCoptsFilter(coptsFilter); - return builder; - } - - /** - * Creates cpp PIC compile action builder from the given builder by adding necessary copt and - * changing output and dotd file names. - */ - private CppCompileActionBuilder copyAsPicBuilder( - CppCompileActionBuilder builder, - String outputName, - ArtifactCategory outputCategory, - boolean generateDotd) - throws RuleErrorException { - CppCompileActionBuilder picBuilder = new CppCompileActionBuilder(builder); - picBuilder - .setPicMode(true) - .setOutputs(ruleContext, outputCategory, outputName, generateDotd); - - return picBuilder; - } - - /** Create the actions for "--save_temps". */ - private ImmutableList<Artifact> createTempsActions( - Artifact source, - Label sourceLabel, - String outputName, - CppCompileActionBuilder builder, - boolean usePic, - boolean generateDotd, - PathFragment ccRelativeName) - throws RuleErrorException { - if (!cppConfiguration.getSaveTemps()) { - return ImmutableList.of(); - } - - String path = source.getFilename(); - boolean isCFile = CppFileTypes.C_SOURCE.matches(path); - boolean isCppFile = CppFileTypes.CPP_SOURCE.matches(path); - - if (!isCFile && !isCppFile) { - return ImmutableList.of(); - } - - ArtifactCategory category = isCFile - ? ArtifactCategory.PREPROCESSED_C_SOURCE : ArtifactCategory.PREPROCESSED_CPP_SOURCE; - - String outputArtifactNameBase = getOutputNameBaseWith(outputName, usePic); - - CppCompileActionBuilder dBuilder = new CppCompileActionBuilder(builder); - dBuilder.setOutputs(ruleContext, category, outputArtifactNameBase, generateDotd); - setupCompileBuildVariables( - dBuilder, - sourceLabel, - usePic, - ccRelativeName, - source.getExecPath(), - /* gcnoFile= */ null, - /* dwoFile= */ null, - /* ltoIndexingFile= */ null, - builder.getContext().getCppModuleMap()); - 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()); - } - - /** - * 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; - } -} diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/proto/CcProtoAspect.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/proto/CcProtoAspect.java index 2f4e7807e8..bce029f360 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/proto/CcProtoAspect.java +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/proto/CcProtoAspect.java @@ -273,7 +273,7 @@ public abstract class CcProtoAspect extends NativeAspectClass implements Configu // TODO(dougk): Configure output artifact with action_config // once proto compile action is configurable from the crosstool. if (!ccToolchain(ruleContext).supportsDynamicLinker()) { - helper.setCreateDynamicLibrary(false); + helper.setShouldCreateDynamicLibrary(false); } return helper; } |