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

This class is not thread-safe, and it should only be used once for each set of source files, * i.e. calling {@link #createCcCompileActions} will throw an Exception if called twice. */ public final class CppModel { private final CppSemantics semantics; private final RuleContext ruleContext; private final BuildConfiguration configuration; private final CppConfiguration cppConfiguration; // compile model private CppCompilationContext context; private CppCompilationContext interfaceContext; private final Set sourceFiles = new LinkedHashSet<>(); private final List mandatoryInputs = new ArrayList<>(); private final List copts = new ArrayList<>(); @Nullable private Pattern nocopts; private boolean fake; private boolean maySaveTemps; private boolean onlySingleOutput; private CcCompilationOutputs compilationOutputs; // link model private final List linkopts = new ArrayList<>(); private LinkTargetType linkType = LinkTargetType.STATIC_LIBRARY; private boolean neverLink; private final List linkActionInputs = new ArrayList<>(); private boolean allowInterfaceSharedObjects; private boolean createDynamicLibrary = true; private Artifact soImplArtifact; private FeatureConfiguration featureConfiguration; private List variablesExtensions = new ArrayList<>(); public CppModel(RuleContext ruleContext, CppSemantics semantics) { this.ruleContext = Preconditions.checkNotNull(ruleContext); this.semantics = semantics; configuration = ruleContext.getConfiguration(); cppConfiguration = ruleContext.getFragment(CppConfiguration.class); } private Artifact getDwoFile(Artifact outputFile) { return ruleContext.getRelatedArtifact(outputFile.getRootRelativePath(), ".dwo"); } /** * 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; } /** * Sets the compilation context, i.e. include directories and allowed header files inclusions, for * the compilation of this model's interface, e.g. header module. */ public CppModel setInterfaceContext(CppCompilationContext context) { this.interfaceContext = 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) { for (Artifact sourceFile : sourceFiles) { this.sourceFiles.add(CppSource.create(sourceFile, sourceLabel, buildVariables)); } return this; } /** * Adds all the source files. Note that this should only be called for primary compilation units, * including module files or headers to be parsed or preprocessed. */ public CppModel addCompilationUnitSources(Set sources) { this.sourceFiles.addAll(sources); return this; } /** Adds mandatory inputs. */ public CppModel addMandatoryInputs(Collection artifacts) { this.mandatoryInputs.addAll(artifacts); return this; } /** * Adds the given copts. */ public CppModel addCopts(Collection copts) { this.copts.addAll(copts); return this; } /** * Sets the nocopts pattern. This is used to filter out flags from the system defined set of * flags. By default no filter is applied. */ public CppModel setNoCopts(@Nullable Pattern nocopts) { this.nocopts = nocopts; return this; } /** * Adds the given linkopts to the optional dynamic library link command. */ public CppModel addLinkopts(Collection linkopts) { this.linkopts.addAll(linkopts); return this; } /** * Adds the given variablesExensions for templating the crosstool. * *

In general, we prefer the build variables (especially those that derive strictly from * the configuration) be learned by inspecting the CcToolchain, as passed to the rule in the * CcToolchainProvider. However, for build variables that must be injected into the rule * implementation (ex. build variables learned from the BUILD file), should be added using the * VariablesExtension abstraction. This allows the injection to construct non-trivial build * variables (lists, ect.). */ public CppModel addVariablesExtension(Collection variablesExtensions) { this.variablesExtensions.addAll(variablesExtensions); return this; } /** * Sets the link type used for the link actions. Note that only static links are supported at this * time. */ public CppModel setLinkTargetType(LinkTargetType linkType) { this.linkType = linkType; return this; } public CppModel setNeverLink(boolean neverLink) { this.neverLink = neverLink; return this; } /** * Adds an artifact to the inputs of any link actions created by this CppModel. */ public CppModel addLinkActionInputs(Collection inputs) { this.linkActionInputs.addAll(inputs); return this; } /** * Whether to allow interface dynamic libraries. Note that setting this to true only has an effect * if the configuration allows it. Defaults to false. */ public CppModel setAllowInterfaceSharedObjects(boolean allowInterfaceSharedObjects) { // TODO(bazel-team): Set the default to true, and require explicit action to disable it. this.allowInterfaceSharedObjects = allowInterfaceSharedObjects; return this; } public CppModel setCreateDynamicLibrary(boolean createDynamicLibrary) { this.createDynamicLibrary = createDynamicLibrary; return this; } public CppModel setDynamicLibrary(Artifact soImplFilename) { this.soImplArtifact = soImplFilename; return this; } /** * Sets the feature configuration to be used for C/C++ actions. */ public CppModel setFeatureConfiguration(FeatureConfiguration featureConfiguration) { this.featureConfiguration = featureConfiguration; return this; } /** * @returns whether we want to provide header modules for the current target. */ private boolean shouldProvideHeaderModules() { return featureConfiguration.isEnabled(CppRuleClasses.HEADER_MODULES) && !cppConfiguration.isLipoContextCollector(); } /** * @return the non-pic header module artifact for the current target. */ public Artifact getHeaderModule(Artifact moduleMapArtifact) { PathFragment objectDir = CppHelper.getObjDirectory(ruleContext.getLabel()); PathFragment outputName = objectDir.getRelative( semantics.getEffectiveSourcePath(moduleMapArtifact)); return ruleContext.getRelatedArtifact(outputName, ".pcm"); } /** * @return the pic header module artifact for the current target. */ public Artifact getPicHeaderModule(Artifact moduleMapArtifact) { PathFragment objectDir = CppHelper.getObjDirectory(ruleContext.getLabel()); PathFragment outputName = objectDir.getRelative( semantics.getEffectiveSourcePath(moduleMapArtifact)); return ruleContext.getRelatedArtifact(outputName, ".pic.pcm"); } /** * @return whether this target needs to generate pic actions. */ private boolean getGeneratePicActions() { return CppHelper.usePic(ruleContext, false); } /** * @return whether this target needs to generate non-pic actions. */ private boolean getGenerateNoPicActions() { return // If we always need pic for everything, then don't bother to create a no-pic action. (!CppHelper.usePic(ruleContext, true) || !CppHelper.usePic(ruleContext, false)) // onlySingleOutput guarantees that the code is only ever linked into a dynamic library - so // we don't need a no-pic action even if linking into a binary would require it. && !((onlySingleOutput && getGeneratePicActions())); } /** * @return whether this target needs to generate a pic header module. */ public boolean getGeneratesPicHeaderModule() { return shouldProvideHeaderModules() && !fake && getGeneratePicActions(); } /** * @return whether this target needs to generate a non-pic header module. */ public boolean getGeneratesNoPicHeaderModule() { return shouldProvideHeaderModules() && !fake && getGenerateNoPicActions(); } /** * Returns a {@code CppCompileActionBuilder} with the common fields for a C++ compile action being * initialized. */ private CppCompileActionBuilder initializeCompileAction( Artifact sourceArtifact, Label sourceLabel, boolean forInterface) { CppCompileActionBuilder builder = createCompileActionBuilder(sourceArtifact, sourceLabel, forInterface); if (nocopts != null) { builder.addNocopts(nocopts); } builder.setFeatureConfiguration(featureConfiguration); return builder; } /** * Get the safe path strings for a list of paths to use in the build variables. */ private Collection getSafePathStrings(Collection paths) { ImmutableSet.Builder result = ImmutableSet.builder(); for (PathFragment path : paths) { result.add(path.getSafePathString()); } return result.build(); } /** * Select .pcm inputs to pass on the command line depending on whether we are in pic or non-pic * mode. */ private Collection getHeaderModulePaths(CppCompileActionBuilder builder, boolean usePic) { Collection result = new LinkedHashSet<>(); Iterable artifacts = featureConfiguration.isEnabled(CppRuleClasses.HEADER_MODULE_INCLUDES_DEPENDENCIES) ? builder.getContext().getTopLevelModules(usePic) : builder.getContext().getTransitiveModules(usePic); for (Artifact artifact : artifacts) { result.add(artifact.getExecPathString()); } return result; } private void setupCompileBuildVariables( CppCompileActionBuilder builder, boolean usePic, PathFragment ccRelativeName, PathFragment autoFdoImportPath, Artifact gcnoFile, Artifact dwoFile, Map sourceSpecificBuildVariables) { CcToolchainFeatures.Variables.Builder buildVariables = new CcToolchainFeatures.Variables.Builder(); // TODO(bazel-team): Pull out string constants for all build variables. CppCompilationContext builderContext = builder.getContext(); CppModuleMap cppModuleMap = builderContext.getCppModuleMap(); Artifact sourceFile = builder.getSourceFile(); Artifact outputFile = builder.getOutputFile(); String realOutputFilePath; buildVariables.addVariable("source_file", sourceFile.getExecPathString()); buildVariables.addVariable("output_file", outputFile.getExecPathString()); if (builder.getTempOutputFile() != null) { realOutputFilePath = builder.getTempOutputFile().getPathString(); } else { realOutputFilePath = builder.getOutputFile().getExecPathString(); } if (FileType.contains(outputFile, CppFileTypes.ASSEMBLER, CppFileTypes.PIC_ASSEMBLER)) { buildVariables.addVariable("output_assembly_file", realOutputFilePath); } else if (FileType.contains(outputFile, CppFileTypes.PREPROCESSED_C, CppFileTypes.PREPROCESSED_CPP, CppFileTypes.PIC_PREPROCESSED_C, CppFileTypes.PIC_PREPROCESSED_CPP)) { buildVariables.addVariable("output_preprocess_file", realOutputFilePath); } else { buildVariables.addVariable("output_object_file", realOutputFilePath); } DotdFile dotdFile = CppFileTypes.mustProduceDotdFile(sourceFile.getPath().toString()) ? Preconditions.checkNotNull(builder.getDotdFile()) : null; // Set dependency_file to enable .d file generation. if (dotdFile != null) { buildVariables.addVariable("dependency_file", 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.addVariable("module_name", cppModuleMap.getName()); buildVariables.addVariable("module_map_file", cppModuleMap.getArtifact().getExecPathString()); CcToolchainFeatures.Variables.ValueSequence.Builder sequence = new CcToolchainFeatures.Variables.ValueSequence.Builder(); for (Artifact artifact : builderContext.getDirectModuleMaps()) { sequence.addValue(artifact.getExecPathString()); } buildVariables.addSequence("dependent_module_map_files", sequence.build()); } if (featureConfiguration.isEnabled(CppRuleClasses.USE_HEADER_MODULES)) { buildVariables.addSequenceVariable("module_files", getHeaderModulePaths(builder, usePic)); } if (featureConfiguration.isEnabled(CppRuleClasses.INCLUDE_PATHS)) { buildVariables.addSequenceVariable("include_paths", getSafePathStrings(builderContext.getIncludeDirs())); buildVariables.addSequenceVariable("quote_include_paths", getSafePathStrings(builderContext.getQuoteIncludeDirs())); buildVariables.addSequenceVariable("system_include_paths", getSafePathStrings(builderContext.getSystemIncludeDirs())); } if (featureConfiguration.isEnabled(CppRuleClasses.PREPROCESSOR_DEFINES)) { String fdoBuildStamp = CppHelper.getFdoBuildStamp(ruleContext); 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) + "\"") .build(); } else { defines = builderContext.getDefines(); } buildVariables.addSequenceVariable("preprocessor_defines", defines); } if (usePic) { if (!featureConfiguration.isEnabled(CppRuleClasses.PIC)) { ruleContext.ruleError("PIC compilation is requested but the toolchain does not support it"); } buildVariables.addVariable("pic", ""); } if (ccRelativeName != null) { CppHelper.getFdoSupport(ruleContext).configureCompilation(builder, buildVariables, ruleContext, ccRelativeName, autoFdoImportPath, usePic, featureConfiguration); } if (gcnoFile != null) { buildVariables.addVariable("gcov_gcno_file", gcnoFile.getExecPathString()); } if (dwoFile != null) { buildVariables.addVariable("per_object_debug_info_file", dwoFile.getExecPathString()); } buildVariables.addAllVariables(CppHelper.getToolchain(ruleContext).getBuildVariables()); buildVariables.addAllVariables(sourceSpecificBuildVariables); for (VariablesExtension extension : variablesExtensions) { extension.addVariables(buildVariables); } CcToolchainFeatures.Variables variables = buildVariables.build(); builder.setVariables(variables); } /** * 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() { CcCompilationOutputs.Builder result = new CcCompilationOutputs.Builder(); Preconditions.checkNotNull(context); AnalysisEnvironment env = ruleContext.getAnalysisEnvironment(); if (shouldProvideHeaderModules()) { Artifact moduleMapArtifact = context.getCppModuleMap().getArtifact(); Label moduleMapLabel = Label.parseAbsoluteUnchecked(context.getCppModuleMap().getName()); CppCompileActionBuilder builder = initializeCompileAction(moduleMapArtifact, moduleMapLabel, /*forInterface=*/ true); 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). createSourceAction( FileSystemUtils.removeExtension(semantics.getEffectiveSourcePath(moduleMapArtifact)) .getPathString(), result, env, moduleMapArtifact, builder, ArtifactCategory.CPP_MODULE, /*addObject=*/ false, /*enableCoverage=*/ false, /*generateDwo=*/ false, CppFileTypes.mustProduceDotdFile(moduleMapArtifact.getFilename()), ImmutableMap.of()); } for (CppSource source : sourceFiles) { Artifact sourceArtifact = source.getSource(); Label sourceLabel = source.getLabel(); String outputName = FileSystemUtils.removeExtension( semantics.getEffectiveSourcePath(sourceArtifact)).getPathString(); CppCompileActionBuilder builder = initializeCompileAction(sourceArtifact, sourceLabel, /*forInterface=*/ false); builder.setSemantics(semantics); if (CppFileTypes.CPP_HEADER.matches(source.getSource().getExecPath())) { createHeaderAction(outputName, result, env, builder, CppFileTypes.mustProduceDotdFile(sourceArtifact.getFilename())); } else if (CppFileTypes.CLIF_INPUT_PROTO.matches(source.getSource().getExecPath())) { createClifMatchAction(outputName, result, env, builder); } else { createSourceAction( outputName, result, env, sourceArtifact, builder, ArtifactCategory.OBJECT_FILE, /*addObject=*/ true, isCodeCoverageEnabled(), /*generateDwo=*/ cppConfiguration.useFission(), CppFileTypes.mustProduceDotdFile(sourceArtifact.getFilename()), source.getBuildVariables()); } } compilationOutputs = result.build(); return compilationOutputs; } private void createHeaderAction(String outputName, Builder result, AnalysisEnvironment env, CppCompileActionBuilder builder, boolean generateDotd) { String outputNameBase = CppHelper.getArtifactNameForCategory(ruleContext, ArtifactCategory.GENERATED_HEADER, outputName); builder .setOutputs(ArtifactCategory.PROCESSED_HEADER, outputNameBase, generateDotd) // If we generate pic actions, we prefer the header actions to use the pic artifacts. .setPicMode(this.getGeneratePicActions()); setupCompileBuildVariables( builder, this.getGeneratePicActions(), /*ccRelativeName=*/ null, /*autoFdoImportPath=*/ null, /*gcnoFile=*/ null, /*dwoFile=*/ null, ImmutableMap.of()); semantics.finalizeCompileActionBuilder(ruleContext, builder); CppCompileAction compileAction = builder.build(); env.registerAction(compileAction); Artifact tokenFile = compileAction.getOutputFile(); result.addHeaderTokenFile(tokenFile); } private void createClifMatchAction( String outputName, Builder result, AnalysisEnvironment env, CppCompileActionBuilder builder) { builder .setOutputs(ArtifactCategory.CLIF_OUTPUT_PROTO, outputName, false) .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, /* usePic=*/ false, /*ccRelativeName=*/ null, /*autoFdoImportPath=*/ null, /*gcnoFile=*/ null, /*dwoFile=*/ null, /*sourceSpecificBuildVariables=*/ ImmutableMap.of()); semantics.finalizeCompileActionBuilder(ruleContext, builder); CppCompileAction compileAction = builder.build(); env.registerAction(compileAction); Artifact tokenFile = compileAction.getOutputFile(); result.addHeaderTokenFile(tokenFile); } private void createSourceAction( String outputName, CcCompilationOutputs.Builder result, AnalysisEnvironment env, Artifact sourceArtifact, CppCompileActionBuilder builder, ArtifactCategory outputCategory, boolean addObject, boolean enableCoverage, boolean generateDwo, boolean generateDotd, Map sourceSpecificBuildVariables) { 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(); // If we always need pic for everything, then don't bother to create a no-pic action. boolean generateNoPicAction = getGenerateNoPicActions(); Preconditions.checkState(generatePicAction || generateNoPicAction); if (fake) { boolean usePic = !generateNoPicAction; createFakeSourceAction(outputName, result, env, builder, outputCategory, addObject, ccRelativeName, sourceArtifact.getExecPath(), usePic, generateDotd); } else { // 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, ArtifactCategory.PIC_FILE, outputName); CppCompileActionBuilder picBuilder = copyAsPicBuilder( builder, picOutputBase, outputCategory, generateDotd); String gcnoFileName = CppHelper.getArtifactNameForCategory(ruleContext, ArtifactCategory.COVERAGE_DATA_FILE, picOutputBase); Artifact gcnoFile = enableCoverage ? CppHelper.getCompileOutputArtifact(ruleContext, gcnoFileName) : null; Artifact dwoFile = generateDwo ? getDwoFile(picBuilder.getOutputFile()) : null; setupCompileBuildVariables( picBuilder, /*usePic=*/ true, ccRelativeName, sourceArtifact.getExecPath(), gcnoFile, dwoFile, sourceSpecificBuildVariables); if (maySaveTemps) { result.addTemps( createTempsActions(sourceArtifact, outputName, picBuilder, /*usePic=*/true, /*generateDotd=*/ generateDotd, ccRelativeName)); } picBuilder.setGcnoFile(gcnoFile); picBuilder.setDwoFile(dwoFile); semantics.finalizeCompileActionBuilder(ruleContext, picBuilder); CppCompileAction picAction = picBuilder.build(); env.registerAction(picAction); if (addObject) { result.addPicObjectFile(picAction.getOutputFile()); if (featureConfiguration.isEnabled(CppRuleClasses.THIN_LTO) && CppFileTypes.LTO_SOURCE.matches(sourceArtifact.getFilename())) { result.addLTOBitcodeFile(picAction.getOutputFile()); } } 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, outputCategory, outputName)); builder.setOutputs(outputCategory, outputName, generateDotd); String gcnoFileName = CppHelper.getArtifactNameForCategory(ruleContext, ArtifactCategory.COVERAGE_DATA_FILE, outputName); // Create non-PIC compile actions Artifact gcnoFile = !cppConfiguration.isLipoOptimization() && enableCoverage ? CppHelper.getCompileOutputArtifact(ruleContext, gcnoFileName) : null; Artifact noPicDwoFile = generateDwo ? getDwoFile(noPicOutputFile) : null; setupCompileBuildVariables( builder, /*usePic=*/ false, ccRelativeName, sourceArtifact.getExecPath(), gcnoFile, noPicDwoFile, sourceSpecificBuildVariables); if (maySaveTemps) { result.addTemps( createTempsActions( sourceArtifact, outputName, builder, /*usePic=*/ false, /*generateDotd*/ generateDotd, ccRelativeName)); } builder.setGcnoFile(gcnoFile); builder.setDwoFile(noPicDwoFile); semantics.finalizeCompileActionBuilder(ruleContext, builder); CppCompileAction compileAction = builder.build(); env.registerAction(compileAction); Artifact objectFile = compileAction.getOutputFile(); if (addObject) { result.addObjectFile(objectFile); if (featureConfiguration.isEnabled(CppRuleClasses.THIN_LTO) && CppFileTypes.LTO_SOURCE.matches(sourceArtifact.getFilename())) { result.addLTOBitcodeFile(objectFile); } } if (noPicDwoFile != null) { // Host targets don't produce .dwo files. result.addDwoFile(noPicDwoFile); } if (cppConfiguration.isLipoContextCollector()) { result.addLipoScannable(compileAction); } } } } String getOutputNameBaseWith(String base, boolean usePic) { return usePic ? CppHelper.getArtifactNameForCategory(ruleContext, ArtifactCategory.PIC_FILE, base) : base; } private void createFakeSourceAction(String outputName, CcCompilationOutputs.Builder result, AnalysisEnvironment env, CppCompileActionBuilder builder, ArtifactCategory outputCategory, boolean addObject, PathFragment ccRelativeName, PathFragment execPath, boolean usePic, boolean generateDotd) { String outputNameBase = getOutputNameBaseWith(outputName, usePic); String tempOutputName = ruleContext.getConfiguration().getBinFragment() .getRelative(CppHelper.getObjDirectory(ruleContext.getLabel())) .getRelative(CppHelper.getArtifactNameForCategory(ruleContext, outputCategory, getOutputNameBaseWith(outputName + ".temp", usePic))) .getPathString(); builder .setPicMode(usePic) .setOutputs(outputCategory, outputNameBase, generateDotd) .setTempOutputFile(new PathFragment(tempOutputName)); setupCompileBuildVariables( builder, usePic, ccRelativeName, execPath, /*gcnoFile=*/ null, /*dwoFile=*/ null, ImmutableMap.of()); semantics.finalizeCompileActionBuilder(ruleContext, builder); CppCompileAction action = builder.build(); 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, linkTargetType); try { String maybePicName = ruleContext.getLabel().getName(); if (linkTargetType.picness() == Picness.PIC) { maybePicName = CppHelper.getArtifactNameForCategory( ruleContext, ArtifactCategory.PIC_FILE, maybePicName); } String linkedName = CppHelper.getArtifactNameForCategory( ruleContext, linkTargetType.getLinkerOutput(), maybePicName); PathFragment artifactFragment = new PathFragment(ruleContext.getLabel().getName()) .getParentDirectory().getRelative(linkedName); result = ruleContext.getBinArtifact(artifactFragment); } 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, true); boolean usePicForSharedLibs = CppHelper.usePic(ruleContext, 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 = new PathFragment(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) .setFeatureConfiguration(featureConfiguration) .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(true)) .addLTOBitcodeFiles(ccOutputs.getLtoBitcodeFiles()) .setLinkType(picLinkType) .setLinkStaticness(LinkStaticness.FULLY_STATIC) .addActionInputs(linkActionInputs) .setLibraryIdentifier(libraryIdentifier) .addVariablesExtensions(variablesExtensions) .setFeatureConfiguration(featureConfiguration) .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, LinkTargetType.INTERFACE_DYNAMIC_LIBRARY); sonameLinkopts = ImmutableList.of("-Wl,-soname=" + SolibSymlinkAction.getDynamicLibrarySoname(soImpl.getRootRelativePath(), false)); } // Should we also link in any libraries that this library depends on? // That is required on some systems... CppLinkActionBuilder linkActionBuilder = 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, CppHelper.getToolchain(ruleContext).getDynamicRuntimeLinkMiddleman(), CppHelper.getToolchain(ruleContext).getDynamicRuntimeLinkInputs()) .setFeatureConfiguration(featureConfiguration) .addVariablesExtensions(variablesExtensions); if (!ccOutputs.getLtoBitcodeFiles().isEmpty() && featureConfiguration.isEnabled(CppRuleClasses.THIN_LTO)) { linkActionBuilder.setLTOIndexing(true); CppLinkAction indexAction = linkActionBuilder.build(); env.registerAction(indexAction); for (LTOBackendArtifacts ltoArtifacts : indexAction.getAllLTOBackendArtifacts()) { ltoArtifacts.scheduleLTOBackendAction( ruleContext, featureConfiguration, usePicForSharedLibs); } linkActionBuilder.setLTOIndexing(false); } CppLinkAction action = linkActionBuilder.build(); env.registerAction(action); if (linkType == LinkTargetType.EXECUTABLE) { return result.build(); } LibraryToLink dynamicLibrary = action.getOutputLibrary(); LibraryToLink interfaceLibrary = action.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. if (neverLink) { result.addDynamicLibrary(interfaceLibrary); result.addExecutionDynamicLibrary(dynamicLibrary); } else { Artifact libraryLink = SolibSymlinkAction.getDynamicLibrarySymlink( ruleContext, interfaceLibrary.getArtifact(), false, false, ruleContext.getConfiguration()); result.addDynamicLibrary(LinkerInputs.solibLibraryToLink( libraryLink, interfaceLibrary.getArtifact(), libraryIdentifier)); Artifact implLibraryLink = SolibSymlinkAction.getDynamicLibrarySymlink( ruleContext, dynamicLibrary.getArtifact(), false, false, ruleContext.getConfiguration()); result.addExecutionDynamicLibrary(LinkerInputs.solibLibraryToLink( implLibraryLink, dynamicLibrary.getArtifact(), libraryIdentifier)); } return result.build(); } private CppLinkActionBuilder newLinkActionBuilder(Artifact outputArtifact) { return new CppLinkActionBuilder(ruleContext, outputArtifact) .setCrosstoolInputs(CppHelper.getToolchain(ruleContext).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, Label label, boolean forInterface) { CppCompileActionBuilder builder = new CppCompileActionBuilder( ruleContext, source, label); builder.setContext(forInterface ? interfaceContext : context).addCopts(copts); builder.addEnvironment(CppHelper.getToolchain(ruleContext).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(outputCategory, outputName, generateDotd); return picBuilder; } /** * Create the actions for "--save_temps". */ private ImmutableList createTempsActions(Artifact source, String outputName, CppCompileActionBuilder builder, boolean usePic, boolean generateDotd, PathFragment ccRelativeName) { 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(category, outputArtifactNameBase, generateDotd); setupCompileBuildVariables( dBuilder, usePic, ccRelativeName, source.getExecPath(), null, null, ImmutableMap.of()); semantics.finalizeCompileActionBuilder(ruleContext, dBuilder); CppCompileAction dAction = dBuilder.build(); ruleContext.registerAction(dAction); CppCompileActionBuilder sdBuilder = new CppCompileActionBuilder(builder); sdBuilder.setOutputs(ArtifactCategory.GENERATED_ASSEMBLY, outputArtifactNameBase, generateDotd); setupCompileBuildVariables( sdBuilder, usePic, ccRelativeName, source.getExecPath(), null, null, ImmutableMap.of()); semantics.finalizeCompileActionBuilder(ruleContext, sdBuilder); CppCompileAction sdAction = sdBuilder.build(); 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; } }