aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/rules/cpp/CppModel.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/rules/cpp/CppModel.java')
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/cpp/CppModel.java707
1 files changed, 707 insertions, 0 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppModel.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppModel.java
new file mode 100644
index 0000000000..44258a5ab1
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppModel.java
@@ -0,0 +1,707 @@
+// 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.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.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.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}.
+ *
+ * <p>This class is not thread-safe, and it should only be used once for each set of source files,
+ * i.e. calling {@link #createCcCompileActions} will throw an Exception if called twice.
+ */
+public final class CppModel {
+ private final CppSemantics semantics;
+ private final RuleContext ruleContext;
+ private final BuildConfiguration configuration;
+ private final CppConfiguration cppConfiguration;
+
+ // compile model
+ private CppCompilationContext context;
+ private final List<Pair<Artifact, Label>> sourceFiles = new ArrayList<>();
+ private final List<String> copts = new ArrayList<>();
+ private final List<PathFragment> additionalIncludes = new ArrayList<>();
+ @Nullable private Pattern nocopts;
+ private boolean fake;
+ private boolean maySaveTemps;
+ private boolean onlySingleOutput;
+ private CcCompilationOutputs compilationOutputs;
+ private boolean enableLayeringCheck;
+ private boolean compileHeaderModules;
+
+ // link model
+ private final List<String> 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;
+ }
+
+ /**
+ * If set, use compiler flags to enable compiler based layering checks.
+ */
+ public CppModel setEnableLayeringCheck(boolean enableLayeringCheck) {
+ this.enableLayeringCheck = enableLayeringCheck;
+ return this;
+ }
+
+ /**
+ * If set, add actions that compile header modules to the build.
+ * See http://clang.llvm.org/docs/Modules.html for more information.
+ */
+ public CppModel setCompileHeaderModules(boolean compileHeaderModules) {
+ this.compileHeaderModules = compileHeaderModules;
+ 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<Artifact> 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<Pair<Artifact, Label>> sources) {
+ Iterables.addAll(this.sourceFiles, sources);
+ return this;
+ }
+
+ /**
+ * Adds the given copts.
+ */
+ public CppModel addCopts(Collection<String> 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<PathFragment> 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<String> 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.
+ */
+ public boolean getGeneratePicActions() {
+ return CppHelper.usePic(ruleContext, false);
+ }
+
+ /**
+ * @return whether this target needs to generate non-pic actions.
+ */
+ public 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 compileHeaderModules && !fake && getGeneratePicActions();
+ }
+
+ /**
+ * @return whether this target needs to generate a non-pic header module.
+ */
+ public boolean getGeratesNoPicHeaderModule() {
+ return compileHeaderModules && !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.setEnableLayeringCheck(enableLayeringCheck);
+ builder.setCompileHeaderModules(compileHeaderModules);
+ builder.setExtraSystemIncludePrefixes(additionalIncludes);
+ builder.setFdoBuildStamp(CppHelper.getFdoBuildStamp(cppConfiguration));
+ builder.setFeatureConfiguration(featureConfiguration);
+ return builder;
+ }
+
+ /**
+ * 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 (compileHeaderModules) {
+ 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");
+ }
+
+ for (Pair<Artifact, Label> 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");
+ }
+ }
+
+ 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", ruleContext);
+ 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) {
+ PathFragment ccRelativeName = semantics.getEffectiveSourcePath(sourceArtifact);
+ LipoContextProvider lipoProvider = null;
+ if (cppConfiguration.isLipoOptimization()) {
+ // TODO(bazel-team): we shouldn't be needing this, merging context with the binary
+ // is a superset of necessary information.
+ 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, ".d", ruleContext)
+ .setTempOutputFile(tempOutputName);
+ semantics.finalizeCompileActionBuilder(ruleContext, builder);
+ CppCompileAction action = builder.build();
+ env.registerAction(action);
+ 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);
+ cppConfiguration.getFdoSupport().configureCompilation(picBuilder, ruleContext, env,
+ ruleContext.getLabel(), ccRelativeName, nocopts, /*usePic=*/true,
+ lipoProvider);
+
+ if (maySaveTemps) {
+ result.addTemps(
+ createTempsActions(sourceArtifact, outputName, picBuilder, /*usePic=*/true));
+ }
+
+ if (isCodeCoverageEnabled()) {
+ picBuilder.setGcnoFile(ruleContext.getRelatedArtifact(outputName, ".pic.gcno"));
+ }
+
+ semantics.finalizeCompileActionBuilder(ruleContext, picBuilder);
+ CppCompileAction picAction = picBuilder.build();
+ env.registerAction(picAction);
+ 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, ".d", ruleContext);
+ // Create non-PIC compile actions
+ cppConfiguration.getFdoSupport().configureCompilation(builder, ruleContext, env,
+ ruleContext.getLabel(), ccRelativeName, nocopts, /*usePic=*/false,
+ lipoProvider);
+
+ if (maySaveTemps) {
+ result.addTemps(
+ createTempsActions(sourceArtifact, outputName, builder, /*usePic=*/false));
+ }
+
+ 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();
+ 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.
+ *
+ * <p>For dynamic libraries, this method can additionally create an interface shared library that
+ * can be used for linking, but doesn't contain any executable code. This increases the number of
+ * cache hits for link actions. Call {@link #setAllowInterfaceSharedObjects(boolean)} to enable
+ * this behavior.
+ */
+ 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<String> 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) {
+ CppCompileActionBuilder picBuilder = new CppCompileActionBuilder(builder);
+ picBuilder.addCopt("-fPIC")
+ .setOutputFile(ruleContext.getRelatedArtifact(outputName, ".pic" + outputExtension))
+ .setDotdFile(outputName, ".pic.d", ruleContext);
+ return picBuilder;
+ }
+
+ /**
+ * Create the actions for "--save_temps".
+ */
+ private ImmutableList<Artifact> createTempsActions(Artifact source, PathFragment outputName,
+ CppCompileActionBuilder builder, boolean usePic) {
+ 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);
+ CppCompileActionBuilder sdBuilder = new CppCompileActionBuilder(builder);
+
+ dBuilder
+ .setOutputFile(ruleContext.getRelatedArtifact(outputName, picExt + iExt))
+ .setDotdFile(outputName, picExt + iExt + ".d", ruleContext);
+ semantics.finalizeCompileActionBuilder(ruleContext, dBuilder);
+ CppCompileAction dAction = dBuilder.build();
+ ruleContext.registerAction(dAction);
+
+ sdBuilder
+ .setOutputFile(ruleContext.getRelatedArtifact(outputName, picExt + ".s"))
+ .setDotdFile(outputName, picExt + ".s.d", ruleContext);
+ 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;
+ }
+}