// Copyright 2014 The Bazel Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.google.devtools.build.lib.rules.cpp; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.base.Supplier; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.actions.FailAction; import com.google.devtools.build.lib.analysis.AnalysisEnvironment; import com.google.devtools.build.lib.analysis.RuleContext; import com.google.devtools.build.lib.analysis.TransitiveInfoCollection; import com.google.devtools.build.lib.analysis.config.BuildConfiguration; import com.google.devtools.build.lib.analysis.config.PerLabelOptions; import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget.Mode; import com.google.devtools.build.lib.analysis.test.InstrumentedFilesCollector; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.packages.BuildType; import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException; import com.google.devtools.build.lib.rules.cpp.CcCompilationOutputs.Builder; import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.ExpansionException; import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.FeatureConfiguration; import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.Variables.StringSequenceBuilder; import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.Variables.VariablesExtension; import com.google.devtools.build.lib.rules.cpp.CppCompileAction.DotdFile; import com.google.devtools.build.lib.rules.cpp.Link.LinkStaticness; import com.google.devtools.build.lib.rules.cpp.Link.LinkTargetType; import com.google.devtools.build.lib.rules.cpp.Link.Picness; import com.google.devtools.build.lib.rules.cpp.Link.Staticness; import com.google.devtools.build.lib.rules.cpp.LinkerInputs.LibraryToLink; import com.google.devtools.build.lib.util.FileType; import com.google.devtools.build.lib.util.Preconditions; import com.google.devtools.build.lib.vfs.FileSystemUtils; import com.google.devtools.build.lib.vfs.PathFragment; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; /** * Representation of a C/C++ compilation. Its purpose is to share the code that creates compilation * actions between all classes that need to do so. It follows the builder pattern - load up the * necessary settings and then call {@link #createCcCompileActions}. * *

This class is not thread-safe, and it should only be used once for each set of source files, * i.e. calling {@link #createCcCompileActions} will throw an Exception if called twice. */ public final class CppModel { /** Name of the build variable for the path to the source file being compiled. */ public static final String SOURCE_FILE_VARIABLE_NAME = "source_file"; /** Name of the build variable for the path to the input file being processed. */ public static final String INPUT_FILE_VARIABLE_NAME = "input_file"; /** Name of the build variable for the path to the compilation output file. */ public static final String OUTPUT_FILE_VARIABLE_NAME = "output_file"; /** * Name of the build variable for the path to the compilation output file in case of assembly * source. */ public static final String OUTPUT_ASSEMBLY_FILE_VARIABLE_NAME = "output_assembly_file"; /** * Name of the build variable for the path to the compilation output file in case of preprocessed * source. */ public static final String OUTPUT_PREPROCESS_FILE_VARIABLE_NAME = "output_preprocess_file"; /** Name of the build variable for the path to the output file when output is an object file. */ public static final String OUTPUT_OBJECT_FILE_VARIABLE_NAME = "output_object_file"; /** Name of the build variable for the module file name. */ public static final String MODULE_NAME_VARIABLE_NAME = "module_name"; /** Name of the build variable for the module map file name. */ public static final String MODULE_MAP_FILE_VARIABLE_NAME = "module_map_file"; /** Name of the build variable for the dependent module map file name. */ public static final String DEPENDENT_MODULE_MAP_FILES_VARIABLE_NAME = "dependent_module_map_files"; /** Name of the build variable for the collection of module files. */ public static final String MODULE_FILES_VARIABLE_NAME = "module_files"; /** * Name of the build variable for includes that compiler needs to include into sources to be * compiled. */ public static final String INCLUDES_VARIABLE_NAME = "includes"; /** * Name of the build variable for the collection of include paths. * * @see CppCompilationContext#getIncludeDirs(). */ public static final String INCLUDE_PATHS_VARIABLE_NAME = "include_paths"; /** * Name of the build variable for the collection of quote include paths. * * @see CppCompilationContext#getIncludeDirs(). */ public static final String QUOTE_INCLUDE_PATHS_VARIABLE_NAME = "quote_include_paths"; /** * Name of the build variable for the collection of system include paths. * * @see CppCompilationContext#getIncludeDirs(). */ public static final String SYSTEM_INCLUDE_PATHS_VARIABLE_NAME = "system_include_paths"; /** Name of the build variable for the dependency file path */ public static final String DEPENDENCY_FILE_VARIABLE_NAME = "dependency_file"; /** Name of the build variable for the collection of macros defined for preprocessor. */ public static final String PREPROCESSOR_DEFINES_VARIABLE_NAME = "preprocessor_defines"; /** Name of the build variable present when the output is compiled as position independent. */ public static final String PIC_VARIABLE_NAME = "pic"; /** Name of the build variable for the gcov coverage file path. */ public static final String GCOV_GCNO_FILE_VARIABLE_NAME = "gcov_gcno_file"; /** Name of the build variable for the per object debug info file. */ public static final String PER_OBJECT_DEBUG_INFO_FILE_VARIABLE_NAME = "per_object_debug_info_file"; /** Name of the build variable for the LTO indexing bitcode file. */ public static final String LTO_INDEXING_BITCODE_FILE_VARIABLE_NAME = "lto_indexing_bitcode_file"; /** Name of the build variable for stripopts for the strip action. */ public static final String STRIPOPTS_VARIABLE_NAME = "stripopts"; /** * Build variable for all flags coming from legacy crosstool fields, such as compiler_flag, * optional_compiler_flag, cxx_flag, optional_cxx_flag. */ public static final String LEGACY_COMPILE_FLAGS_VARIABLE_NAME = "legacy_compile_flags"; /** * Build variable for all flags coming from copt rule attribute, and from --copt, --cxxopt, or * --conlyopt options. */ public static final String USER_COMPILE_FLAGS_VARIABLE_NAME = "user_compile_flags"; /** Build variable for flags coming from unfiltered_cxx_flag CROSSTOOL fields. */ public static final String UNFILTERED_COMPILE_FLAGS_VARIABLE_NAME = "unfiltered_compile_flags"; /** Name of the build variable for the sysroot path variable name. */ public static final String SYSROOT_VARIABLE_NAME = "sysroot"; private final CppSemantics semantics; private final RuleContext ruleContext; private final BuildConfiguration configuration; private final CppConfiguration cppConfiguration; // compile model private CppCompilationContext context; private final Set sourceFiles = new LinkedHashSet<>(); private final List mandatoryInputs = new ArrayList<>(); private final ImmutableList copts; private final Predicate coptsFilter; private boolean fake; private boolean maySaveTemps; private boolean onlySingleOutput; private CcCompilationOutputs compilationOutputs; // link model private final List linkopts = new ArrayList<>(); private LinkTargetType linkType = LinkTargetType.STATIC_LIBRARY; private boolean neverLink; private final List linkActionInputs = new ArrayList<>(); private boolean allowInterfaceSharedObjects; private boolean createDynamicLibrary = true; private Artifact soImplArtifact; private FeatureConfiguration featureConfiguration; private List variablesExtensions = new ArrayList<>(); private final CcToolchainProvider ccToolchain; private final FdoSupportProvider fdoSupport; private String linkedArtifactNameSuffix = ""; private final ImmutableSet features; public CppModel( RuleContext ruleContext, CppSemantics semantics, CcToolchainProvider ccToolchain, FdoSupportProvider fdoSupport, ImmutableList copts) { this( ruleContext, semantics, ccToolchain, fdoSupport, ruleContext.getConfiguration(), copts, Predicates.alwaysTrue()); } public CppModel( RuleContext ruleContext, CppSemantics semantics, CcToolchainProvider ccToolchain, FdoSupportProvider fdoSupport, ImmutableList copts, Predicate coptsFilter) { this( ruleContext, semantics, ccToolchain, fdoSupport, ruleContext.getConfiguration(), copts, coptsFilter); } public CppModel( RuleContext ruleContext, CppSemantics semantics, CcToolchainProvider ccToolchain, FdoSupportProvider fdoSupport, BuildConfiguration configuration, ImmutableList copts, Predicate coptsFilter) { this.ruleContext = Preconditions.checkNotNull(ruleContext); this.semantics = semantics; this.ccToolchain = Preconditions.checkNotNull(ccToolchain); this.fdoSupport = Preconditions.checkNotNull(fdoSupport); this.configuration = configuration; this.copts = copts; this.coptsFilter = Preconditions.checkNotNull(coptsFilter); cppConfiguration = ruleContext.getFragment(CppConfiguration.class); features = ruleContext.getFeatures(); } private Artifact getDwoFile(Artifact outputFile) { return ruleContext.getRelatedArtifact(outputFile.getRootRelativePath(), ".dwo"); } private Artifact getLtoIndexingFile(Artifact outputFile) { String ext = Iterables.getOnlyElement(CppFileTypes.LTO_INDEXING_OBJECT_FILE.getExtensions()); return ruleContext.getRelatedArtifact(outputFile.getRootRelativePath(), ext); } /** * If the cpp compilation is a fake, then it creates only a single compile action without PIC. * Defaults to false. */ public CppModel setFake(boolean fake) { this.fake = fake; return this; } /** * If set, the CppModel only creates a single .o output that can be linked into a dynamic library, * i.e., it never generates both PIC and non-PIC outputs. Otherwise it creates outputs that can be * linked into both static binaries and dynamic libraries (if both require PIC or both require * non-PIC, then it still only creates a single output). Defaults to false. */ public CppModel setOnlySingleOutput(boolean onlySingleOutput) { this.onlySingleOutput = onlySingleOutput; return this; } /** * Whether to create actions for temps. This defaults to false. */ public CppModel setSaveTemps(boolean maySaveTemps) { this.maySaveTemps = maySaveTemps; return this; } /** * Sets the compilation context, i.e. include directories and allowed header files inclusions. */ public CppModel setContext(CppCompilationContext context) { this.context = context; return this; } /** * Adds a single source file to be compiled. The given build variables will be added to those used * to compile this source file. Note that this should only be called for primary compilation * units, including module files or headers to be parsed or preprocessed. */ public CppModel addCompilationUnitSources( Iterable sourceFiles, Label sourceLabel, Map buildVariables, CppSource.Type type) { for (Artifact sourceFile : sourceFiles) { this.sourceFiles.add(CppSource.create(sourceFile, sourceLabel, buildVariables, type)); } return this; } /** * Adds all the source files. Note that this should only be called for primary compilation units, * including module files or headers to be parsed or preprocessed. */ public CppModel addCompilationUnitSources(Set sources) { this.sourceFiles.addAll(sources); return this; } /** Adds mandatory inputs. */ public CppModel addMandatoryInputs(Collection artifacts) { this.mandatoryInputs.addAll(artifacts); return this; } /** * Adds the given linkopts to the optional dynamic library link command. */ public CppModel addLinkopts(Collection linkopts) { this.linkopts.addAll(linkopts); return this; } /** * Adds the given variablesExensions for templating the crosstool. * *

In general, we prefer the build variables (especially those that derive strictly from * the configuration) be learned by inspecting the CcToolchain, as passed to the rule in the * CcToolchainProvider. However, for build variables that must be injected into the rule * implementation (ex. build variables learned from the BUILD file), should be added using the * VariablesExtension abstraction. This allows the injection to construct non-trivial build * variables (lists, ect.). */ public CppModel addVariablesExtension(Collection variablesExtensions) { this.variablesExtensions.addAll(variablesExtensions); return this; } /** * Sets the link type used for the link actions. Note that only static links are supported at this * time. */ public CppModel setLinkTargetType(LinkTargetType linkType) { this.linkType = linkType; return this; } public CppModel setNeverLink(boolean neverLink) { this.neverLink = neverLink; return this; } /** * Adds an artifact to the inputs of any link actions created by this CppModel. */ public CppModel addLinkActionInputs(Collection inputs) { this.linkActionInputs.addAll(inputs); return this; } /** * Whether to allow interface dynamic libraries. Note that setting this to true only has an effect * if the configuration allows it. Defaults to false. */ public CppModel setAllowInterfaceSharedObjects(boolean allowInterfaceSharedObjects) { // TODO(bazel-team): Set the default to true, and require explicit action to disable it. this.allowInterfaceSharedObjects = allowInterfaceSharedObjects; return this; } public CppModel setCreateDynamicLibrary(boolean createDynamicLibrary) { this.createDynamicLibrary = createDynamicLibrary; return this; } public CppModel setDynamicLibrary(Artifact soImplFilename) { this.soImplArtifact = soImplFilename; return this; } /** Sets the feature configuration to be used for C/C++ actions. */ public CppModel setFeatureConfiguration(FeatureConfiguration featureConfiguration) { this.featureConfiguration = featureConfiguration; return this; } /* * Adds a suffix for paths of linked artifacts. Normally their paths are derived solely from rule * labels. In the case of multiple callers (e.g., aspects) acting on a single rule, they may * generate the same linked artifact and therefore lead to artifact conflicts. This method * provides a way to avoid this artifact conflict by allowing different callers acting on the same * rule to provide a suffix that will be used to scope their own linked artifacts. */ public CppModel setLinkedArtifactNameSuffix(String suffix) { this.linkedArtifactNameSuffix = suffix; return this; } /** * @returns whether we want to provide header modules for the current target. */ private boolean shouldProvideHeaderModules() { return featureConfiguration.isEnabled(CppRuleClasses.HEADER_MODULES) && !cppConfiguration.isLipoContextCollector(); } /** * @return the non-pic header module artifact for the current target. */ public Artifact getHeaderModule(Artifact moduleMapArtifact) { PathFragment objectDir = CppHelper.getObjDirectory(ruleContext.getLabel()); PathFragment outputName = objectDir.getRelative( semantics.getEffectiveSourcePath(moduleMapArtifact)); return ruleContext.getRelatedArtifact(outputName, ".pcm"); } /** * @return the pic header module artifact for the current target. */ public Artifact getPicHeaderModule(Artifact moduleMapArtifact) { PathFragment objectDir = CppHelper.getObjDirectory(ruleContext.getLabel()); PathFragment outputName = objectDir.getRelative( semantics.getEffectiveSourcePath(moduleMapArtifact)); return ruleContext.getRelatedArtifact(outputName, ".pic.pcm"); } /** * @return whether this target needs to generate pic actions. */ private boolean getGeneratePicActions() { return CppHelper.usePic(ruleContext, false); } /** * @return whether this target needs to generate non-pic actions. */ private boolean getGenerateNoPicActions() { return // If we always need pic for everything, then don't bother to create a no-pic action. (!CppHelper.usePic(ruleContext, true) || !CppHelper.usePic(ruleContext, false)) // onlySingleOutput guarantees that the code is only ever linked into a dynamic library - so // we don't need a no-pic action even if linking into a binary would require it. && !((onlySingleOutput && getGeneratePicActions())); } /** * @return whether this target needs to generate a pic header module. */ public boolean getGeneratesPicHeaderModule() { return shouldProvideHeaderModules() && !fake && getGeneratePicActions(); } /** * @return whether this target needs to generate a non-pic header module. */ public boolean getGeneratesNoPicHeaderModule() { return shouldProvideHeaderModules() && !fake && getGenerateNoPicActions(); } /** * Returns a {@code CppCompileActionBuilder} with the common fields for a C++ compile action being * initialized. */ private CppCompileActionBuilder initializeCompileAction(Artifact sourceArtifact) { 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 getSafePathStrings(Collection paths) { ImmutableSet.Builder 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. * *

Dear friends of the lambda, this method exists to limit the scope of captured variables * only to arguments (to prevent accidental capture of enclosing instance which could regress * memory). */ public static Supplier> getUnfilteredCompileFlagsSupplier( CcToolchainProvider ccToolchain, ImmutableSet features) { return () -> ccToolchain.getUnfilteredCompilerOptions(features); } /** * Supplier that computes legacy_compile_flags lazily at the execution phase. * *

Dear friends of the lambda, this method exists to limit the scope of captured variables * only to arguments (to prevent accidental capture of enclosing instance which could regress * memory). */ public static Supplier> getLegacyCompileFlagsSupplier( CppConfiguration cppConfiguration, String sourceFilename, ImmutableSet features) { return () -> cppConfiguration.collectLegacyCompileFlags(sourceFilename, features); } private void setupCompileBuildVariables( CppCompileActionBuilder builder, Label sourceLabel, boolean usePic, PathFragment ccRelativeName, PathFragment autoFdoImportPath, Artifact gcnoFile, Artifact dwoFile, Artifact ltoIndexingFile, CppModuleMap cppModuleMap, Map sourceSpecificBuildVariables) { CcToolchainFeatures.Variables.Builder buildVariables = new CcToolchainFeatures.Variables.Builder(ccToolchain.getBuildVariables()); CppCompilationContext builderContext = builder.getContext(); Artifact sourceFile = builder.getSourceFile(); Artifact outputFile = builder.getOutputFile(); String realOutputFilePath; buildVariables.addStringVariable(SOURCE_FILE_VARIABLE_NAME, sourceFile.getExecPathString()); buildVariables.addStringVariable(OUTPUT_FILE_VARIABLE_NAME, outputFile.getExecPathString()); buildVariables.addStringSequenceVariable( USER_COMPILE_FLAGS_VARIABLE_NAME, ImmutableList.builder() .addAll(copts) .addAll(collectPerFileCopts(sourceFile, sourceLabel)) .build()); String sourceFilename = sourceFile.getExecPathString(); buildVariables.addLazyStringSequenceVariable( LEGACY_COMPILE_FLAGS_VARIABLE_NAME, getLegacyCompileFlagsSupplier(cppConfiguration, sourceFilename, features)); if (!CppFileTypes.OBJC_SOURCE.matches(sourceFilename) && !CppFileTypes.OBJCPP_SOURCE.matches(sourceFilename)) { buildVariables.addLazyStringSequenceVariable( UNFILTERED_COMPILE_FLAGS_VARIABLE_NAME, getUnfilteredCompileFlagsSupplier(ccToolchain, features)); } if (builder.getTempOutputFile() != null) { realOutputFilePath = builder.getTempOutputFile().getPathString(); } else { realOutputFilePath = builder.getOutputFile().getExecPathString(); } if (FileType.contains(outputFile, CppFileTypes.ASSEMBLER, CppFileTypes.PIC_ASSEMBLER)) { buildVariables.addStringVariable(OUTPUT_ASSEMBLY_FILE_VARIABLE_NAME, realOutputFilePath); } else if (FileType.contains(outputFile, CppFileTypes.PREPROCESSED_C, CppFileTypes.PREPROCESSED_CPP, CppFileTypes.PIC_PREPROCESSED_C, CppFileTypes.PIC_PREPROCESSED_CPP)) { buildVariables.addStringVariable(OUTPUT_PREPROCESS_FILE_VARIABLE_NAME, realOutputFilePath); } else { buildVariables.addStringVariable(OUTPUT_OBJECT_FILE_VARIABLE_NAME, realOutputFilePath); } DotdFile dotdFile = isGenerateDotdFile(sourceFile) ? Preconditions.checkNotNull(builder.getDotdFile()) : null; // Set dependency_file to enable .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 defines; if (fdoBuildStamp != null) { // Stamp FDO builds with FDO subtype string defines = ImmutableList.builder() .addAll(builderContext.getDefines()) .add(CppConfiguration.FDO_STAMP_MACRO + "=\"" + CppHelper.getFdoBuildStamp( ruleContext, fdoSupport.getFdoSupport()) + "\"") .build(); } else { defines = builderContext.getDefines(); } buildVariables.addStringSequenceVariable(PREPROCESSOR_DEFINES_VARIABLE_NAME, defines); } if (usePic) { if (!featureConfiguration.isEnabled(CppRuleClasses.PIC)) { ruleContext.ruleError("PIC compilation is requested but the toolchain does not support it"); } buildVariables.addStringVariable(PIC_VARIABLE_NAME, ""); } if (ccRelativeName != null) { fdoSupport.getFdoSupport().configureCompilation( builder, buildVariables, ruleContext, ccRelativeName, autoFdoImportPath, usePic, featureConfiguration, fdoSupport); } if (gcnoFile != null) { buildVariables.addStringVariable(GCOV_GCNO_FILE_VARIABLE_NAME, gcnoFile.getExecPathString()); } if (dwoFile != null) { buildVariables.addStringVariable( PER_OBJECT_DEBUG_INFO_FILE_VARIABLE_NAME, dwoFile.getExecPathString()); } if (ltoIndexingFile != null) { buildVariables.addStringVariable( LTO_INDEXING_BITCODE_FILE_VARIABLE_NAME, ltoIndexingFile.getExecPathString()); } buildVariables.addAllStringVariables(sourceSpecificBuildVariables); for (VariablesExtension extension : variablesExtensions) { extension.addVariables(buildVariables); } CcToolchainFeatures.Variables variables = buildVariables.build(); builder.setVariables(variables); } private ImmutableList 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 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 modules = createModuleAction(result, context.getVerificationModuleMap()); for (Artifact module : modules) { result.addHeaderTokenFile(module); } } for (CppSource source : sourceFiles) { Artifact sourceArtifact = source.getSource(); Label sourceLabel = source.getLabel(); String outputName = FileSystemUtils.removeExtension( semantics.getEffectiveSourcePath(sourceArtifact)).getPathString(); CppCompileActionBuilder builder = initializeCompileAction(sourceArtifact); builder.setSemantics(semantics); if (!sourceArtifact.isTreeArtifact()) { switch (source.getType()) { case HEADER: createHeaderAction( sourceLabel, outputName, result, env, builder, isGenerateDotdFile(sourceArtifact)); break; case CLIF_INPUT_PROTO: createClifMatchAction(sourceLabel, outputName, result, env, builder); break; default: boolean bitcodeOutput = featureConfiguration.isEnabled(CppRuleClasses.THIN_LTO) && CppFileTypes.LTO_SOURCE.matches(sourceArtifact.getFilename()); createSourceAction( sourceLabel, outputName, result, env, sourceArtifact, builder, ArtifactCategory.OBJECT_FILE, context.getCppModuleMap(), /* addObject= */ true, isCodeCoverageEnabled(), // The source action does not generate dwo when it has bitcode // output (since it isn't generating a native object with debug // info). In that case the LtoBackendAction will generate the dwo. /* generateDwo= */ cppConfiguration.useFission() && !bitcodeOutput, isGenerateDotdFile(sourceArtifact), source.getBuildVariables()); break; } } else { switch (source.getType()) { case HEADER: Artifact headerTokenFile = createCompileActionTemplate( env, source, builder, ImmutableList.of( ArtifactCategory.GENERATED_HEADER, ArtifactCategory.PROCESSED_HEADER)); result.addHeaderTokenFile(headerTokenFile); break; case SOURCE: Artifact objectFile = createCompileActionTemplate( env, source, builder, ImmutableList.of(ArtifactCategory.OBJECT_FILE)); result.addObjectFile(objectFile); break; default: throw new IllegalStateException( "Encountered invalid source types when creating CppCompileActionTemplates"); } } } compilationOutputs = result.build(); return compilationOutputs; } private void createHeaderAction( Label sourceLabel, String outputName, Builder result, AnalysisEnvironment env, CppCompileActionBuilder builder, boolean generateDotd) throws RuleErrorException { String outputNameBase = CppHelper.getArtifactNameForCategory( ruleContext, ccToolchain, ArtifactCategory.GENERATED_HEADER, outputName); builder .setOutputs(ruleContext, ArtifactCategory.PROCESSED_HEADER, outputNameBase, generateDotd) // If we generate pic actions, we prefer the header actions to use the pic artifacts. .setPicMode(getGeneratePicActions()); setupCompileBuildVariables( builder, sourceLabel, this.getGeneratePicActions(), /* ccRelativeName= */ null, /* autoFdoImportPath= */ null, /* gcnoFile= */ null, /* dwoFile= */ null, /* ltoIndexingFile= */ null, builder.getContext().getCppModuleMap(), /* sourceSpecificBuildVariables= */ ImmutableMap.of()); semantics.finalizeCompileActionBuilder( ruleContext, builder, featureConfiguration.getFeatureSpecification(), coptsFilter, features); CppCompileAction compileAction = builder.buildOrThrowRuleError(ruleContext); env.registerAction(compileAction); Artifact tokenFile = compileAction.getOutputFile(); result.addHeaderTokenFile(tokenFile); } private void createModuleCodegenAction( CcCompilationOutputs.Builder result, Label sourceLabel, Artifact module) throws RuleErrorException { if (fake) { // We can't currently foresee a situation where we'd want nocompile tests for module codegen. // If we find one, support needs to be added here. return; } String outputName = semantics.getEffectiveSourcePath(module).getPathString(); // TODO(djasper): Make this less hacky after refactoring how the PIC/noPIC actions are created. boolean pic = module.getFilename().contains(".pic."); CppCompileActionBuilder builder = initializeCompileAction(module); builder.setSemantics(semantics); builder.setPicMode(pic); builder.setOutputs( ruleContext, ArtifactCategory.OBJECT_FILE, outputName, isGenerateDotdFile(module)); PathFragment ccRelativeName = semantics.getEffectiveSourcePath(module); String gcnoFileName = CppHelper.getArtifactNameForCategory( ruleContext, ccToolchain, ArtifactCategory.COVERAGE_DATA_FILE, outputName); // TODO(djasper): This is now duplicated. Refactor the various create..Action functions. Artifact gcnoFile = isCodeCoverageEnabled() && !cppConfiguration.isLipoOptimization() ? CppHelper.getCompileOutputArtifact(ruleContext, gcnoFileName, configuration) : null; boolean generateDwo = cppConfiguration.useFission(); Artifact dwoFile = generateDwo ? getDwoFile(builder.getOutputFile()) : null; // TODO(tejohnson): Add support for ThinLTO if needed. boolean bitcodeOutput = featureConfiguration.isEnabled(CppRuleClasses.THIN_LTO) && CppFileTypes.LTO_SOURCE.matches(module.getFilename()); Preconditions.checkState(!bitcodeOutput); setupCompileBuildVariables( builder, sourceLabel, /* usePic= */ pic, ccRelativeName, module.getExecPath(), gcnoFile, dwoFile, /* ltoIndexingFile= */ null, builder.getContext().getCppModuleMap(), /* sourceSpecificBuildVariables= */ ImmutableMap.of()); builder.setGcnoFile(gcnoFile); builder.setDwoFile(dwoFile); semantics.finalizeCompileActionBuilder( ruleContext, builder, featureConfiguration.getFeatureSpecification(), coptsFilter, features); CppCompileAction compileAction = builder.buildOrThrowRuleError(ruleContext); AnalysisEnvironment env = ruleContext.getAnalysisEnvironment(); env.registerAction(compileAction); Artifact objectFile = compileAction.getOutputFile(); if (pic) { result.addPicObjectFile(objectFile); } else { result.addObjectFile(objectFile); } } private Collection createModuleAction( CcCompilationOutputs.Builder result, CppModuleMap cppModuleMap) throws RuleErrorException { AnalysisEnvironment env = ruleContext.getAnalysisEnvironment(); Artifact moduleMapArtifact = cppModuleMap.getArtifact(); CppCompileActionBuilder builder = initializeCompileAction(moduleMapArtifact); builder.setSemantics(semantics); // A header module compile action is just like a normal compile action, but: // - the compiled source file is the module map // - it creates a header module (.pcm file). return createSourceAction( Label.parseAbsoluteUnchecked(cppModuleMap.getName()), FileSystemUtils.removeExtension(semantics.getEffectiveSourcePath(moduleMapArtifact)) .getPathString(), result, env, moduleMapArtifact, builder, ArtifactCategory.CPP_MODULE, cppModuleMap, /* addObject= */ false, /* enableCoverage= */ false, /* generateDwo= */ false, isGenerateDotdFile(moduleMapArtifact), ImmutableMap.of()); } private void createClifMatchAction( Label sourceLabel, String outputName, Builder result, AnalysisEnvironment env, CppCompileActionBuilder builder) throws RuleErrorException { builder .setOutputs( ruleContext, ArtifactCategory.CLIF_OUTPUT_PROTO, outputName, /* generateDotd= */ true) .setPicMode(false) // The additional headers in a clif action are both mandatory inputs and // need to be include-scanned. .addMandatoryInputs(mandatoryInputs) .addAdditionalIncludes(mandatoryInputs); setupCompileBuildVariables( builder, sourceLabel, /* usePic= */ false, /* ccRelativeName= */ null, /* autoFdoImportPath= */ null, /* gcnoFile= */ null, /* dwoFile= */ null, /* ltoIndexingFile= */ null, builder.getContext().getCppModuleMap(), /* sourceSpecificBuildVariables= */ ImmutableMap.of()); semantics.finalizeCompileActionBuilder( ruleContext, builder, featureConfiguration.getFeatureSpecification(), coptsFilter, features); CppCompileAction compileAction = builder.buildOrThrowRuleError(ruleContext); env.registerAction(compileAction); Artifact tokenFile = compileAction.getOutputFile(); result.addHeaderTokenFile(tokenFile); } private Collection createSourceAction( Label sourceLabel, String outputName, CcCompilationOutputs.Builder result, AnalysisEnvironment env, Artifact sourceArtifact, CppCompileActionBuilder builder, ArtifactCategory outputCategory, CppModuleMap cppModuleMap, boolean addObject, boolean enableCoverage, boolean generateDwo, boolean generateDotd, Map sourceSpecificBuildVariables) throws RuleErrorException { ImmutableList.Builder directOutputs = new ImmutableList.Builder<>(); PathFragment ccRelativeName = semantics.getEffectiveSourcePath(sourceArtifact); if (cppConfiguration.isLipoOptimization()) { // TODO(bazel-team): we shouldn't be needing this, merging context with the binary // is a superset of necessary information. LipoContextProvider lipoProvider = Preconditions.checkNotNull(CppHelper.getLipoContextProvider(ruleContext), outputName); builder.setContext(CppCompilationContext.mergeForLipo(lipoProvider.getLipoContext(), context)); } boolean generatePicAction = getGeneratePicActions(); boolean generateNoPicAction = getGenerateNoPicActions(); Preconditions.checkState(generatePicAction || generateNoPicAction); if (fake) { boolean usePic = !generateNoPicAction; createFakeSourceAction( sourceLabel, outputName, result, env, builder, outputCategory, addObject, ccRelativeName, sourceArtifact.getExecPath(), usePic, generateDotd); } else { boolean bitcodeOutput = featureConfiguration.isEnabled(CppRuleClasses.THIN_LTO) && CppFileTypes.LTO_SOURCE.matches(sourceArtifact.getFilename()); // Create PIC compile actions (same as non-PIC, but use -fPIC and // generate .pic.o, .pic.d, .pic.gcno instead of .o, .d, .gcno.) if (generatePicAction) { String picOutputBase = CppHelper.getArtifactNameForCategory(ruleContext, ccToolchain, ArtifactCategory.PIC_FILE, outputName); CppCompileActionBuilder picBuilder = copyAsPicBuilder( builder, picOutputBase, outputCategory, generateDotd); String gcnoFileName = CppHelper.getArtifactNameForCategory(ruleContext, ccToolchain, ArtifactCategory.COVERAGE_DATA_FILE, picOutputBase); Artifact gcnoFile = enableCoverage ? CppHelper.getCompileOutputArtifact(ruleContext, gcnoFileName, configuration) : null; Artifact dwoFile = generateDwo ? getDwoFile(picBuilder.getOutputFile()) : null; Artifact ltoIndexingFile = bitcodeOutput ? getLtoIndexingFile(picBuilder.getOutputFile()) : null; setupCompileBuildVariables( picBuilder, sourceLabel, /* usePic= */ true, ccRelativeName, sourceArtifact.getExecPath(), gcnoFile, dwoFile, ltoIndexingFile, cppModuleMap, sourceSpecificBuildVariables); if (maySaveTemps) { result.addTemps( createTempsActions( sourceArtifact, sourceLabel, outputName, picBuilder, /* usePic= */ true, /* generateDotd= */ generateDotd, ccRelativeName)); } picBuilder.setGcnoFile(gcnoFile); picBuilder.setDwoFile(dwoFile); picBuilder.setLtoIndexingFile(ltoIndexingFile); semantics.finalizeCompileActionBuilder( ruleContext, picBuilder, featureConfiguration.getFeatureSpecification(), coptsFilter, features); CppCompileAction picAction = picBuilder.buildOrThrowRuleError(ruleContext); env.registerAction(picAction); directOutputs.add(picAction.getOutputFile()); if (addObject) { result.addPicObjectFile(picAction.getOutputFile()); if (bitcodeOutput) { result.addLtoBitcodeFile(picAction.getOutputFile(), ltoIndexingFile); } } if (dwoFile != null) { // Host targets don't produce .dwo files. result.addPicDwoFile(dwoFile); } if (cppConfiguration.isLipoContextCollector() && !generateNoPicAction) { result.addLipoScannable(picAction); } } if (generateNoPicAction) { Artifact noPicOutputFile = CppHelper.getCompileOutputArtifact( ruleContext, CppHelper.getArtifactNameForCategory( ruleContext, ccToolchain, outputCategory, outputName), configuration); builder.setOutputs(ruleContext, outputCategory, outputName, generateDotd); String gcnoFileName = CppHelper.getArtifactNameForCategory(ruleContext, ccToolchain, ArtifactCategory.COVERAGE_DATA_FILE, outputName); // Create non-PIC compile actions Artifact gcnoFile = !cppConfiguration.isLipoOptimization() && enableCoverage ? CppHelper.getCompileOutputArtifact(ruleContext, gcnoFileName, configuration) : null; Artifact noPicDwoFile = generateDwo ? getDwoFile(noPicOutputFile) : null; Artifact ltoIndexingFile = bitcodeOutput ? getLtoIndexingFile(builder.getOutputFile()) : null; setupCompileBuildVariables( builder, sourceLabel, /* usePic= */ false, ccRelativeName, sourceArtifact.getExecPath(), gcnoFile, noPicDwoFile, ltoIndexingFile, cppModuleMap, sourceSpecificBuildVariables); if (maySaveTemps) { result.addTemps( createTempsActions( sourceArtifact, sourceLabel, outputName, builder, /* usePic= */ false, generateDotd, ccRelativeName)); } builder.setGcnoFile(gcnoFile); builder.setDwoFile(noPicDwoFile); builder.setLtoIndexingFile(ltoIndexingFile); semantics.finalizeCompileActionBuilder( ruleContext, builder, featureConfiguration.getFeatureSpecification(), coptsFilter, features); CppCompileAction compileAction = builder.buildOrThrowRuleError(ruleContext); env.registerAction(compileAction); Artifact objectFile = compileAction.getOutputFile(); directOutputs.add(objectFile); if (addObject) { result.addObjectFile(objectFile); if (bitcodeOutput) { result.addLtoBitcodeFile(objectFile, ltoIndexingFile); } } if (noPicDwoFile != null) { // Host targets don't produce .dwo files. result.addDwoFile(noPicDwoFile); } if (cppConfiguration.isLipoContextCollector()) { result.addLipoScannable(compileAction); } } } return directOutputs.build(); } private Artifact createCompileActionTemplate(AnalysisEnvironment env, CppSource source, CppCompileActionBuilder builder, Iterable outputCategories) { Artifact sourceArtifact = source.getSource(); Artifact outputFiles = CppHelper.getCompileOutputTreeArtifact(ruleContext, sourceArtifact); // TODO(rduan): Dotd file output is not supported yet. builder.setOutputs(outputFiles, /* dotdFile= */ null); setupCompileBuildVariables( builder, source.getLabel(), /* usePic= */ false, /* ccRelativeName= */ null, /* autoFdoImportPath= */ null, /* gcnoFile= */ null, /* dwoFile= */ null, /* ltoIndexingFile= */ null, builder.getContext().getCppModuleMap(), source.getBuildVariables()); semantics.finalizeCompileActionBuilder( ruleContext, builder, featureConfiguration.getFeatureSpecification(), coptsFilter, features); // Make sure this builder doesn't reference ruleContext outside of analysis phase. CppCompileActionTemplate actionTemplate = new CppCompileActionTemplate( sourceArtifact, outputFiles, builder, cppConfiguration, outputCategories, ruleContext.getActionOwner()); env.registerAction(actionTemplate); return outputFiles; } String getOutputNameBaseWith(String base, boolean usePic) { return usePic ? CppHelper.getArtifactNameForCategory( ruleContext, ccToolchain, ArtifactCategory.PIC_FILE, base) : base; } private void createFakeSourceAction( Label sourceLabel, String outputName, CcCompilationOutputs.Builder result, AnalysisEnvironment env, CppCompileActionBuilder builder, ArtifactCategory outputCategory, boolean addObject, PathFragment ccRelativeName, PathFragment execPath, boolean usePic, boolean generateDotd) throws RuleErrorException { String outputNameBase = getOutputNameBaseWith(outputName, usePic); String tempOutputName = ruleContext.getConfiguration().getBinFragment() .getRelative(CppHelper.getObjDirectory(ruleContext.getLabel())) .getRelative( CppHelper.getArtifactNameForCategory(ruleContext, ccToolchain, outputCategory, getOutputNameBaseWith(outputName + ".temp", usePic))) .getPathString(); builder .setPicMode(usePic) .setOutputs(ruleContext, outputCategory, outputNameBase, generateDotd) .setTempOutputFile(PathFragment.create(tempOutputName)); setupCompileBuildVariables( builder, sourceLabel, usePic, ccRelativeName, execPath, /* gcnoFile= */ null, /* dwoFile= */ null, /* ltoIndexingFile= */ null, builder.getContext().getCppModuleMap(), /* sourceSpecificBuildVariables= */ ImmutableMap.of()); semantics.finalizeCompileActionBuilder( ruleContext, builder, featureConfiguration.getFeatureSpecification(), coptsFilter, features); CppCompileAction action = builder.buildOrThrowRuleError(ruleContext); env.registerAction(action); if (addObject) { if (usePic) { result.addPicObjectFile(action.getOutputFile()); } else { result.addObjectFile(action.getOutputFile()); } } } /** * Returns the linked artifact resulting from a linking of the given type. Consults the feature * configuration to obtain an action_config that provides the artifact. If the feature * configuration provides no artifact, uses a default. * *

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. * *

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 nonCodeLinkerInputs) throws RuleErrorException, InterruptedException { // For now only handle static links. Note that the dynamic library link below ignores linkType. // TODO(bazel-team): Either support non-static links or move this check to setLinkType(). Preconditions.checkState( linkType.staticness() == Staticness.STATIC, "can only handle static links"); CcLinkingOutputs.Builder result = new CcLinkingOutputs.Builder(); if (cppConfiguration.isLipoContextCollector()) { // Don't try to create LIPO link actions in collector mode, // because it needs some data that's not available at this point. return result.build(); } AnalysisEnvironment env = ruleContext.getAnalysisEnvironment(); boolean usePicForBinaries = CppHelper.usePic(ruleContext, /* forBinary= */ true); boolean usePicForSharedLibs = CppHelper.usePic(ruleContext, /* forBinary= */ false); // Create static library (.a). The linkType only reflects whether the library is alwayslink or // not. The PIC-ness is determined by whether we need to use PIC or not. There are three cases // for (usePicForSharedLibs usePicForBinaries): // // (1) (false false) -> no pic code // (2) (true false) -> shared libraries as pic, but not binaries // (3) (true true) -> both shared libraries and binaries as pic // // In case (3), we always need PIC, so only create one static library containing the PIC object // files. The name therefore does not match the content. // // Presumably, it is done this way because the .a file is an implicit output of every cc_library // rule, so we can't use ".pic.a" that in the always-PIC case. // If the crosstool is configured to select an output artifact, we use that selection. // Otherwise, we use linux defaults. Artifact linkedArtifact = getLinkedArtifact(linkType); PathFragment labelName = PathFragment.create(ruleContext.getLabel().getName()); String libraryIdentifier = ruleContext.getPackageDirectory().getRelative( labelName.replaceName("lib" + labelName.getBaseName())).getPathString(); CppLinkAction maybePicAction = newLinkActionBuilder(linkedArtifact) .addObjectFiles(ccOutputs.getObjectFiles(usePicForBinaries)) .addNonCodeInputs(nonCodeLinkerInputs) .addLtoBitcodeFiles(ccOutputs.getLtoBitcodeFiles()) .setLinkType(linkType) .setLinkStaticness(LinkStaticness.FULLY_STATIC) .addActionInputs(linkActionInputs) .setLibraryIdentifier(libraryIdentifier) .addVariablesExtensions(variablesExtensions) .build(); env.registerAction(maybePicAction); if (linkType != LinkTargetType.EXECUTABLE) { result.addStaticLibrary(maybePicAction.getOutputLibrary()); } // Create a second static library (.pic.a). Only in case (2) do we need both PIC and non-PIC // static libraries. In that case, the first static library contains the non-PIC code, and this // one contains the PIC code, so the names match the content. if (!usePicForBinaries && usePicForSharedLibs) { LinkTargetType picLinkType = (linkType == LinkTargetType.ALWAYS_LINK_STATIC_LIBRARY) ? LinkTargetType.ALWAYS_LINK_PIC_STATIC_LIBRARY : LinkTargetType.PIC_STATIC_LIBRARY; // If the crosstool is configured to select an output artifact, we use that selection. // Otherwise, we use linux defaults. Artifact picArtifact = getLinkedArtifact(picLinkType); CppLinkAction picAction = newLinkActionBuilder(picArtifact) .addObjectFiles(ccOutputs.getObjectFiles(/* usePic= */ true)) .addLtoBitcodeFiles(ccOutputs.getLtoBitcodeFiles()) .setLinkType(picLinkType) .setLinkStaticness(LinkStaticness.FULLY_STATIC) .addActionInputs(linkActionInputs) .setLibraryIdentifier(libraryIdentifier) .addVariablesExtensions(variablesExtensions) .build(); env.registerAction(picAction); if (linkType != LinkTargetType.EXECUTABLE) { result.addPicStaticLibrary(picAction.getOutputLibrary()); } } if (!createDynamicLibrary) { return result.build(); } // Create dynamic library. Artifact soImpl; String mainLibraryIdentifier; if (soImplArtifact == null) { // If the crosstool is configured to select an output artifact, we use that selection. // Otherwise, we use linux defaults. soImpl = getLinkedArtifact(LinkTargetType.DYNAMIC_LIBRARY); mainLibraryIdentifier = libraryIdentifier; } else { // This branch is only used for vestigial Google-internal rules where the name of the output // file is explicitly specified in the BUILD file and as such, is platform-dependent. Thus, // we just hardcode some reasonable logic to compute the library identifier and hope that this // will eventually go away. soImpl = soImplArtifact; mainLibraryIdentifier = FileSystemUtils.removeExtension( soImpl.getRootRelativePath().getPathString()); } List sonameLinkopts = ImmutableList.of(); Artifact soInterface = null; if (cppConfiguration.useInterfaceSharedObjects() && allowInterfaceSharedObjects) { soInterface = CppHelper.getLinuxLinkedArtifact( ruleContext, configuration, LinkTargetType.INTERFACE_DYNAMIC_LIBRARY, linkedArtifactNameSuffix); // TODO(b/28946988): Remove this hard-coded flag. if (!featureConfiguration.isEnabled(CppRuleClasses.TARGETS_WINDOWS)) { sonameLinkopts = ImmutableList.of( "-Wl,-soname=" + SolibSymlinkAction.getDynamicLibrarySoname( soImpl.getRootRelativePath(), /* preserveName= */ false)); } } CppLinkActionBuilder dynamicLinkActionBuilder = newLinkActionBuilder(soImpl) .setInterfaceOutput(soInterface) .addObjectFiles(ccOutputs.getObjectFiles(usePicForSharedLibs)) .addNonCodeInputs(ccOutputs.getHeaderTokenFiles()) .addLtoBitcodeFiles(ccOutputs.getLtoBitcodeFiles()) .setLinkType(LinkTargetType.DYNAMIC_LIBRARY) .setLinkStaticness(LinkStaticness.DYNAMIC) .addActionInputs(linkActionInputs) .setLibraryIdentifier(mainLibraryIdentifier) .addLinkopts(linkopts) .addLinkopts(sonameLinkopts) .setRuntimeInputs( ArtifactCategory.DYNAMIC_LIBRARY, ccToolchain.getDynamicRuntimeLinkMiddleman(), ccToolchain.getDynamicRuntimeLinkInputs()) .addVariablesExtensions(variablesExtensions); if (CppHelper.shouldUseDefFile(featureConfiguration)) { Artifact defFile = CppHelper.createDefFileActions( ruleContext, ccToolchain.getDefParserTool(), ccOutputs.getObjectFiles(false), SolibSymlinkAction.getDynamicLibrarySoname(soImpl.getRootRelativePath(), true)); dynamicLinkActionBuilder.setDefFile(defFile); } // On Windows, we cannot build a shared library with symbols unresolved, so here we dynamically // link to all it's dependencies. if (featureConfiguration.isEnabled(CppRuleClasses.TARGETS_WINDOWS)) { CcLinkParams.Builder ccLinkParamsBuilder = CcLinkParams.builder(/* linkingStatically= */ false, /* linkShared= */ true); ccLinkParamsBuilder.addCcLibrary(ruleContext); dynamicLinkActionBuilder.addLinkParams(ccLinkParamsBuilder.build(), ruleContext); } if (!ccOutputs.getLtoBitcodeFiles().isEmpty() && featureConfiguration.isEnabled(CppRuleClasses.THIN_LTO)) { dynamicLinkActionBuilder.setLtoIndexing(true); dynamicLinkActionBuilder.setUsePicForLtoBackendActions(usePicForSharedLibs); CppLinkAction indexAction = dynamicLinkActionBuilder.build(); env.registerAction(indexAction); dynamicLinkActionBuilder.setLtoIndexing(false); } CppLinkAction dynamicLinkAction = dynamicLinkActionBuilder.build(); env.registerAction(dynamicLinkAction); if (linkType == LinkTargetType.EXECUTABLE) { return result.build(); } LibraryToLink dynamicLibrary = dynamicLinkAction.getOutputLibrary(); LibraryToLink interfaceLibrary = dynamicLinkAction.getInterfaceOutputLibrary(); if (interfaceLibrary == null) { interfaceLibrary = dynamicLibrary; } // If shared library has neverlink=1, then leave it untouched. Otherwise, // create a mangled symlink for it and from now on reference it through // mangled name only. // // When COPY_DYNAMIC_LIBRARIES_TO_BINARY is enabled, we don't need to create the special // solibDir, instead we use the original interface library and dynamic library. if (neverLink || featureConfiguration.isEnabled(CppRuleClasses.COPY_DYNAMIC_LIBRARIES_TO_BINARY)) { result.addDynamicLibrary(interfaceLibrary); result.addExecutionDynamicLibrary(dynamicLibrary); } else { Artifact libraryLink = SolibSymlinkAction.getDynamicLibrarySymlink( ruleContext, interfaceLibrary.getArtifact(), /* preserveName= */ false, /* prefixConsumer= */ false, ruleContext.getConfiguration()); result.addDynamicLibrary(LinkerInputs.solibLibraryToLink( libraryLink, interfaceLibrary.getArtifact(), libraryIdentifier)); Artifact implLibraryLink = SolibSymlinkAction.getDynamicLibrarySymlink( ruleContext, dynamicLibrary.getArtifact(), /* preserveName= */ false, /* prefixConsumer= */ false, ruleContext.getConfiguration()); result.addExecutionDynamicLibrary(LinkerInputs.solibLibraryToLink( implLibraryLink, dynamicLibrary.getArtifact(), libraryIdentifier)); } return result.build(); } private CppLinkActionBuilder newLinkActionBuilder(Artifact outputArtifact) { return new CppLinkActionBuilder( ruleContext, outputArtifact, ccToolchain, fdoSupport, featureConfiguration, semantics) .setCrosstoolInputs(ccToolchain.getLink()) .addNonCodeInputs(context.getTransitiveCompilationPrerequisites()); } /** * Creates a basic cpp compile action builder for source file. Configures options, crosstool * inputs, output and dotd file names, compilation context and copts. */ private CppCompileActionBuilder createCompileActionBuilder(Artifact source) { CppCompileActionBuilder builder = new CppCompileActionBuilder(ruleContext, ccToolchain, configuration); builder.setSourceFile(source); builder.setContext(context); builder.addEnvironment(ccToolchain.getEnvironment()); return builder; } /** * Creates cpp PIC compile action builder from the given builder by adding necessary copt and * changing output and dotd file names. */ private CppCompileActionBuilder copyAsPicBuilder(CppCompileActionBuilder builder, String outputName, ArtifactCategory outputCategory, boolean generateDotd) { CppCompileActionBuilder picBuilder = new CppCompileActionBuilder(builder); picBuilder .setPicMode(true) .setOutputs(ruleContext, outputCategory, outputName, generateDotd); return picBuilder; } /** Create the actions for "--save_temps". */ private ImmutableList createTempsActions( Artifact source, Label sourceLabel, String outputName, CppCompileActionBuilder builder, boolean usePic, boolean generateDotd, PathFragment ccRelativeName) throws RuleErrorException { if (!cppConfiguration.getSaveTemps()) { return ImmutableList.of(); } String path = source.getFilename(); boolean isCFile = CppFileTypes.C_SOURCE.matches(path); boolean isCppFile = CppFileTypes.CPP_SOURCE.matches(path); if (!isCFile && !isCppFile) { return ImmutableList.of(); } ArtifactCategory category = isCFile ? ArtifactCategory.PREPROCESSED_C_SOURCE : ArtifactCategory.PREPROCESSED_CPP_SOURCE; String outputArtifactNameBase = getOutputNameBaseWith(outputName, usePic); CppCompileActionBuilder dBuilder = new CppCompileActionBuilder(builder); dBuilder.setOutputs(ruleContext, category, outputArtifactNameBase, generateDotd); setupCompileBuildVariables( dBuilder, sourceLabel, usePic, ccRelativeName, source.getExecPath(), /* gcnoFile= */ null, /* dwoFile= */ null, /* ltoIndexingFile= */ null, builder.getContext().getCppModuleMap(), /* sourceSpecificBuildVariables= */ ImmutableMap.of()); semantics.finalizeCompileActionBuilder( ruleContext, dBuilder, featureConfiguration.getFeatureSpecification(), coptsFilter, features); CppCompileAction dAction = dBuilder.buildOrThrowRuleError(ruleContext); ruleContext.registerAction(dAction); CppCompileActionBuilder sdBuilder = new CppCompileActionBuilder(builder); sdBuilder.setOutputs( ruleContext, ArtifactCategory.GENERATED_ASSEMBLY, outputArtifactNameBase, generateDotd); setupCompileBuildVariables( sdBuilder, sourceLabel, usePic, ccRelativeName, source.getExecPath(), /* gcnoFile= */ null, /* dwoFile= */ null, /* ltoIndexingFile= */ null, builder.getContext().getCppModuleMap(), /* sourceSpecificBuildVariables= */ ImmutableMap.of()); semantics.finalizeCompileActionBuilder( ruleContext, sdBuilder, featureConfiguration.getFeatureSpecification(), coptsFilter, features); CppCompileAction sdAction = sdBuilder.buildOrThrowRuleError(ruleContext); ruleContext.registerAction(sdAction); return ImmutableList.of( dAction.getOutputFile(), sdAction.getOutputFile()); } /** * Returns true iff code coverage is enabled for the given target. */ private boolean isCodeCoverageEnabled() { if (configuration.isCodeCoverageEnabled()) { // If rule is matched by the instrumentation filter, enable instrumentation if (InstrumentedFilesCollector.shouldIncludeLocalSources(ruleContext)) { return true; } // At this point the rule itself is not matched by the instrumentation filter. However, we // might still want to instrument C++ rules if one of the targets listed in "deps" is // instrumented and, therefore, can supply header files that we would want to collect code // coverage for. For example, think about cc_test rule that tests functionality defined in a // header file that is supplied by the cc_library. // // Note that we only check direct prerequisites and not the transitive closure. This is done // for two reasons: // a) It is a good practice to declare libraries which you directly rely on. Including headers // from a library hidden deep inside the transitive closure makes build dependencies less // readable and can lead to unexpected breakage. // b) Traversing the transitive closure for each C++ compile action would require more complex // implementation (with caching results of this method) to avoid O(N^2) slowdown. if (ruleContext.getRule().isAttrDefined("deps", BuildType.LABEL_LIST)) { for (TransitiveInfoCollection dep : ruleContext.getPrerequisites("deps", Mode.TARGET)) { if (dep.getProvider(CppCompilationContext.class) != null && InstrumentedFilesCollector.shouldIncludeLocalSources(configuration, dep)) { return true; } } } } return false; } }