// Copyright 2014 Google Inc. 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.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.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.collect.nestedset.NestedSet; import com.google.devtools.build.lib.packages.Type; import com.google.devtools.build.lib.rules.cpp.CcCompilationOutputs.Builder; import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.FeatureConfiguration; 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.LinkerInputs.LibraryToLink; import com.google.devtools.build.lib.syntax.Label; import com.google.devtools.build.lib.util.Pair; import com.google.devtools.build.lib.util.RegexFilter; 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.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 final List> sourceFiles = new ArrayList<>(); private final List copts = new ArrayList<>(); private final List additionalIncludes = 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 boolean allowInterfaceSharedObjects; private boolean createDynamicLibrary = true; private PathFragment soImplFilename; private FeatureConfiguration featureConfiguration; public CppModel(RuleContext ruleContext, CppSemantics semantics) { this.ruleContext = ruleContext; this.semantics = semantics; configuration = ruleContext.getConfiguration(); cppConfiguration = configuration.getFragment(CppConfiguration.class); } /** * 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. Note that this should only be called for primary * compilation units, not for header files or files that are otherwise included. */ public CppModel addSources(Iterable sourceFiles, Label sourceLabel) { for (Artifact sourceFile : sourceFiles) { this.sourceFiles.add(Pair.of(sourceFile, sourceLabel)); } return this; } /** * Adds all the source files. Note that this should only be called for primary compilation units, * not for header files or files that are otherwise included. */ public CppModel addSources(Iterable> sources) { Iterables.addAll(this.sourceFiles, sources); 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; } /** * This can be used to specify additional include directories, without modifying the compilation * context. */ public CppModel addAdditionalIncludes(Collection additionalIncludes) { // TODO(bazel-team): Maybe this could be handled by the compilation context instead? this.additionalIncludes.addAll(additionalIncludes); return this; } /** * Adds the given linkopts to the optional dynamic library link command. */ public CppModel addLinkopts(Collection linkopts) { this.linkopts.addAll(linkopts); 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; } /** * 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 setDynamicLibraryPath(PathFragment soImplFilename) { this.soImplFilename = soImplFilename; return this; } /** * Sets the feature configuration to be used for C/C++ actions. */ public CppModel setFeatureConfiguration(FeatureConfiguration featureConfiguration) { this.featureConfiguration = featureConfiguration; return this; } /** * @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() { // TODO(bazel-team): Make sure cc_fake_binary works with header module support. return featureConfiguration.isEnabled(CppRuleClasses.HEADER_MODULES) && !fake && getGeneratePicActions(); } /** * @return whether this target needs to generate a non-pic header module. */ public boolean getGeneratesNoPicHeaderModule() { return featureConfiguration.isEnabled(CppRuleClasses.HEADER_MODULES) && !fake && getGenerateNoPicActions(); } /** * Returns a {@code CppCompileActionBuilder} with the common fields for a C++ compile action * being initialized. */ private CppCompileActionBuilder initializeCompileAction(Artifact sourceArtifact, Label sourceLabel) { CppCompileActionBuilder builder = createCompileActionBuilder(sourceArtifact, sourceLabel); if (nocopts != null) { builder.addNocopts(nocopts); } builder.setExtraSystemIncludePrefixes(additionalIncludes); builder.setFdoBuildStamp(CppHelper.getFdoBuildStamp(cppConfiguration)); 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<>(); NestedSet artifacts = featureConfiguration.isEnabled( CppRuleClasses.HEADER_MODULE_INCLUDES_DEPENDENCIES) ? builder.getContext().getTopLevelHeaderModules() : builder.getContext().getAdditionalInputs(); for (Artifact artifact : artifacts) { String filename = artifact.getFilename(); if (!filename.endsWith(".pcm")) { continue; } // Depending on whether this specific compile action is pic or non-pic, select the // corresponding header modules. Note that the compilation context might give us both // from targets that are built in both modes. if (usePic == filename.endsWith(".pic.pcm")) { result.add(artifact.getExecPathString()); } } return result; } private void setupBuildVariables(CppCompileActionBuilder builder, boolean usePic, PathFragment ccRelativeName) { CcToolchainFeatures.Variables.Builder buildVariables = new CcToolchainFeatures.Variables.Builder(); CppModuleMap cppModuleMap = context.getCppModuleMap(); 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()); } 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(context.getIncludeDirs())); buildVariables.addSequenceVariable("quote_include_paths", getSafePathStrings(context.getQuoteIncludeDirs())); buildVariables.addSequenceVariable("system_include_paths", getSafePathStrings(context.getSystemIncludeDirs())); } if (ccRelativeName != null) { cppConfiguration.getFdoSupport().configureCompilation(builder, buildVariables, ruleContext, ccRelativeName, usePic, featureConfiguration, cppConfiguration); } 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(); PathFragment objectDir = CppHelper.getObjDirectory(ruleContext.getLabel()); if (featureConfiguration.isEnabled(CppRuleClasses.HEADER_MODULES)) { Artifact moduleMapArtifact = context.getCppModuleMap().getArtifact(); Label moduleMapLabel = Label.parseAbsoluteUnchecked(context.getCppModuleMap().getName()); PathFragment outputName = getObjectOutputPath(moduleMapArtifact, objectDir); CppCompileActionBuilder builder = initializeCompileAction(moduleMapArtifact, moduleMapLabel); // 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(outputName, result, env, moduleMapArtifact, builder, ".pcm", ".pcm.d", /*addObject=*/false); } for (Pair source : sourceFiles) { Artifact sourceArtifact = source.getFirst(); Label sourceLabel = source.getSecond(); PathFragment outputName = getObjectOutputPath(sourceArtifact, objectDir); CppCompileActionBuilder builder = initializeCompileAction(sourceArtifact, sourceLabel); if (CppFileTypes.CPP_HEADER.matches(source.first.getExecPath())) { createHeaderAction(outputName, result, env, builder); } else { createSourceAction(outputName, result, env, sourceArtifact, builder, ".o", ".d", /*addObject=*/true); } } compilationOutputs = result.build(); return compilationOutputs; } private void createHeaderAction(PathFragment outputName, Builder result, AnalysisEnvironment env, CppCompileActionBuilder builder) { builder .setOutputFile(ruleContext.getRelatedArtifact(outputName, ".h.processed")) .setDotdFile(outputName, ".h.d") // If we generate pic actions, we prefer the header actions to use the pic artifacts. .setPicMode(this.getGeneratePicActions()); setupBuildVariables(builder, this.getGeneratePicActions(), null); semantics.finalizeCompileActionBuilder(ruleContext, builder); CppCompileAction compileAction = builder.build(); env.registerAction(compileAction); Artifact tokenFile = compileAction.getOutputFile(); result.addHeaderTokenFile(tokenFile); } private void createSourceAction(PathFragment outputName, CcCompilationOutputs.Builder result, AnalysisEnvironment env, Artifact sourceArtifact, CppCompileActionBuilder builder, String outputExtension, String dependencyFileExtension, boolean addObject) { 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)); } if (fake) { // For cc_fake_binary, we only create a single fake compile action. It's // not necessary to use -fPIC for negative compilation tests, and using // .pic.o files in cc_fake_binary would break existing uses of // cc_fake_binary. Artifact outputFile = ruleContext.getRelatedArtifact(outputName, outputExtension); PathFragment tempOutputName = FileSystemUtils.replaceExtension(outputFile.getExecPath(), ".temp" + outputExtension); builder .setOutputFile(outputFile) .setDotdFile(outputName, dependencyFileExtension) .setTempOutputFile(tempOutputName); setupBuildVariables(builder, getGeneratePicActions(), ccRelativeName); semantics.finalizeCompileActionBuilder(ruleContext, builder); CppCompileAction action = builder.build(); env.registerAction(action); if (addObject) { result.addObjectFile(action.getOutputFile()); } } else { 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); // 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) { CppCompileActionBuilder picBuilder = copyAsPicBuilder(builder, outputName, outputExtension, dependencyFileExtension); setupBuildVariables(picBuilder, /*usePic=*/true, ccRelativeName); if (maySaveTemps) { result.addTemps( createTempsActions(sourceArtifact, outputName, picBuilder, /*usePic=*/true, ccRelativeName)); } if (isCodeCoverageEnabled()) { picBuilder.setGcnoFile(ruleContext.getRelatedArtifact(outputName, ".pic.gcno")); } semantics.finalizeCompileActionBuilder(ruleContext, picBuilder); CppCompileAction picAction = picBuilder.build(); env.registerAction(picAction); if (addObject) { result.addPicObjectFile(picAction.getOutputFile()); } if (picAction.getDwoFile() != null) { // Host targets don't produce .dwo files. result.addPicDwoFile(picAction.getDwoFile()); } if (cppConfiguration.isLipoContextCollector() && !generateNoPicAction) { result.addLipoScannable(picAction); } } if (generateNoPicAction) { builder .setOutputFile(ruleContext.getRelatedArtifact(outputName, outputExtension)) .setDotdFile(outputName, dependencyFileExtension); // Create non-PIC compile actions setupBuildVariables(builder, /*usePic=*/false, ccRelativeName); if (maySaveTemps) { result.addTemps( createTempsActions(sourceArtifact, outputName, builder, /*usePic=*/false, ccRelativeName)); } if (!cppConfiguration.isLipoOptimization() && isCodeCoverageEnabled()) { builder.setGcnoFile(ruleContext.getRelatedArtifact(outputName, ".gcno")); } semantics.finalizeCompileActionBuilder(ruleContext, builder); CppCompileAction compileAction = builder.build(); env.registerAction(compileAction); Artifact objectFile = compileAction.getOutputFile(); if (addObject) { result.addObjectFile(objectFile); } if (compileAction.getDwoFile() != null) { // Host targets don't produce .dwo files. result.addDwoFile(compileAction.getDwoFile()); } if (cppConfiguration.isLipoContextCollector()) { result.addLipoScannable(compileAction); } } } } /** * 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. */ public CcLinkingOutputs createCcLinkActions(CcCompilationOutputs ccOutputs) { // 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.isStaticLibraryLink(), "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. PathFragment linkedFileName = CppHelper.getLinkedFilename(ruleContext, linkType); CppLinkAction maybePicAction = newLinkActionBuilder(linkedFileName) .addNonLibraryInputs(ccOutputs.getObjectFiles(usePicForBinaries)) .addNonLibraryInputs(ccOutputs.getHeaderTokenFiles()) .setLinkType(linkType) .setLinkStaticness(LinkStaticness.FULLY_STATIC) .build(); env.registerAction(maybePicAction); 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; PathFragment picFileName = CppHelper.getLinkedFilename(ruleContext, picLinkType); CppLinkAction picAction = newLinkActionBuilder(picFileName) .addNonLibraryInputs(ccOutputs.getObjectFiles(true)) .addNonLibraryInputs(ccOutputs.getHeaderTokenFiles()) .setLinkType(picLinkType) .setLinkStaticness(LinkStaticness.FULLY_STATIC) .build(); env.registerAction(picAction); result.addPicStaticLibrary(picAction.getOutputLibrary()); } if (!createDynamicLibrary) { return result.build(); } // Create dynamic library. if (soImplFilename == null) { soImplFilename = CppHelper.getLinkedFilename(ruleContext, LinkTargetType.DYNAMIC_LIBRARY); } List sonameLinkopts = ImmutableList.of(); PathFragment soInterfaceFilename = null; if (cppConfiguration.useInterfaceSharedObjects() && allowInterfaceSharedObjects) { soInterfaceFilename = CppHelper.getLinkedFilename(ruleContext, LinkTargetType.INTERFACE_DYNAMIC_LIBRARY); Artifact dynamicLibrary = env.getDerivedArtifact( soImplFilename, configuration.getBinDirectory()); sonameLinkopts = ImmutableList.of("-Wl,-soname=" + SolibSymlinkAction.getDynamicLibrarySoname(dynamicLibrary.getRootRelativePath(), false)); } // Should we also link in any libraries that this library depends on? // That is required on some systems... CppLinkAction action = newLinkActionBuilder(soImplFilename) .setInterfaceOutputPath(soInterfaceFilename) .addNonLibraryInputs(ccOutputs.getObjectFiles(usePicForSharedLibs)) .addNonLibraryInputs(ccOutputs.getHeaderTokenFiles()) .setLinkType(LinkTargetType.DYNAMIC_LIBRARY) .setLinkStaticness(LinkStaticness.DYNAMIC) .addLinkopts(linkopts) .addLinkopts(sonameLinkopts) .setRuntimeInputs( CppHelper.getToolchain(ruleContext).getDynamicRuntimeLinkMiddleman(), CppHelper.getToolchain(ruleContext).getDynamicRuntimeLinkInputs()) .build(); env.registerAction(action); 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 { LibraryToLink libraryLink = SolibSymlinkAction.getDynamicLibrarySymlink( ruleContext, interfaceLibrary.getArtifact(), false, false, ruleContext.getConfiguration()); result.addDynamicLibrary(libraryLink); LibraryToLink implLibraryLink = SolibSymlinkAction.getDynamicLibrarySymlink( ruleContext, dynamicLibrary.getArtifact(), false, false, ruleContext.getConfiguration()); result.addExecutionDynamicLibrary(implLibraryLink); } return result.build(); } private CppLinkAction.Builder newLinkActionBuilder(PathFragment outputPath) { return new CppLinkAction.Builder(ruleContext, outputPath) .setCrosstoolInputs(CppHelper.getToolchain(ruleContext).getLink()) .addNonLibraryInputs(context.getCompilationPrerequisites()); } /** * Returns the output artifact path relative to the object directory. */ private PathFragment getObjectOutputPath(Artifact source, PathFragment objectDirectory) { return objectDirectory.getRelative(semantics.getEffectiveSourcePath(source)); } /** * 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) { CppCompileActionBuilder builder = new CppCompileActionBuilder( ruleContext, source, label); builder .setContext(context) .addCopts(copts); 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, PathFragment outputName, String outputExtension, String dependencyFileExtension) { CppCompileActionBuilder picBuilder = new CppCompileActionBuilder(builder); picBuilder .setPicMode(true) .setOutputFile(ruleContext.getRelatedArtifact(outputName, ".pic" + outputExtension)) .setDotdFile(outputName, ".pic" + dependencyFileExtension); return picBuilder; } /** * Create the actions for "--save_temps". */ private ImmutableList createTempsActions(Artifact source, PathFragment outputName, CppCompileActionBuilder builder, boolean usePic, 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(); } String iExt = isCFile ? ".i" : ".ii"; String picExt = usePic ? ".pic" : ""; CppCompileActionBuilder dBuilder = new CppCompileActionBuilder(builder); setupBuildVariables(dBuilder, usePic, ccRelativeName); CppCompileActionBuilder sdBuilder = new CppCompileActionBuilder(builder); setupBuildVariables(sdBuilder, usePic, ccRelativeName); dBuilder .setOutputFile(ruleContext.getRelatedArtifact(outputName, picExt + iExt)) .setDotdFile(outputName, picExt + iExt + ".d"); semantics.finalizeCompileActionBuilder(ruleContext, dBuilder); CppCompileAction dAction = dBuilder.build(); ruleContext.registerAction(dAction); sdBuilder .setOutputFile(ruleContext.getRelatedArtifact(outputName, picExt + ".s")) .setDotdFile(outputName, picExt + ".s.d"); 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()) { final RegexFilter filter = configuration.getInstrumentationFilter(); // If rule is matched by the instrumentation filter, enable instrumentation if (filter.isIncluded(ruleContext.getLabel().toString())) { 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", Type.LABEL_LIST)) { for (TransitiveInfoCollection dep : ruleContext.getPrerequisites("deps", Mode.TARGET)) { if (dep.getProvider(CppCompilationContext.class) != null && filter.isIncluded(dep.getLabel().toString())) { return true; } } } } return false; } }