aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/rules
diff options
context:
space:
mode:
authorGravatar Cal Peyser <cpeyser@google.com>2016-07-12 18:22:13 +0000
committerGravatar Kristina Chodorow <kchodorow@google.com>2016-07-13 11:15:40 +0000
commit5234eed84d845cda522256ad9674ec0a7076f7be (patch)
tree6d8d276cc0146e02256aa9ce2b10afa56f3fd2b8 /src/main/java/com/google/devtools/build/lib/rules
parentbc08ee7963816bbd361cc3043dec54f4e58a4bdb (diff)
Extract CppLinkAction.Builder to its own top level class.
-- MOS_MIGRATED_REVID=127221256
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/rules')
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/cpp/CcBinary.java25
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/cpp/CppLinkAction.java890
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/cpp/CppLinkActionBuilder.java939
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/cpp/CppModel.java6
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/nativedeps/NativeDepsHelper.java5
5 files changed, 1056 insertions, 809 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcBinary.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcBinary.java
index 78bc18c9f5..f83158feb5 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcBinary.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcBinary.java
@@ -192,7 +192,7 @@ public abstract class CcBinary implements RuleConfiguredTargetFactory {
}
Artifact binary = ruleContext.getPackageRelativeArtifact(
binaryPath, ruleContext.getConfiguration().getBinDirectory());
- CppLinkAction.Builder linkActionBuilder =
+ CppLinkActionBuilder linkActionBuilder =
determineLinkerArguments(
ruleContext,
common,
@@ -380,18 +380,23 @@ public abstract class CcBinary implements RuleConfiguredTargetFactory {
}
/**
- * Given 'temps', traverse this target and its dependencies and collect up all
- * the object files, libraries, linker options, linkstamps attributes and linker scripts.
+ * Given 'temps', traverse this target and its dependencies and collect up all the object files,
+ * libraries, linker options, linkstamps attributes and linker scripts.
*/
- private static CppLinkAction.Builder determineLinkerArguments(RuleContext context,
- CcCommon common, PrecompiledFiles precompiledFiles,
+ private static CppLinkActionBuilder determineLinkerArguments(
+ RuleContext context,
+ CcCommon common,
+ PrecompiledFiles precompiledFiles,
CcCompilationOutputs compilationOutputs,
ImmutableSet<Artifact> compilationPrerequisites,
- boolean fake, Artifact binary,
- LinkStaticness linkStaticness, List<String> linkopts) {
- CppLinkAction.Builder builder = new CppLinkAction.Builder(context, binary)
- .setCrosstoolInputs(CppHelper.getToolchain(context).getLink())
- .addNonLibraryInputs(compilationPrerequisites);
+ boolean fake,
+ Artifact binary,
+ LinkStaticness linkStaticness,
+ List<String> linkopts) {
+ CppLinkActionBuilder builder =
+ new CppLinkActionBuilder(context, binary)
+ .setCrosstoolInputs(CppHelper.getToolchain(context).getLink())
+ .addNonLibraryInputs(compilationPrerequisites);
// Determine the object files to link in.
boolean usePic = CppHelper.usePic(context, !isLinkShared(context));
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppLinkAction.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppLinkAction.java
index 6d240bbddc..5a7d0717eb 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppLinkAction.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppLinkAction.java
@@ -24,53 +24,38 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.devtools.build.lib.actions.AbstractAction;
-import com.google.devtools.build.lib.actions.Action;
import com.google.devtools.build.lib.actions.ActionExecutionContext;
import com.google.devtools.build.lib.actions.ActionExecutionException;
import com.google.devtools.build.lib.actions.ActionOwner;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.ExecException;
import com.google.devtools.build.lib.actions.Executor;
-import com.google.devtools.build.lib.actions.ParameterFile;
import com.google.devtools.build.lib.actions.ResourceSet;
import com.google.devtools.build.lib.actions.extra.CppLinkInfo;
import com.google.devtools.build.lib.actions.extra.ExtraActionInfo;
-import com.google.devtools.build.lib.analysis.AnalysisEnvironment;
import com.google.devtools.build.lib.analysis.RuleContext;
import com.google.devtools.build.lib.analysis.TransitiveInfoProvider;
import com.google.devtools.build.lib.analysis.actions.ExecutionInfoSpecifier;
-import com.google.devtools.build.lib.analysis.actions.ParameterFileWriteAction;
import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
-import com.google.devtools.build.lib.collect.CollectionUtils;
-import com.google.devtools.build.lib.collect.ImmutableIterable;
-import com.google.devtools.build.lib.collect.IterablesChain;
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
-import com.google.devtools.build.lib.collect.nestedset.Order;
import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadCompatible;
import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
-import com.google.devtools.build.lib.packages.RuleErrorConsumer;
-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.util.Fingerprint;
import com.google.devtools.build.lib.util.OS;
-import com.google.devtools.build.lib.util.Preconditions;
import com.google.devtools.build.lib.util.ShellEscaper;
import com.google.devtools.build.lib.vfs.FileSystemUtils;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.PathFragment;
import java.io.IOException;
-import java.util.ArrayList;
import java.util.Collection;
-import java.util.HashMap;
-import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import javax.annotation.Nullable;
@@ -144,13 +129,13 @@ public final class CppLinkAction extends AbstractAction implements ExecutionInfo
ResourceSet.createWithRamCpuIo(1024, 0.3, 0.2);
/**
- * Use {@link Builder} to create instances of this class. Also see there for
- * the documentation of all parameters.
+ * Use {@link CppLinkActionBuilder} to create instances of this class. Also see there for the
+ * documentation of all parameters.
*
- * <p>This constructor is intentionally private and is only to be called from
- * {@link Builder#build()}.
+ * <p>This constructor is intentionally private and is only to be called from {@link
+ * CppLinkActionBuilder#build()}.
*/
- private CppLinkAction(
+ CppLinkAction(
ActionOwner owner,
Iterable<Artifact> inputs,
ImmutableList<Artifact> outputs,
@@ -176,7 +161,7 @@ public final class CppLinkAction extends AbstractAction implements ExecutionInfo
this.executionRequirements = executionRequirements;
}
- private static Iterable<LinkerInput> filterLinkerInputs(Iterable<LinkerInput> inputs) {
+ static Iterable<LinkerInput> filterLinkerInputs(Iterable<LinkerInput> inputs) {
return Iterables.filter(inputs, new Predicate<LinkerInput>() {
@Override
public boolean apply(LinkerInput input) {
@@ -185,7 +170,7 @@ public final class CppLinkAction extends AbstractAction implements ExecutionInfo
});
}
- private static Iterable<Artifact> filterLinkerInputArtifacts(Iterable<Artifact> inputs) {
+ static Iterable<Artifact> filterLinkerInputArtifacts(Iterable<Artifact> inputs) {
return Iterables.filter(inputs, new Predicate<Artifact>() {
@Override
public boolean apply(Artifact input) {
@@ -528,824 +513,141 @@ public final class CppLinkAction extends AbstractAction implements ExecutionInfo
}
/**
- * Builder class to construct {@link CppLinkAction}s.
+ * TransitiveInfoProvider for ELF link actions.
*/
- public static class Builder {
- // Builder-only
- // Null when invoked from tests (e.g. via createTestBuilder).
- @Nullable private final RuleContext ruleContext;
- private final AnalysisEnvironment analysisEnvironment;
- private final Artifact output;
-
- // can be null for CppLinkAction.createTestBuilder()
- @Nullable private final CcToolchainProvider toolchain;
- private Artifact interfaceOutput;
- private Artifact symbolCounts;
- private PathFragment runtimeSolibDir;
- protected final BuildConfiguration configuration;
- private final CppConfiguration cppConfiguration;
- private FeatureConfiguration featureConfiguration;
- private CcToolchainFeatures.Variables buildVariables =
- new CcToolchainFeatures.Variables.Builder().build();
-
- // Morally equivalent with {@link Context}, except these are mutable.
- // Keep these in sync with {@link Context}.
- private final Set<LinkerInput> nonLibraries = new LinkedHashSet<>();
- private final NestedSetBuilder<LibraryToLink> libraries = NestedSetBuilder.linkOrder();
- private NestedSet<Artifact> crosstoolInputs = NestedSetBuilder.emptySet(Order.STABLE_ORDER);
- private Artifact runtimeMiddleman;
- private NestedSet<Artifact> runtimeInputs = NestedSetBuilder.emptySet(Order.STABLE_ORDER);
- private final NestedSetBuilder<Artifact> compilationInputs = NestedSetBuilder.stableOrder();
- private final Set<Artifact> linkstamps = new LinkedHashSet<>();
- private List<String> linkstampOptions = new ArrayList<>();
- private final List<String> linkopts = new ArrayList<>();
- private LinkTargetType linkType = LinkTargetType.STATIC_LIBRARY;
- private LinkStaticness linkStaticness = LinkStaticness.FULLY_STATIC;
- private List<Artifact> ltoBitcodeFiles = new ArrayList<>();
-
- private boolean fake;
- private boolean isNativeDeps;
- private boolean useTestOnlyFlags;
- private boolean wholeArchive;
- private LinkArtifactFactory linkArtifactFactory = DEFAULT_ARTIFACT_FACTORY;
-
- private boolean isLTOIndexing = false;
- private Iterable<LTOBackendArtifacts> allLTOArtifacts = null;
-
- /**
- * Creates a builder that builds {@link CppLinkAction} instances.
- *
- * @param ruleContext the rule that owns the action
- * @param output the output artifact
- */
- public Builder(RuleContext ruleContext, Artifact output) {
- this(ruleContext, output, ruleContext.getConfiguration(),
- ruleContext.getAnalysisEnvironment(), CppHelper.getToolchain(ruleContext));
- }
-
- /**
- * Creates a builder that builds {@link CppLinkAction} instances.
- *
- * @param ruleContext the rule that owns the action
- * @param output the output artifact
- */
- public Builder(RuleContext ruleContext, Artifact output,
- BuildConfiguration configuration, CcToolchainProvider toolchain) {
- this(ruleContext, output, configuration,
- ruleContext.getAnalysisEnvironment(), toolchain);
- }
-
- /**
- * Creates a builder that builds {@link CppLinkAction}s.
- *
- * @param ruleContext the rule that owns the action
- * @param output the output artifact
- * @param configuration the configuration used to determine the tool chain
- * and the default link options
- */
- private Builder(@Nullable RuleContext ruleContext, Artifact output,
- BuildConfiguration configuration, AnalysisEnvironment analysisEnvironment,
- CcToolchainProvider toolchain) {
- this.ruleContext = ruleContext;
- this.analysisEnvironment = Preconditions.checkNotNull(analysisEnvironment);
- this.output = Preconditions.checkNotNull(output);
- this.configuration = Preconditions.checkNotNull(configuration);
- this.cppConfiguration = configuration.getFragment(CppConfiguration.class);
- this.toolchain = toolchain;
- if (cppConfiguration.supportsEmbeddedRuntimes() && toolchain != null) {
- runtimeSolibDir = toolchain.getDynamicRuntimeSolibDir();
- }
- }
-
- /**
- * Given a Context, creates a Builder that builds {@link CppLinkAction}s.
+ @Immutable @ThreadSafe
+ public static final class Context implements TransitiveInfoProvider {
+ // Morally equivalent with {@link Builder}, except these are immutable.
+ // Keep these in sync with {@link Builder}.
+ final ImmutableSet<LinkerInput> nonLibraries;
+ final NestedSet<LibraryToLink> libraries;
+ final NestedSet<Artifact> crosstoolInputs;
+ final Artifact runtimeMiddleman;
+ final NestedSet<Artifact> runtimeInputs;
+ final NestedSet<Artifact> compilationInputs;
+ final ImmutableSet<Artifact> linkstamps;
+ final ImmutableList<String> linkopts;
+ final LinkTargetType linkType;
+ final LinkStaticness linkStaticness;
+ final boolean fake;
+ final boolean isNativeDeps;
+ final boolean useTestOnlyFlags;
+
+ /**
+ * Given a {@link CppLinkActionBuilder}, creates a {@code Context} to pass to another target.
* Note well: Keep the Builder->Context and Context->Builder transforms consistent!
- * @param ruleContext the rule that owns the action
- * @param output the output artifact
- * @param linkContext an immutable CppLinkAction.Context from the original builder
- */
- public Builder(RuleContext ruleContext, Artifact output, Context linkContext,
- BuildConfiguration configuration) {
- // These Builder-only fields get set in the constructor:
- // ruleContext, analysisEnvironment, outputPath, configuration, runtimeSolibDir
- this(ruleContext, output, configuration, ruleContext.getAnalysisEnvironment(),
- CppHelper.getToolchain(ruleContext));
- Preconditions.checkNotNull(linkContext);
-
- // All linkContext fields should be transferred to this Builder.
- this.nonLibraries.addAll(linkContext.nonLibraries);
- this.libraries.addTransitive(linkContext.libraries);
- this.crosstoolInputs = linkContext.crosstoolInputs;
- this.runtimeMiddleman = linkContext.runtimeMiddleman;
- this.runtimeInputs = linkContext.runtimeInputs;
- this.compilationInputs.addTransitive(linkContext.compilationInputs);
- this.linkstamps.addAll(linkContext.linkstamps);
- this.linkopts.addAll(linkContext.linkopts);
- this.linkType = linkContext.linkType;
- this.linkStaticness = linkContext.linkStaticness;
- this.fake = linkContext.fake;
- this.isNativeDeps = linkContext.isNativeDeps;
- this.useTestOnlyFlags = linkContext.useTestOnlyFlags;
- }
-
- /**
- * Returns the action name for purposes of querying the crosstool.
- */
- private String getActionName() {
- return linkType.getActionName();
- }
-
- public CppLinkAction.Builder setLinkArtifactFactory(LinkArtifactFactory linkArtifactFactory) {
- this.linkArtifactFactory = linkArtifactFactory;
- return this;
- }
-
- private Iterable<LTOBackendArtifacts> createLTOArtifacts(
- PathFragment ltoOutputRootPrefix, NestedSet<LibraryToLink> uniqueLibraries) {
- Set<Artifact> compiled = new LinkedHashSet<>();
- for (LibraryToLink lib : uniqueLibraries) {
- Iterables.addAll(compiled, lib.getLTOBitcodeFiles());
- }
-
- // This flattens the set of object files, so for M binaries and N .o files,
- // this is O(M*N). If we had a nested set of .o files, we could have O(M + N) instead.
- Map<PathFragment, Artifact> allBitcode = new HashMap<>();
- for (LibraryToLink lib : uniqueLibraries) {
- if (!lib.containsObjectFiles()) {
- continue;
- }
- for (Artifact a : lib.getObjectFiles()) {
- if (compiled.contains(a)) {
- allBitcode.put(a.getExecPath(), a);
- }
- }
- }
- for (LinkerInput input : nonLibraries) {
- // This relies on file naming conventions. It would be less fragile to have a dedicated
- // field for non-library .o files.
- if (CppFileTypes.OBJECT_FILE.matches(input.getArtifact().getExecPath())
- || CppFileTypes.PIC_OBJECT_FILE.matches(input.getArtifact().getExecPath())) {
- if (this.ltoBitcodeFiles.contains(input.getArtifact())) {
- allBitcode.put(input.getArtifact().getExecPath(), input.getArtifact());
- }
- }
- }
-
- ImmutableList.Builder<LTOBackendArtifacts> ltoOutputs = ImmutableList.builder();
- for (Artifact a : allBitcode.values()) {
- LTOBackendArtifacts ltoArtifacts = new LTOBackendArtifacts(
- ltoOutputRootPrefix, a, allBitcode, ruleContext, configuration, linkArtifactFactory);
- ltoOutputs.add(ltoArtifacts);
- }
- return ltoOutputs.build();
- }
-
- @VisibleForTesting
- boolean canSplitCommandLine() {
- if (toolchain == null || !toolchain.supportsParamFiles()) {
- return false;
- }
-
- switch (linkType) {
- // We currently can't split dynamic library links if they have interface outputs. That was
- // probably an unintended side effect of the change that introduced interface outputs.
- case DYNAMIC_LIBRARY:
- return interfaceOutput == null;
- case EXECUTABLE:
- case STATIC_LIBRARY:
- case PIC_STATIC_LIBRARY:
- case ALWAYS_LINK_STATIC_LIBRARY:
- case ALWAYS_LINK_PIC_STATIC_LIBRARY:
- return true;
-
- default:
- return false;
- }
- }
-
- /**
- * Builds the Action as configured and returns it.
- */
- public CppLinkAction build() {
- if (interfaceOutput != null && (fake || linkType != LinkTargetType.DYNAMIC_LIBRARY)) {
- throw new RuntimeException("Interface output can only be used "
- + "with non-fake DYNAMIC_LIBRARY targets");
- }
-
- final ImmutableList<Artifact> buildInfoHeaderArtifacts = !linkstamps.isEmpty()
- ? analysisEnvironment.getBuildInfo(ruleContext, CppBuildInfo.KEY, configuration)
- : ImmutableList.<Artifact>of();
-
- boolean needWholeArchive = wholeArchive || needWholeArchive(
- linkStaticness, linkType, linkopts, isNativeDeps, cppConfiguration);
-
- NestedSet<LibraryToLink> uniqueLibraries = libraries.build();
- final Iterable<Artifact> filteredNonLibraryArtifacts =
- filterLinkerInputArtifacts(LinkerInputs.toLibraryArtifacts(nonLibraries));
-
- final Iterable<LinkerInput> linkerInputs = IterablesChain.<LinkerInput>builder()
- .add(ImmutableList.copyOf(filterLinkerInputs(nonLibraries)))
- .add(ImmutableIterable.from(Link.mergeInputsCmdLine(
- uniqueLibraries, needWholeArchive, cppConfiguration.archiveType())))
- .build();
-
- // ruleContext can only be null during testing. This is kind of ugly.
- final ImmutableSet<String> features = (ruleContext == null)
- ? ImmutableSet.<String>of()
- : ruleContext.getFeatures();
-
- final LibraryToLink outputLibrary =
- LinkerInputs.newInputLibrary(output, filteredNonLibraryArtifacts, this.ltoBitcodeFiles);
- final LibraryToLink interfaceOutputLibrary =
- (interfaceOutput == null)
- ? null
- : LinkerInputs.newInputLibrary(
- interfaceOutput, filteredNonLibraryArtifacts, this.ltoBitcodeFiles);
-
- final ImmutableMap<Artifact, Artifact> linkstampMap =
- mapLinkstampsToOutputs(linkstamps, ruleContext, configuration, output,
- linkArtifactFactory);
-
- PathFragment ltoOutputRootPrefix = null;
- if (isLTOIndexing && allLTOArtifacts == null) {
- ltoOutputRootPrefix =
- FileSystemUtils.appendExtension(
- outputLibrary.getArtifact().getRootRelativePath(), ".lto");
- allLTOArtifacts = createLTOArtifacts(ltoOutputRootPrefix, uniqueLibraries);
- }
-
- final ImmutableList<Artifact> actionOutputs;
- if (isLTOIndexing) {
- ImmutableList.Builder<Artifact> builder = ImmutableList.builder();
- for (LTOBackendArtifacts ltoA : allLTOArtifacts) {
- ltoA.addIndexingOutputs(builder);
- }
- actionOutputs = builder.build();
- } else {
- actionOutputs =
- constructOutputs(
- outputLibrary.getArtifact(),
- linkstampMap.values(),
- interfaceOutputLibrary == null ? null : interfaceOutputLibrary.getArtifact(),
- symbolCounts);
- }
-
- PathFragment paramRootPath =
- ParameterFile.derivePath(
- outputLibrary.getArtifact().getRootRelativePath(), (isLTOIndexing) ? "lto" : "2");
-
- @Nullable
- final Artifact paramFile =
- canSplitCommandLine()
- ? linkArtifactFactory.create(ruleContext, configuration, paramRootPath)
- : null;
-
- LinkCommandLine.Builder linkCommandLineBuilder =
- new LinkCommandLine.Builder(configuration, getOwner(), ruleContext)
- .setActionName(getActionName())
- .setLinkerInputs(linkerInputs)
- .setRuntimeInputs(
- ImmutableList.copyOf(LinkerInputs.simpleLinkerInputs(runtimeInputs)))
- .setLinkTargetType(linkType)
- .setLinkStaticness(linkStaticness)
- .setFeatures(features)
- .setRuntimeSolibDir(linkType.isStaticLibraryLink() ? null : runtimeSolibDir)
- .setNativeDeps(isNativeDeps)
- .setUseTestOnlyFlags(useTestOnlyFlags)
- .setNeedWholeArchive(needWholeArchive)
- .setParamFile(paramFile)
- .setAllLTOArtifacts(isLTOIndexing ? null : allLTOArtifacts)
- .setToolchain(toolchain)
- .setFeatureConfiguration(featureConfiguration);
-
- if (!isLTOIndexing) {
- linkCommandLineBuilder
- .setOutput(outputLibrary.getArtifact())
- .setInterfaceOutput(interfaceOutput)
- .setSymbolCountsOutput(symbolCounts)
- .setBuildInfoHeaderArtifacts(buildInfoHeaderArtifacts)
- .setInterfaceSoBuilder(getInterfaceSoBuilder())
- .setLinkstamps(linkstampMap)
- .setLinkopts(ImmutableList.copyOf(linkopts))
- .addLinkstampCompileOptions(linkstampOptions);
- } else {
- // TODO(bazel-team): once the LLVM compiler patches have been finalized, this should
- // be converted to a crosstool feature configuration instead.
- List<String> opts = new ArrayList<>(linkopts);
- opts.add("-flto=thin");
- opts.add("-Wl,-plugin-opt,thinlto-index-only");
- opts.add("-Wl,-plugin-opt,thinlto-emit-imports-files");
- opts.add(
- "-Wl,-plugin-opt,thinlto-prefix-replace="
- + configuration.getBinDirectory().getExecPathString()
- + ";"
- + configuration
- .getBinDirectory()
- .getExecPath()
- .getRelative(ltoOutputRootPrefix)
- .toString());
- linkCommandLineBuilder.setLinkopts(ImmutableList.copyOf(opts));
- }
-
- LinkCommandLine linkCommandLine = linkCommandLineBuilder.build();
-
- // Compute the set of inputs - we only need stable order here.
- NestedSetBuilder<Artifact> dependencyInputsBuilder = NestedSetBuilder.stableOrder();
- dependencyInputsBuilder.addTransitive(crosstoolInputs);
- if (runtimeMiddleman != null) {
- dependencyInputsBuilder.add(runtimeMiddleman);
- }
- if (!isLTOIndexing) {
- dependencyInputsBuilder.addAll(buildInfoHeaderArtifacts);
- dependencyInputsBuilder.addAll(linkstamps);
- dependencyInputsBuilder.addTransitive(compilationInputs.build());
- }
-
- // For backwards compatibility, and for tests, we permit the link action to be
- // instantiated without a feature configuration. In this case, an empty feature
- // configuration is used.
- if (featureConfiguration == null) {
- this.featureConfiguration = new FeatureConfiguration();
- }
-
- Iterable<Artifact> expandedInputs =
- LinkerInputs.toLibraryArtifacts(
- Link.mergeInputsDependencies(
- uniqueLibraries, needWholeArchive, cppConfiguration.archiveType()));
- Iterable<Artifact> expandedNonLibraryInputs = LinkerInputs.toLibraryArtifacts(nonLibraries);
- if (!isLTOIndexing && allLTOArtifacts != null) {
- // We are doing LTO, and this is the real link, so substitute
- // the LTO bitcode files with the real object files they were translated into.
- Map<Artifact, Artifact> ltoMapping = new HashMap<>();
- for (LTOBackendArtifacts a : allLTOArtifacts) {
- ltoMapping.put(a.getBitcodeFile(), a.getObjectFile());
- }
-
- // Handle libraries.
- List<Artifact> renamedInputs = new ArrayList<>();
- for (Artifact a : expandedInputs) {
- Artifact renamed = ltoMapping.get(a);
- renamedInputs.add(renamed == null ? a : renamed);
- }
- expandedInputs = renamedInputs;
-
- // Handle non-libraries.
- List<Artifact> renamedNonLibraryInputs = new ArrayList<>();
- for (Artifact a : expandedNonLibraryInputs) {
- Artifact renamed = ltoMapping.get(a);
- renamedNonLibraryInputs.add(renamed == null ? a : renamed);
- }
- expandedNonLibraryInputs = renamedNonLibraryInputs;
- } else if (isLTOIndexing && allLTOArtifacts != null) {
- for (LTOBackendArtifacts a : allLTOArtifacts) {
- List<String> argv = new ArrayList<>();
- argv.addAll(cppConfiguration.getLinkOptions());
- argv.addAll(featureConfiguration.getCommandLine(getActionName(), buildVariables));
- argv.addAll(cppConfiguration.getCompilerOptions(features));
- a.setCommandLine(argv);
- }
- }
-
- // getPrimaryInput returns the first element, and that is a public interface - therefore the
- // order here is important.
- IterablesChain.Builder<Artifact> inputsBuilder =
- IterablesChain.<Artifact>builder()
- .add(ImmutableList.copyOf(expandedNonLibraryInputs))
- .add(dependencyInputsBuilder.build())
- .add(ImmutableIterable.from(expandedInputs));
-
- if (linkCommandLine.getParamFile() != null) {
- inputsBuilder.add(ImmutableList.of(linkCommandLine.getParamFile()));
- Action parameterFileWriteAction =
- new ParameterFileWriteAction(
- getOwner(),
- paramFile,
- linkCommandLine.paramCmdLine(),
- ParameterFile.ParameterFileType.UNQUOTED,
- ISO_8859_1);
- analysisEnvironment.registerAction(parameterFileWriteAction);
- }
-
-
- Map<String, String> toolchainEnv =
- featureConfiguration.getEnvironmentVariables(getActionName(), buildVariables);
-
- // If the crosstool uses action_configs to configure cc compilation, collect execution info
- // from there, otherwise, use no execution info.
- // TODO(b/27903698): Assert that the crosstool has an action_config for this action.
- ImmutableSet.Builder<String> executionRequirements = ImmutableSet.<String>builder();
- if (featureConfiguration.actionIsConfigured(getActionName())) {
- executionRequirements.addAll(
- featureConfiguration.getToolForAction(getActionName()).getExecutionRequirements());
- }
-
- return new CppLinkAction(
- getOwner(),
- inputsBuilder.deduplicate().build(),
- actionOutputs,
- cppConfiguration,
- outputLibrary,
- interfaceOutputLibrary,
- fake,
- isLTOIndexing,
- allLTOArtifacts,
- linkCommandLine,
- toolchainEnv,
- executionRequirements.build());
- }
-
- /**
- * The default heuristic on whether we need to use whole-archive for the link.
- */
- private static boolean needWholeArchive(LinkStaticness staticness,
- LinkTargetType type, Collection<String> linkopts, boolean isNativeDeps,
- CppConfiguration cppConfig) {
- boolean fullyStatic = (staticness == LinkStaticness.FULLY_STATIC);
- boolean mostlyStatic = (staticness == LinkStaticness.MOSTLY_STATIC);
- boolean sharedLinkopts = type == LinkTargetType.DYNAMIC_LIBRARY
- || linkopts.contains("-shared")
- || cppConfig.getLinkOptions().contains("-shared");
- return (isNativeDeps || cppConfig.legacyWholeArchive())
- && (fullyStatic || mostlyStatic)
- && sharedLinkopts;
- }
-
- private static ImmutableList<Artifact> constructOutputs(Artifact primaryOutput,
- Collection<Artifact> outputList, Artifact... outputs) {
- return new ImmutableList.Builder<Artifact>()
- .add(primaryOutput)
- .addAll(outputList)
- .addAll(CollectionUtils.asListWithoutNulls(outputs))
- .build();
- }
-
- /**
- * Translates a collection of linkstamp source files to an immutable
- * mapping from source files to object files. In other words, given a
- * set of source files, this method determines the output path to which
- * each file should be compiled.
*
- * @param linkstamps collection of linkstamp source files
- * @param ruleContext the rule for which this link is being performed
- * @param outputBinary the binary output path for this link
- * @return an immutable map that pairs each source file with the
- * corresponding object file that should be fed into the link
+ * @param builder a mutable {@link CppLinkActionBuilder} to clone from
*/
- public static ImmutableMap<Artifact, Artifact> mapLinkstampsToOutputs(
- Collection<Artifact> linkstamps, RuleContext ruleContext, BuildConfiguration configuration,
- Artifact outputBinary, LinkArtifactFactory linkArtifactFactory) {
- ImmutableMap.Builder<Artifact, Artifact> mapBuilder = ImmutableMap.builder();
-
- PathFragment outputBinaryPath = outputBinary.getRootRelativePath();
- PathFragment stampOutputDirectory = outputBinaryPath.getParentDirectory().
- getRelative("_objs").getRelative(outputBinaryPath.getBaseName());
-
- for (Artifact linkstamp : linkstamps) {
- PathFragment stampOutputPath = stampOutputDirectory.getRelative(
- FileSystemUtils.replaceExtension(linkstamp.getRootRelativePath(), ".o"));
- mapBuilder.put(linkstamp,
- // Note that link stamp actions can be shared between link actions that output shared
- // native dep libraries.
- linkArtifactFactory.create(ruleContext, configuration, stampOutputPath));
- }
- return mapBuilder.build(); }
-
- protected ActionOwner getOwner() {
- return ruleContext.getActionOwner();
- }
-
- protected Artifact getInterfaceSoBuilder() {
- return analysisEnvironment.getEmbeddedToolArtifact(CppRuleClasses.BUILD_INTERFACE_SO);
+ public Context(CppLinkActionBuilder builder) {
+ this.nonLibraries = ImmutableSet.copyOf(builder.getNonLibraries());
+ this.libraries = NestedSetBuilder.<LibraryToLink>linkOrder()
+ .addTransitive(builder.getLibraries().build()).build();
+ this.crosstoolInputs =
+ NestedSetBuilder.<Artifact>stableOrder().addTransitive(builder.getCrosstoolInputs()).build();
+ this.runtimeMiddleman = builder.getRuntimeMiddleman();
+ this.runtimeInputs =
+ NestedSetBuilder.<Artifact>stableOrder().addTransitive(builder.getRuntimeInputs()).build();
+ this.compilationInputs = NestedSetBuilder.<Artifact>stableOrder()
+ .addTransitive(builder.getCompilationInputs().build()).build();
+ this.linkstamps = ImmutableSet.copyOf(builder.getLinkstamps());
+ this.linkopts = ImmutableList.copyOf(builder.getLinkopts());
+ this.linkType = builder.getLinkType();
+ this.linkStaticness = builder.getLinkStaticness();
+ this.fake = builder.isFake();
+ this.isNativeDeps = builder.isNativeDeps();
+ this.useTestOnlyFlags = builder.useTestOnlyFlags();
}
/**
- * Set the crosstool inputs required for the action.
+ * Returns linker inputs that are not libraries.
*/
- public Builder setCrosstoolInputs(NestedSet<Artifact> inputs) {
- this.crosstoolInputs = inputs;
- return this;
+ public ImmutableSet<LinkerInput> getNonLibraries() {
+ return this.nonLibraries;
}
/**
- * Sets the feature configuration for the action.
- */
- public Builder setFeatureConfiguration(FeatureConfiguration featureConfiguration) {
- this.featureConfiguration = featureConfiguration;
- return this;
- }
-
- /**
- * Sets the build variables that will be used to template the crosstool.
- */
- public Builder setBuildVariables(CcToolchainFeatures.Variables buildVariables) {
- this.buildVariables = buildVariables;
- return this;
- }
-
- /**
- * This is the LTO indexing step, rather than the real link.
- *
- * <p>When using this, build() will store allLTOArtifacts as a side-effect so the next build()
- * call can emit the real link. Do not call addInput() between the two build() calls.
- *
- */
- public Builder setLTOIndexing(boolean ltoIndexing) {
- this.isLTOIndexing = ltoIndexing;
- return this;
- }
-
- /**
- * Sets the C++ runtime library inputs for the action.
+ * Returns libraries that are to be inputs to the linker.
*/
- public Builder setRuntimeInputs(Artifact middleman, NestedSet<Artifact> inputs) {
- Preconditions.checkArgument((middleman == null) == inputs.isEmpty());
- this.runtimeMiddleman = middleman;
- this.runtimeInputs = inputs;
- return this;
+ public NestedSet<LibraryToLink> getLibraries() {
+ return this.libraries;
}
-
- /**
- * Sets the interface output of the link. A non-null argument can
- * only be provided if the link type is {@code DYNAMIC_LIBRARY}
- * and fake is false.
- */
- public Builder setInterfaceOutput(Artifact interfaceOutput) {
- this.interfaceOutput = interfaceOutput;
- return this;
- }
-
- public Builder setSymbolCountsOutput(Artifact symbolCounts) {
- this.symbolCounts = symbolCounts;
- return this;
- }
-
- /**
- * Add additional inputs needed for the linkstamp compilation that is being done as part of the
- * link.
- */
- public Builder addCompilationInputs(Iterable<Artifact> inputs) {
- this.compilationInputs.addAll(inputs);
- return this;
- }
-
- public Builder addTransitiveCompilationInputs(NestedSet<Artifact> inputs) {
- this.compilationInputs.addTransitive(inputs);
- return this;
- }
-
- private void addNonLibraryInput(LinkerInput input) {
- String name = input.getArtifact().getFilename();
- Preconditions.checkArgument(
- !Link.ARCHIVE_LIBRARY_FILETYPES.matches(name)
- && !Link.SHARED_LIBRARY_FILETYPES.matches(name),
- "'%s' is a library file", input);
- this.nonLibraries.add(input);
- }
-
- public Builder addLTOBitcodeFiles(Iterable<Artifact> files) {
- for (Artifact a : files) {
- ltoBitcodeFiles.add(a);
- }
- return this;
- }
-
- /**
- * Adds a single artifact to the set of inputs (C++ source files, header files, etc). Artifacts
- * that are not of recognized types will be used for dependency checking but will not be passed
- * to the linker. The artifact must not be an archive or a shared library.
- */
- public Builder addNonLibraryInput(Artifact input) {
- addNonLibraryInput(LinkerInputs.simpleLinkerInput(input));
- return this;
- }
-
- /**
- * Adds multiple artifacts to the set of inputs (C++ source files, header files, etc).
- * Artifacts that are not of recognized types will be used for dependency checking but will
- * not be passed to the linker. The artifacts must not be archives or shared libraries.
- */
- public Builder addNonLibraryInputs(Iterable<Artifact> inputs) {
- for (Artifact input : inputs) {
- addNonLibraryInput(LinkerInputs.simpleLinkerInput(input));
- }
- return this;
- }
-
- public Builder addFakeNonLibraryInputs(Iterable<Artifact> inputs) {
- for (Artifact input : inputs) {
- addNonLibraryInput(LinkerInputs.fakeLinkerInput(input));
- }
- return this;
- }
-
- private void checkLibrary(LibraryToLink input) {
- String name = input.getArtifact().getFilename();
- Preconditions.checkArgument(
- Link.ARCHIVE_LIBRARY_FILETYPES.matches(name)
- || Link.SHARED_LIBRARY_FILETYPES.matches(name),
- "'%s' is not a library file",
- input);
- }
-
- /**
- * Adds a single artifact to the set of inputs. The artifact must be an archive or a shared
- * library. Note that all directly added libraries are implicitly ordered before all nested
- * sets added with {@link #addLibraries}, even if added in the opposite order.
- */
- public Builder addLibrary(LibraryToLink input) {
- checkLibrary(input);
- libraries.add(input);
- return this;
- }
-
- /**
- * Adds multiple artifact to the set of inputs. The artifacts must be archives or shared
- * libraries.
- */
- public Builder addLibraries(NestedSet<LibraryToLink> inputs) {
- for (LibraryToLink input : inputs) {
- checkLibrary(input);
- }
- this.libraries.addTransitive(inputs);
- return this;
- }
-
+
/**
- * Sets the type of ELF file to be created (.a, .so, .lo, executable). The
- * default is {@link LinkTargetType#STATIC_LIBRARY}.
+ * Returns input artifacts arising from the crosstool.
*/
- public Builder setLinkType(LinkTargetType linkType) {
- this.linkType = linkType;
- return this;
+ public NestedSet<Artifact> getCrosstoolInputs() {
+ return this.crosstoolInputs;
}
-
+
/**
- * Sets the degree of "staticness" of the link: fully static (static binding
- * of all symbols), mostly static (use dynamic binding only for symbols from
- * glibc), dynamic (use dynamic binding wherever possible). The default is
- * {@link LinkStaticness#FULLY_STATIC}.
+ * Returns the runtime middleman artifact.
*/
- public Builder setLinkStaticness(LinkStaticness linkStaticness) {
- this.linkStaticness = linkStaticness;
- return this;
+ public Artifact getRuntimeMiddleman() {
+ return this.runtimeMiddleman;
}
-
- /**
- * Adds a C++ source file which will be compiled at link time. This is used
- * to embed various values from the build system into binaries to identify
- * their provenance.
- *
- * <p>Link stamps are also automatically added to the inputs.
- */
- public Builder addLinkstamps(Map<Artifact, NestedSet<Artifact>> linkstamps) {
- this.linkstamps.addAll(linkstamps.keySet());
- // Add inputs for linkstamping.
- if (!linkstamps.isEmpty()) {
- addTransitiveCompilationInputs(toolchain.getCompile());
- for (Map.Entry<Artifact, NestedSet<Artifact>> entry : linkstamps.entrySet()) {
- addCompilationInputs(entry.getValue());
- }
- }
- return this;
- }
-
- public Builder addLinkstampCompilerOptions(ImmutableList<String> linkstampOptions) {
- this.linkstampOptions = linkstampOptions;
- return this;
- }
-
+
/**
- * Adds an additional linker option.
+ * Returns runtime inputs for the linker.
*/
- public Builder addLinkopt(String linkopt) {
- this.linkopts.add(linkopt);
- return this;
+ public NestedSet<Artifact> getRuntimeInputs() {
+ return this.runtimeInputs;
}
-
+
/**
- * Adds multiple linker options at once.
- *
- * @see #addLinkopt(String)
+ * Returns compilation inputs for compilations arising from the linking of this target.
*/
- public Builder addLinkopts(Collection<String> linkopts) {
- this.linkopts.addAll(linkopts);
- return this;
+ public NestedSet<Artifact> getCompilationInputs() {
+ return this.compilationInputs;
}
/**
- * Merges the given link params into this builder by calling {@link #addLinkopts}, {@link
- * #addLibraries}, and {@link #addLinkstamps}.
+ * Returns linkstamp artifacts.
*/
- public Builder addLinkParams(CcLinkParams linkParams, RuleErrorConsumer errorListener) {
- addLinkopts(linkParams.flattenedLinkopts());
- addLibraries(linkParams.getLibraries());
- ExtraLinkTimeLibraries extraLinkTimeLibraries = linkParams.getExtraLinkTimeLibraries();
- if (extraLinkTimeLibraries != null) {
- for (ExtraLinkTimeLibrary extraLibrary : extraLinkTimeLibraries.getExtraLibraries()) {
- addLibraries(extraLibrary.buildLibraries(ruleContext));
- }
- }
- addLinkstamps(CppHelper.resolveLinkstamps(errorListener, linkParams));
- return this;
+ public ImmutableSet<Artifact> getLinkstamps() {
+ return this.linkstamps;
}
-
+
/**
- * Sets whether this link action will be used for a cc_fake_binary; false by
- * default.
+ * Returns linkopts for the linking of this target.
*/
- public Builder setFake(boolean fake) {
- this.fake = fake;
- return this;
+ public ImmutableList<String> getLinkopts() {
+ return this.linkopts;
}
-
+
/**
- * Sets whether this link action is used for a native dependency library.
+ * Returns the type of the linking of this target.
*/
- public Builder setNativeDeps(boolean isNativeDeps) {
- this.isNativeDeps = isNativeDeps;
- return this;
+ public LinkTargetType getLinkType() {
+ return this.linkType;
}
-
+
/**
- * Setting this to true overrides the default whole-archive computation and force-enables
- * whole archives for every archive in the link. This is only necessary for linking executable
- * binaries that are supposed to export symbols.
- *
- * <p>Usually, the link action while use whole archives for dynamic libraries that are native
- * deps (or the legacy whole archive flag is enabled), and that are not dynamically linked.
- *
- * <p>(Note that it is possible to build dynamic libraries with cc_binary rules by specifying
- * linkshared = 1, and giving the rule a name that matches the pattern {@code
- * lib&lt;name&gt;.so}.)
+ * Returns the staticness of the linking of this target.
*/
- public Builder setWholeArchive(boolean wholeArchive) {
- this.wholeArchive = wholeArchive;
- return this;
+ public LinkStaticness getLinkStaticness() {
+ return this.linkStaticness;
}
-
+
/**
- * Sets whether this link action should use test-specific flags (e.g. $EXEC_ORIGIN instead of
- * $ORIGIN for the solib search path or lazy binding); false by default.
+ * Returns true for cc_fake_binary targets.
*/
- public Builder setUseTestOnlyFlags(boolean useTestOnlyFlags) {
- this.useTestOnlyFlags = useTestOnlyFlags;
- return this;
+ public boolean isFake() {
+ return this.fake;
}
-
+
/**
- * Sets the name of the directory where the solib symlinks for the dynamic runtime libraries
- * live. This is usually automatically set from the cc_toolchain.
+ * Returns true if the linking of this target is used for a native dependecy library.
*/
- public Builder setRuntimeSolibDir(PathFragment runtimeSolibDir) {
- this.runtimeSolibDir = runtimeSolibDir;
- return this;
+ public boolean isNativeDeps() {
+ return this.isNativeDeps;
}
- }
-
- /**
- * TransitiveInfoProvider for ELF link actions.
- */
- @Immutable @ThreadSafe
- public static final class Context implements TransitiveInfoProvider {
- // Morally equivalent with {@link Builder}, except these are immutable.
- // Keep these in sync with {@link Builder}.
- private final ImmutableSet<LinkerInput> nonLibraries;
- private final NestedSet<LibraryToLink> libraries;
- private final NestedSet<Artifact> crosstoolInputs;
- private final Artifact runtimeMiddleman;
- private final NestedSet<Artifact> runtimeInputs;
- private final NestedSet<Artifact> compilationInputs;
- private final ImmutableSet<Artifact> linkstamps;
- private final ImmutableList<String> linkopts;
- private final LinkTargetType linkType;
- private final LinkStaticness linkStaticness;
- private final boolean fake;
- private final boolean isNativeDeps;
- private final boolean useTestOnlyFlags;
-
+
/**
- * Given a {@link Builder}, creates a {@code Context} to pass to another target.
- * Note well: Keep the Builder->Context and Context->Builder transforms consistent!
- * @param builder a mutable {@link CppLinkAction.Builder} to clone from
+ * Returns true if the linking for this target uses test-specific flags.
*/
- public Context(Builder builder) {
- this.nonLibraries = ImmutableSet.copyOf(builder.nonLibraries);
- this.libraries = NestedSetBuilder.<LibraryToLink>linkOrder()
- .addTransitive(builder.libraries.build()).build();
- this.crosstoolInputs =
- NestedSetBuilder.<Artifact>stableOrder().addTransitive(builder.crosstoolInputs).build();
- this.runtimeMiddleman = builder.runtimeMiddleman;
- this.runtimeInputs =
- NestedSetBuilder.<Artifact>stableOrder().addTransitive(builder.runtimeInputs).build();
- this.compilationInputs = NestedSetBuilder.<Artifact>stableOrder()
- .addTransitive(builder.compilationInputs.build()).build();
- this.linkstamps = ImmutableSet.copyOf(builder.linkstamps);
- this.linkopts = ImmutableList.copyOf(builder.linkopts);
- this.linkType = builder.linkType;
- this.linkStaticness = builder.linkStaticness;
- this.fake = builder.fake;
- this.isNativeDeps = builder.isNativeDeps;
- this.useTestOnlyFlags = builder.useTestOnlyFlags;
+ public boolean useTestOnlyFlags() {
+ return this.useTestOnlyFlags;
}
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppLinkActionBuilder.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppLinkActionBuilder.java
new file mode 100644
index 0000000000..3ec9843777
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppLinkActionBuilder.java
@@ -0,0 +1,939 @@
+// Copyright 2016 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.build.lib.rules.cpp;
+
+import static java.nio.charset.StandardCharsets.ISO_8859_1;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.devtools.build.lib.actions.Action;
+import com.google.devtools.build.lib.actions.ActionOwner;
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.actions.ParameterFile;
+import com.google.devtools.build.lib.analysis.AnalysisEnvironment;
+import com.google.devtools.build.lib.analysis.RuleContext;
+import com.google.devtools.build.lib.analysis.actions.ParameterFileWriteAction;
+import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
+import com.google.devtools.build.lib.collect.CollectionUtils;
+import com.google.devtools.build.lib.collect.ImmutableIterable;
+import com.google.devtools.build.lib.collect.IterablesChain;
+import com.google.devtools.build.lib.collect.nestedset.NestedSet;
+import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
+import com.google.devtools.build.lib.collect.nestedset.Order;
+import com.google.devtools.build.lib.packages.RuleErrorConsumer;
+import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.FeatureConfiguration;
+import com.google.devtools.build.lib.rules.cpp.CppLinkAction.Context;
+import com.google.devtools.build.lib.rules.cpp.CppLinkAction.LinkArtifactFactory;
+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.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.HashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.annotation.Nullable;
+
+/** Builder class to construct {@link CppLinkAction}s. */
+public class CppLinkActionBuilder {
+ // Builder-only
+ // Null when invoked from tests (e.g. via createTestBuilder).
+ @Nullable private final RuleContext ruleContext;
+ private final AnalysisEnvironment analysisEnvironment;
+ private final Artifact output;
+
+ // can be null for CppLinkAction.createTestBuilder()
+ @Nullable private final CcToolchainProvider toolchain;
+ private Artifact interfaceOutput;
+ private Artifact symbolCounts;
+ private PathFragment runtimeSolibDir;
+ protected final BuildConfiguration configuration;
+ private final CppConfiguration cppConfiguration;
+ private FeatureConfiguration featureConfiguration;
+ private CcToolchainFeatures.Variables buildVariables =
+ new CcToolchainFeatures.Variables.Builder().build();
+
+ // Morally equivalent with {@link Context}, except these are mutable.
+ // Keep these in sync with {@link Context}.
+ private final Set<LinkerInput> nonLibraries = new LinkedHashSet<>();
+ private final NestedSetBuilder<LibraryToLink> libraries = NestedSetBuilder.linkOrder();
+ private NestedSet<Artifact> crosstoolInputs = NestedSetBuilder.emptySet(Order.STABLE_ORDER);
+ private Artifact runtimeMiddleman;
+ private NestedSet<Artifact> runtimeInputs = NestedSetBuilder.emptySet(Order.STABLE_ORDER);
+ private final NestedSetBuilder<Artifact> compilationInputs = NestedSetBuilder.stableOrder();
+ private final Set<Artifact> linkstamps = new LinkedHashSet<>();
+ private List<String> linkstampOptions = new ArrayList<>();
+ private final List<String> linkopts = new ArrayList<>();
+ private LinkTargetType linkType = LinkTargetType.STATIC_LIBRARY;
+ private LinkStaticness linkStaticness = LinkStaticness.FULLY_STATIC;
+ private List<Artifact> ltoBitcodeFiles = new ArrayList<>();
+
+ private boolean fake;
+ private boolean isNativeDeps;
+ private boolean useTestOnlyFlags;
+ private boolean wholeArchive;
+ private LinkArtifactFactory linkArtifactFactory = CppLinkAction.DEFAULT_ARTIFACT_FACTORY;
+
+ private boolean isLTOIndexing = false;
+ private Iterable<LTOBackendArtifacts> allLTOArtifacts = null;
+
+ /**
+ * Creates a builder that builds {@link CppLinkAction} instances.
+ *
+ * @param ruleContext the rule that owns the action
+ * @param output the output artifact
+ */
+ public CppLinkActionBuilder(RuleContext ruleContext, Artifact output) {
+ this(
+ ruleContext,
+ output,
+ ruleContext.getConfiguration(),
+ ruleContext.getAnalysisEnvironment(),
+ CppHelper.getToolchain(ruleContext));
+ }
+
+ /**
+ * Creates a builder that builds {@link CppLinkAction} instances.
+ *
+ * @param ruleContext the rule that owns the action
+ * @param output the output artifact
+ */
+ public CppLinkActionBuilder(
+ RuleContext ruleContext,
+ Artifact output,
+ BuildConfiguration configuration,
+ CcToolchainProvider toolchain) {
+ this(ruleContext, output, configuration, ruleContext.getAnalysisEnvironment(), toolchain);
+ }
+
+ /**
+ * Creates a builder that builds {@link CppLinkAction}s.
+ *
+ * @param ruleContext the rule that owns the action
+ * @param output the output artifact
+ * @param configuration the configuration used to determine the tool chain and the default link
+ * options
+ */
+ private CppLinkActionBuilder(
+ @Nullable RuleContext ruleContext,
+ Artifact output,
+ BuildConfiguration configuration,
+ AnalysisEnvironment analysisEnvironment,
+ CcToolchainProvider toolchain) {
+ this.ruleContext = ruleContext;
+ this.analysisEnvironment = Preconditions.checkNotNull(analysisEnvironment);
+ this.output = Preconditions.checkNotNull(output);
+ this.configuration = Preconditions.checkNotNull(configuration);
+ this.cppConfiguration = configuration.getFragment(CppConfiguration.class);
+ this.toolchain = toolchain;
+ if (cppConfiguration.supportsEmbeddedRuntimes() && toolchain != null) {
+ runtimeSolibDir = toolchain.getDynamicRuntimeSolibDir();
+ }
+ }
+
+ /**
+ * Given a Context, creates a Builder that builds {@link CppLinkAction}s. Note well: Keep the
+ * Builder->Context and Context->Builder transforms consistent!
+ *
+ * @param ruleContext the rule that owns the action
+ * @param output the output artifact
+ * @param linkContext an immutable CppLinkAction.Context from the original builder
+ */
+ public CppLinkActionBuilder(
+ RuleContext ruleContext,
+ Artifact output,
+ Context linkContext,
+ BuildConfiguration configuration) {
+ // These Builder-only fields get set in the constructor:
+ // ruleContext, analysisEnvironment, outputPath, configuration, runtimeSolibDir
+ this(
+ ruleContext,
+ output,
+ configuration,
+ ruleContext.getAnalysisEnvironment(),
+ CppHelper.getToolchain(ruleContext));
+ Preconditions.checkNotNull(linkContext);
+
+ // All linkContext fields should be transferred to this Builder.
+ this.nonLibraries.addAll(linkContext.nonLibraries);
+ this.libraries.addTransitive(linkContext.libraries);
+ this.crosstoolInputs = linkContext.crosstoolInputs;
+ this.runtimeMiddleman = linkContext.runtimeMiddleman;
+ this.runtimeInputs = linkContext.runtimeInputs;
+ this.compilationInputs.addTransitive(linkContext.compilationInputs);
+ this.linkstamps.addAll(linkContext.linkstamps);
+ this.linkopts.addAll(linkContext.linkopts);
+ this.linkType = linkContext.linkType;
+ this.linkStaticness = linkContext.linkStaticness;
+ this.fake = linkContext.fake;
+ this.isNativeDeps = linkContext.isNativeDeps;
+ this.useTestOnlyFlags = linkContext.useTestOnlyFlags;
+ }
+
+ /** Returns the action name for purposes of querying the crosstool. */
+ private String getActionName() {
+ return linkType.getActionName();
+ }
+
+ /**
+ * Returns linker inputs that are not libraries.
+ */
+ public Set<LinkerInput> getNonLibraries() {
+ return nonLibraries;
+ }
+
+ /**
+ * Returns linker inputs that are libraries.
+ */
+ public NestedSetBuilder<LibraryToLink> getLibraries() {
+ return libraries;
+ }
+
+ /**
+ * Returns inputs arising from the crosstool.
+ */
+ public NestedSet<Artifact> getCrosstoolInputs() {
+ return this.crosstoolInputs;
+ }
+
+ /**
+ * Returns the runtime middleman artifact.
+ */
+ public Artifact getRuntimeMiddleman() {
+ return this.runtimeMiddleman;
+ }
+
+ /**
+ * Returns runtime inputs for this link action.
+ */
+ public NestedSet<Artifact> getRuntimeInputs() {
+ return this.runtimeInputs;
+ }
+
+ /**
+ * Returns compilation inputs for this link action.
+ */
+ public final NestedSetBuilder<Artifact> getCompilationInputs() {
+ return this.compilationInputs;
+ }
+
+ /**
+ * Returns linkstamps for this link action.
+ */
+ public final Set<Artifact> getLinkstamps() {
+ return this.linkstamps;
+ }
+ /**
+ * Returns linkstamp options for this link action.
+ */
+ public List<String> getLinkstampOptions() {
+ return this.linkstampOptions;
+ }
+
+ /**
+ * Returns command line options for this link action.
+ */
+ public final List<String> getLinkopts() {
+ return this.linkopts;
+ }
+
+ /**
+ * Returns the type of this link action.
+ */
+ public LinkTargetType getLinkType() {
+ return this.linkType;
+ }
+ /**
+ * Returns the staticness of this link action.
+ */
+ public LinkStaticness getLinkStaticness() {
+ return this.linkStaticness;
+ }
+ /**
+ * Returns lto bitcode files for this link action.
+ */
+ public List<Artifact> getLtoBitcodeFiles() {
+ return this.ltoBitcodeFiles;
+ }
+
+ /**
+ * Returns true for a cc_fake_binary.
+ */
+ public boolean isFake() {
+ return this.fake;
+ }
+
+ /**
+ * Returns true for native dependencies of another language.
+ */
+ public boolean isNativeDeps() {
+ return this.isNativeDeps;
+ }
+
+ public CppLinkActionBuilder setLinkArtifactFactory(LinkArtifactFactory linkArtifactFactory) {
+ this.linkArtifactFactory = linkArtifactFactory;
+ return this;
+ }
+
+ /**
+ * Returns true if this link action uses test only flags.
+ */
+ public boolean useTestOnlyFlags() {
+ return this.useTestOnlyFlags;
+ }
+
+ private Iterable<LTOBackendArtifacts> createLTOArtifacts(
+ PathFragment ltoOutputRootPrefix, NestedSet<LibraryToLink> uniqueLibraries) {
+ Set<Artifact> compiled = new LinkedHashSet<>();
+ for (LibraryToLink lib : uniqueLibraries) {
+ Iterables.addAll(compiled, lib.getLTOBitcodeFiles());
+ }
+
+ // This flattens the set of object files, so for M binaries and N .o files,
+ // this is O(M*N). If we had a nested set of .o files, we could have O(M + N) instead.
+ Map<PathFragment, Artifact> allBitcode = new HashMap<>();
+ for (LibraryToLink lib : uniqueLibraries) {
+ if (!lib.containsObjectFiles()) {
+ continue;
+ }
+ for (Artifact a : lib.getObjectFiles()) {
+ if (compiled.contains(a)) {
+ allBitcode.put(a.getExecPath(), a);
+ }
+ }
+ }
+ for (LinkerInput input : nonLibraries) {
+ // This relies on file naming conventions. It would be less fragile to have a dedicated
+ // field for non-library .o files.
+ if (CppFileTypes.OBJECT_FILE.matches(input.getArtifact().getExecPath())
+ || CppFileTypes.PIC_OBJECT_FILE.matches(input.getArtifact().getExecPath())) {
+ if (this.ltoBitcodeFiles.contains(input.getArtifact())) {
+ allBitcode.put(input.getArtifact().getExecPath(), input.getArtifact());
+ }
+ }
+ }
+
+ ImmutableList.Builder<LTOBackendArtifacts> ltoOutputs = ImmutableList.builder();
+ for (Artifact a : allBitcode.values()) {
+ LTOBackendArtifacts ltoArtifacts =
+ new LTOBackendArtifacts(
+ ltoOutputRootPrefix, a, allBitcode, ruleContext, configuration, linkArtifactFactory);
+ ltoOutputs.add(ltoArtifacts);
+ }
+ return ltoOutputs.build();
+ }
+
+ @VisibleForTesting
+ boolean canSplitCommandLine() {
+ if (toolchain == null || !toolchain.supportsParamFiles()) {
+ return false;
+ }
+
+ switch (linkType) {
+ // We currently can't split dynamic library links if they have interface outputs. That was
+ // probably an unintended side effect of the change that introduced interface outputs.
+ case DYNAMIC_LIBRARY:
+ return interfaceOutput == null;
+ case EXECUTABLE:
+ case STATIC_LIBRARY:
+ case PIC_STATIC_LIBRARY:
+ case ALWAYS_LINK_STATIC_LIBRARY:
+ case ALWAYS_LINK_PIC_STATIC_LIBRARY:
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ /** Builds the Action as configured and returns it. */
+ public CppLinkAction build() {
+ if (interfaceOutput != null && (fake || linkType != LinkTargetType.DYNAMIC_LIBRARY)) {
+ throw new RuntimeException(
+ "Interface output can only be used " + "with non-fake DYNAMIC_LIBRARY targets");
+ }
+
+ final ImmutableList<Artifact> buildInfoHeaderArtifacts =
+ !linkstamps.isEmpty()
+ ? analysisEnvironment.getBuildInfo(ruleContext, CppBuildInfo.KEY, configuration)
+ : ImmutableList.<Artifact>of();
+
+ boolean needWholeArchive =
+ wholeArchive
+ || needWholeArchive(linkStaticness, linkType, linkopts, isNativeDeps, cppConfiguration);
+
+ NestedSet<LibraryToLink> uniqueLibraries = libraries.build();
+ final Iterable<Artifact> filteredNonLibraryArtifacts =
+ CppLinkAction.filterLinkerInputArtifacts(LinkerInputs.toLibraryArtifacts(nonLibraries));
+
+ final Iterable<LinkerInput> linkerInputs =
+ IterablesChain.<LinkerInput>builder()
+ .add(ImmutableList.copyOf(CppLinkAction.filterLinkerInputs(nonLibraries)))
+ .add(
+ ImmutableIterable.from(
+ Link.mergeInputsCmdLine(
+ uniqueLibraries, needWholeArchive, cppConfiguration.archiveType())))
+ .build();
+
+ // ruleContext can only be null during testing. This is kind of ugly.
+ final ImmutableSet<String> features =
+ (ruleContext == null) ? ImmutableSet.<String>of() : ruleContext.getFeatures();
+
+ final LibraryToLink outputLibrary =
+ LinkerInputs.newInputLibrary(output, filteredNonLibraryArtifacts, this.ltoBitcodeFiles);
+ final LibraryToLink interfaceOutputLibrary =
+ (interfaceOutput == null)
+ ? null
+ : LinkerInputs.newInputLibrary(
+ interfaceOutput, filteredNonLibraryArtifacts, this.ltoBitcodeFiles);
+
+ final ImmutableMap<Artifact, Artifact> linkstampMap =
+ mapLinkstampsToOutputs(linkstamps, ruleContext, configuration, output, linkArtifactFactory);
+
+ PathFragment ltoOutputRootPrefix = null;
+ if (isLTOIndexing && allLTOArtifacts == null) {
+ ltoOutputRootPrefix =
+ FileSystemUtils.appendExtension(
+ outputLibrary.getArtifact().getRootRelativePath(), ".lto");
+ allLTOArtifacts = createLTOArtifacts(ltoOutputRootPrefix, uniqueLibraries);
+ }
+
+ final ImmutableList<Artifact> actionOutputs;
+ if (isLTOIndexing) {
+ ImmutableList.Builder<Artifact> builder = ImmutableList.builder();
+ for (LTOBackendArtifacts ltoA : allLTOArtifacts) {
+ ltoA.addIndexingOutputs(builder);
+ }
+ actionOutputs = builder.build();
+ } else {
+ actionOutputs =
+ constructOutputs(
+ outputLibrary.getArtifact(),
+ linkstampMap.values(),
+ interfaceOutputLibrary == null ? null : interfaceOutputLibrary.getArtifact(),
+ symbolCounts);
+ }
+
+ PathFragment paramRootPath =
+ ParameterFile.derivePath(
+ outputLibrary.getArtifact().getRootRelativePath(), (isLTOIndexing) ? "lto" : "2");
+
+ @Nullable
+ final Artifact paramFile =
+ canSplitCommandLine()
+ ? linkArtifactFactory.create(ruleContext, configuration, paramRootPath)
+ : null;
+
+ LinkCommandLine.Builder linkCommandLineBuilder =
+ new LinkCommandLine.Builder(configuration, getOwner(), ruleContext)
+ .setActionName(getActionName())
+ .setLinkerInputs(linkerInputs)
+ .setRuntimeInputs(ImmutableList.copyOf(LinkerInputs.simpleLinkerInputs(runtimeInputs)))
+ .setLinkTargetType(linkType)
+ .setLinkStaticness(linkStaticness)
+ .setFeatures(features)
+ .setRuntimeSolibDir(linkType.isStaticLibraryLink() ? null : runtimeSolibDir)
+ .setNativeDeps(isNativeDeps)
+ .setUseTestOnlyFlags(useTestOnlyFlags)
+ .setNeedWholeArchive(needWholeArchive)
+ .setParamFile(paramFile)
+ .setAllLTOArtifacts(isLTOIndexing ? null : allLTOArtifacts)
+ .setToolchain(toolchain)
+ .setFeatureConfiguration(featureConfiguration);
+
+ if (!isLTOIndexing) {
+ linkCommandLineBuilder
+ .setOutput(outputLibrary.getArtifact())
+ .setInterfaceOutput(interfaceOutput)
+ .setSymbolCountsOutput(symbolCounts)
+ .setBuildInfoHeaderArtifacts(buildInfoHeaderArtifacts)
+ .setInterfaceSoBuilder(getInterfaceSoBuilder())
+ .setLinkstamps(linkstampMap)
+ .setLinkopts(ImmutableList.copyOf(linkopts))
+ .addLinkstampCompileOptions(linkstampOptions);
+ } else {
+ // TODO(bazel-team): once the LLVM compiler patches have been finalized, this should
+ // be converted to a crosstool feature configuration instead.
+ List<String> opts = new ArrayList<>(linkopts);
+ opts.add("-flto=thin");
+ opts.add("-Wl,-plugin-opt,thinlto-index-only");
+ opts.add("-Wl,-plugin-opt,thinlto-emit-imports-files");
+ opts.add(
+ "-Wl,-plugin-opt,thinlto-prefix-replace="
+ + configuration.getBinDirectory().getExecPathString()
+ + ";"
+ + configuration
+ .getBinDirectory()
+ .getExecPath()
+ .getRelative(ltoOutputRootPrefix)
+ .toString());
+ linkCommandLineBuilder.setLinkopts(ImmutableList.copyOf(opts));
+ }
+
+ LinkCommandLine linkCommandLine = linkCommandLineBuilder.build();
+
+ // Compute the set of inputs - we only need stable order here.
+ NestedSetBuilder<Artifact> dependencyInputsBuilder = NestedSetBuilder.stableOrder();
+ dependencyInputsBuilder.addTransitive(crosstoolInputs);
+ if (runtimeMiddleman != null) {
+ dependencyInputsBuilder.add(runtimeMiddleman);
+ }
+ if (!isLTOIndexing) {
+ dependencyInputsBuilder.addAll(buildInfoHeaderArtifacts);
+ dependencyInputsBuilder.addAll(linkstamps);
+ dependencyInputsBuilder.addTransitive(compilationInputs.build());
+ }
+
+ // For backwards compatibility, and for tests, we permit the link action to be
+ // instantiated without a feature configuration. In this case, an empty feature
+ // configuration is used.
+ if (featureConfiguration == null) {
+ this.featureConfiguration = new FeatureConfiguration();
+ }
+
+ Iterable<Artifact> expandedInputs =
+ LinkerInputs.toLibraryArtifacts(
+ Link.mergeInputsDependencies(
+ uniqueLibraries, needWholeArchive, cppConfiguration.archiveType()));
+ Iterable<Artifact> expandedNonLibraryInputs = LinkerInputs.toLibraryArtifacts(nonLibraries);
+ if (!isLTOIndexing && allLTOArtifacts != null) {
+ // We are doing LTO, and this is the real link, so substitute
+ // the LTO bitcode files with the real object files they were translated into.
+ Map<Artifact, Artifact> ltoMapping = new HashMap<>();
+ for (LTOBackendArtifacts a : allLTOArtifacts) {
+ ltoMapping.put(a.getBitcodeFile(), a.getObjectFile());
+ }
+
+ // Handle libraries.
+ List<Artifact> renamedInputs = new ArrayList<>();
+ for (Artifact a : expandedInputs) {
+ Artifact renamed = ltoMapping.get(a);
+ renamedInputs.add(renamed == null ? a : renamed);
+ }
+ expandedInputs = renamedInputs;
+
+ // Handle non-libraries.
+ List<Artifact> renamedNonLibraryInputs = new ArrayList<>();
+ for (Artifact a : expandedNonLibraryInputs) {
+ Artifact renamed = ltoMapping.get(a);
+ renamedNonLibraryInputs.add(renamed == null ? a : renamed);
+ }
+ expandedNonLibraryInputs = renamedNonLibraryInputs;
+ } else if (isLTOIndexing && allLTOArtifacts != null) {
+ for (LTOBackendArtifacts a : allLTOArtifacts) {
+ List<String> argv = new ArrayList<>();
+ argv.addAll(cppConfiguration.getLinkOptions());
+ argv.addAll(featureConfiguration.getCommandLine(getActionName(), buildVariables));
+ argv.addAll(cppConfiguration.getCompilerOptions(features));
+ a.setCommandLine(argv);
+ }
+ }
+
+ // getPrimaryInput returns the first element, and that is a public interface - therefore the
+ // order here is important.
+ IterablesChain.Builder<Artifact> inputsBuilder =
+ IterablesChain.<Artifact>builder()
+ .add(ImmutableList.copyOf(expandedNonLibraryInputs))
+ .add(dependencyInputsBuilder.build())
+ .add(ImmutableIterable.from(expandedInputs));
+
+ if (linkCommandLine.getParamFile() != null) {
+ inputsBuilder.add(ImmutableList.of(linkCommandLine.getParamFile()));
+ Action parameterFileWriteAction =
+ new ParameterFileWriteAction(
+ getOwner(),
+ paramFile,
+ linkCommandLine.paramCmdLine(),
+ ParameterFile.ParameterFileType.UNQUOTED,
+ ISO_8859_1);
+ analysisEnvironment.registerAction(parameterFileWriteAction);
+ }
+
+ Map<String, String> toolchainEnv =
+ featureConfiguration.getEnvironmentVariables(getActionName(), buildVariables);
+
+ // If the crosstool uses action_configs to configure cc compilation, collect execution info
+ // from there, otherwise, use no execution info.
+ // TODO(b/27903698): Assert that the crosstool has an action_config for this action.
+ ImmutableSet.Builder<String> executionRequirements = ImmutableSet.<String>builder();
+ if (featureConfiguration.actionIsConfigured(getActionName())) {
+ executionRequirements.addAll(
+ featureConfiguration.getToolForAction(getActionName()).getExecutionRequirements());
+ }
+
+ return new CppLinkAction(
+ getOwner(),
+ inputsBuilder.deduplicate().build(),
+ actionOutputs,
+ cppConfiguration,
+ outputLibrary,
+ interfaceOutputLibrary,
+ fake,
+ isLTOIndexing,
+ allLTOArtifacts,
+ linkCommandLine,
+ toolchainEnv,
+ executionRequirements.build());
+ }
+
+ /** The default heuristic on whether we need to use whole-archive for the link. */
+ private static boolean needWholeArchive(
+ LinkStaticness staticness,
+ LinkTargetType type,
+ Collection<String> linkopts,
+ boolean isNativeDeps,
+ CppConfiguration cppConfig) {
+ boolean fullyStatic = (staticness == LinkStaticness.FULLY_STATIC);
+ boolean mostlyStatic = (staticness == LinkStaticness.MOSTLY_STATIC);
+ boolean sharedLinkopts =
+ type == LinkTargetType.DYNAMIC_LIBRARY
+ || linkopts.contains("-shared")
+ || cppConfig.getLinkOptions().contains("-shared");
+ return (isNativeDeps || cppConfig.legacyWholeArchive())
+ && (fullyStatic || mostlyStatic)
+ && sharedLinkopts;
+ }
+
+ private static ImmutableList<Artifact> constructOutputs(
+ Artifact primaryOutput, Collection<Artifact> outputList, Artifact... outputs) {
+ return new ImmutableList.Builder<Artifact>()
+ .add(primaryOutput)
+ .addAll(outputList)
+ .addAll(CollectionUtils.asListWithoutNulls(outputs))
+ .build();
+ }
+
+ /**
+ * Translates a collection of linkstamp source files to an immutable mapping from source files to
+ * object files. In other words, given a set of source files, this method determines the output
+ * path to which each file should be compiled.
+ *
+ * @param linkstamps collection of linkstamp source files
+ * @param ruleContext the rule for which this link is being performed
+ * @param outputBinary the binary output path for this link
+ * @return an immutable map that pairs each source file with the corresponding object file that
+ * should be fed into the link
+ */
+ public static ImmutableMap<Artifact, Artifact> mapLinkstampsToOutputs(
+ Collection<Artifact> linkstamps,
+ RuleContext ruleContext,
+ BuildConfiguration configuration,
+ Artifact outputBinary,
+ LinkArtifactFactory linkArtifactFactory) {
+ ImmutableMap.Builder<Artifact, Artifact> mapBuilder = ImmutableMap.builder();
+
+ PathFragment outputBinaryPath = outputBinary.getRootRelativePath();
+ PathFragment stampOutputDirectory =
+ outputBinaryPath
+ .getParentDirectory()
+ .getRelative("_objs")
+ .getRelative(outputBinaryPath.getBaseName());
+
+ for (Artifact linkstamp : linkstamps) {
+ PathFragment stampOutputPath =
+ stampOutputDirectory.getRelative(
+ FileSystemUtils.replaceExtension(linkstamp.getRootRelativePath(), ".o"));
+ mapBuilder.put(
+ linkstamp,
+ // Note that link stamp actions can be shared between link actions that output shared
+ // native dep libraries.
+ linkArtifactFactory.create(ruleContext, configuration, stampOutputPath));
+ }
+ return mapBuilder.build();
+ }
+
+ protected ActionOwner getOwner() {
+ return ruleContext.getActionOwner();
+ }
+
+ protected Artifact getInterfaceSoBuilder() {
+ return analysisEnvironment.getEmbeddedToolArtifact(CppRuleClasses.BUILD_INTERFACE_SO);
+ }
+
+ /** Set the crosstool inputs required for the action. */
+ public CppLinkActionBuilder setCrosstoolInputs(NestedSet<Artifact> inputs) {
+ this.crosstoolInputs = inputs;
+ return this;
+ }
+
+ /** Sets the feature configuration for the action. */
+ public CppLinkActionBuilder setFeatureConfiguration(FeatureConfiguration featureConfiguration) {
+ this.featureConfiguration = featureConfiguration;
+ return this;
+ }
+
+ /** Sets the build variables that will be used to template the crosstool. */
+ public CppLinkActionBuilder setBuildVariables(CcToolchainFeatures.Variables buildVariables) {
+ this.buildVariables = buildVariables;
+ return this;
+ }
+
+ /**
+ * This is the LTO indexing step, rather than the real link.
+ *
+ * <p>When using this, build() will store allLTOArtifacts as a side-effect so the next build()
+ * call can emit the real link. Do not call addInput() between the two build() calls.
+ */
+ public CppLinkActionBuilder setLTOIndexing(boolean ltoIndexing) {
+ this.isLTOIndexing = ltoIndexing;
+ return this;
+ }
+
+ /** Sets the C++ runtime library inputs for the action. */
+ public CppLinkActionBuilder setRuntimeInputs(Artifact middleman, NestedSet<Artifact> inputs) {
+ Preconditions.checkArgument((middleman == null) == inputs.isEmpty());
+ this.runtimeMiddleman = middleman;
+ this.runtimeInputs = inputs;
+ return this;
+ }
+
+ /**
+ * Sets the interface output of the link. A non-null argument can only be provided if the link
+ * type is {@code DYNAMIC_LIBRARY} and fake is false.
+ */
+ public CppLinkActionBuilder setInterfaceOutput(Artifact interfaceOutput) {
+ this.interfaceOutput = interfaceOutput;
+ return this;
+ }
+
+ public CppLinkActionBuilder setSymbolCountsOutput(Artifact symbolCounts) {
+ this.symbolCounts = symbolCounts;
+ return this;
+ }
+
+ /**
+ * Add additional inputs needed for the linkstamp compilation that is being done as part of the
+ * link.
+ */
+ public CppLinkActionBuilder addCompilationInputs(Iterable<Artifact> inputs) {
+ this.compilationInputs.addAll(inputs);
+ return this;
+ }
+
+ public CppLinkActionBuilder addTransitiveCompilationInputs(NestedSet<Artifact> inputs) {
+ this.compilationInputs.addTransitive(inputs);
+ return this;
+ }
+
+ private void addNonLibraryInput(LinkerInput input) {
+ String name = input.getArtifact().getFilename();
+ Preconditions.checkArgument(
+ !Link.ARCHIVE_LIBRARY_FILETYPES.matches(name)
+ && !Link.SHARED_LIBRARY_FILETYPES.matches(name),
+ "'%s' is a library file",
+ input);
+ this.nonLibraries.add(input);
+ }
+
+ public CppLinkActionBuilder addLTOBitcodeFiles(Iterable<Artifact> files) {
+ for (Artifact a : files) {
+ ltoBitcodeFiles.add(a);
+ }
+ return this;
+ }
+
+ /**
+ * Adds a single artifact to the set of inputs (C++ source files, header files, etc). Artifacts
+ * that are not of recognized types will be used for dependency checking but will not be passed to
+ * the linker. The artifact must not be an archive or a shared library.
+ */
+ public CppLinkActionBuilder addNonLibraryInput(Artifact input) {
+ addNonLibraryInput(LinkerInputs.simpleLinkerInput(input));
+ return this;
+ }
+
+ /**
+ * Adds multiple artifacts to the set of inputs (C++ source files, header files, etc). Artifacts
+ * that are not of recognized types will be used for dependency checking but will not be passed to
+ * the linker. The artifacts must not be archives or shared libraries.
+ */
+ public CppLinkActionBuilder addNonLibraryInputs(Iterable<Artifact> inputs) {
+ for (Artifact input : inputs) {
+ addNonLibraryInput(LinkerInputs.simpleLinkerInput(input));
+ }
+ return this;
+ }
+
+ public CppLinkActionBuilder addFakeNonLibraryInputs(Iterable<Artifact> inputs) {
+ for (Artifact input : inputs) {
+ addNonLibraryInput(LinkerInputs.fakeLinkerInput(input));
+ }
+ return this;
+ }
+
+ private void checkLibrary(LibraryToLink input) {
+ String name = input.getArtifact().getFilename();
+ Preconditions.checkArgument(
+ Link.ARCHIVE_LIBRARY_FILETYPES.matches(name) || Link.SHARED_LIBRARY_FILETYPES.matches(name),
+ "'%s' is not a library file",
+ input);
+ }
+
+ /**
+ * Adds a single artifact to the set of inputs. The artifact must be an archive or a shared
+ * library. Note that all directly added libraries are implicitly ordered before all nested sets
+ * added with {@link #addLibraries}, even if added in the opposite order.
+ */
+ public CppLinkActionBuilder addLibrary(LibraryToLink input) {
+ checkLibrary(input);
+ libraries.add(input);
+ return this;
+ }
+
+ /**
+ * Adds multiple artifact to the set of inputs. The artifacts must be archives or shared
+ * libraries.
+ */
+ public CppLinkActionBuilder addLibraries(NestedSet<LibraryToLink> inputs) {
+ for (LibraryToLink input : inputs) {
+ checkLibrary(input);
+ }
+ this.libraries.addTransitive(inputs);
+ return this;
+ }
+
+ /**
+ * Sets the type of ELF file to be created (.a, .so, .lo, executable). The default is {@link
+ * LinkTargetType#STATIC_LIBRARY}.
+ */
+ public CppLinkActionBuilder setLinkType(LinkTargetType linkType) {
+ this.linkType = linkType;
+ return this;
+ }
+
+ /**
+ * Sets the degree of "staticness" of the link: fully static (static binding of all symbols),
+ * mostly static (use dynamic binding only for symbols from glibc), dynamic (use dynamic binding
+ * wherever possible). The default is {@link LinkStaticness#FULLY_STATIC}.
+ */
+ public CppLinkActionBuilder setLinkStaticness(LinkStaticness linkStaticness) {
+ this.linkStaticness = linkStaticness;
+ return this;
+ }
+
+ /**
+ * Adds a C++ source file which will be compiled at link time. This is used to embed various
+ * values from the build system into binaries to identify their provenance.
+ *
+ * <p>Link stamps are also automatically added to the inputs.
+ */
+ public CppLinkActionBuilder addLinkstamps(Map<Artifact, NestedSet<Artifact>> linkstamps) {
+ this.linkstamps.addAll(linkstamps.keySet());
+ // Add inputs for linkstamping.
+ if (!linkstamps.isEmpty()) {
+ addTransitiveCompilationInputs(toolchain.getCompile());
+ for (Map.Entry<Artifact, NestedSet<Artifact>> entry : linkstamps.entrySet()) {
+ addCompilationInputs(entry.getValue());
+ }
+ }
+ return this;
+ }
+
+ public CppLinkActionBuilder addLinkstampCompilerOptions(ImmutableList<String> linkstampOptions) {
+ this.linkstampOptions = linkstampOptions;
+ return this;
+ }
+
+ /** Adds an additional linker option. */
+ public CppLinkActionBuilder addLinkopt(String linkopt) {
+ this.linkopts.add(linkopt);
+ return this;
+ }
+
+ /**
+ * Adds multiple linker options at once.
+ *
+ * @see #addLinkopt(String)
+ */
+ public CppLinkActionBuilder addLinkopts(Collection<String> linkopts) {
+ this.linkopts.addAll(linkopts);
+ return this;
+ }
+
+ /**
+ * Merges the given link params into this builder by calling {@link #addLinkopts}, {@link
+ * #addLibraries}, and {@link #addLinkstamps}.
+ */
+ public CppLinkActionBuilder addLinkParams(
+ CcLinkParams linkParams, RuleErrorConsumer errorListener) {
+ addLinkopts(linkParams.flattenedLinkopts());
+ addLibraries(linkParams.getLibraries());
+ ExtraLinkTimeLibraries extraLinkTimeLibraries = linkParams.getExtraLinkTimeLibraries();
+ if (extraLinkTimeLibraries != null) {
+ for (ExtraLinkTimeLibrary extraLibrary : extraLinkTimeLibraries.getExtraLibraries()) {
+ addLibraries(extraLibrary.buildLibraries(ruleContext));
+ }
+ }
+ addLinkstamps(CppHelper.resolveLinkstamps(errorListener, linkParams));
+ return this;
+ }
+
+ /** Sets whether this link action will be used for a cc_fake_binary; false by default. */
+ public CppLinkActionBuilder setFake(boolean fake) {
+ this.fake = fake;
+ return this;
+ }
+
+ /** Sets whether this link action is used for a native dependency library. */
+ public CppLinkActionBuilder setNativeDeps(boolean isNativeDeps) {
+ this.isNativeDeps = isNativeDeps;
+ return this;
+ }
+
+ /**
+ * Setting this to true overrides the default whole-archive computation and force-enables whole
+ * archives for every archive in the link. This is only necessary for linking executable binaries
+ * that are supposed to export symbols.
+ *
+ * <p>Usually, the link action while use whole archives for dynamic libraries that are native deps
+ * (or the legacy whole archive flag is enabled), and that are not dynamically linked.
+ *
+ * <p>(Note that it is possible to build dynamic libraries with cc_binary rules by specifying
+ * linkshared = 1, and giving the rule a name that matches the pattern {@code
+ * lib&lt;name&gt;.so}.)
+ */
+ public CppLinkActionBuilder setWholeArchive(boolean wholeArchive) {
+ this.wholeArchive = wholeArchive;
+ return this;
+ }
+
+ /**
+ * Sets whether this link action should use test-specific flags (e.g. $EXEC_ORIGIN instead of
+ * $ORIGIN for the solib search path or lazy binding); false by default.
+ */
+ public CppLinkActionBuilder setUseTestOnlyFlags(boolean useTestOnlyFlags) {
+ this.useTestOnlyFlags = useTestOnlyFlags;
+ return this;
+ }
+
+ /**
+ * Sets the name of the directory where the solib symlinks for the dynamic runtime libraries live.
+ * This is usually automatically set from the cc_toolchain.
+ */
+ public CppLinkActionBuilder setRuntimeSolibDir(PathFragment runtimeSolibDir) {
+ this.runtimeSolibDir = runtimeSolibDir;
+ return this;
+ }
+}
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
index b1305aebaf..a70f9d91c2 100644
--- 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
@@ -842,7 +842,7 @@ public final class CppModel {
// Should we also link in any libraries that this library depends on?
// That is required on some systems...
- CppLinkAction.Builder linkActionBuilder =
+ CppLinkActionBuilder linkActionBuilder =
newLinkActionBuilder(soImpl)
.setInterfaceOutput(soInterface)
.addNonLibraryInputs(ccOutputs.getObjectFiles(usePicForSharedLibs))
@@ -899,8 +899,8 @@ public final class CppModel {
return result.build();
}
- private CppLinkAction.Builder newLinkActionBuilder(Artifact outputArtifact) {
- return new CppLinkAction.Builder(ruleContext, outputArtifact)
+ private CppLinkActionBuilder newLinkActionBuilder(Artifact outputArtifact) {
+ return new CppLinkActionBuilder(ruleContext, outputArtifact)
.setCrosstoolInputs(CppHelper.getToolchain(ruleContext).getLink())
.addNonLibraryInputs(context.getTransitiveCompilationPrerequisites());
}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/nativedeps/NativeDepsHelper.java b/src/main/java/com/google/devtools/build/lib/rules/nativedeps/NativeDepsHelper.java
index fda5b4e8aa..45f204ee92 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/nativedeps/NativeDepsHelper.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/nativedeps/NativeDepsHelper.java
@@ -27,6 +27,7 @@ import com.google.devtools.build.lib.rules.cpp.CppBuildInfo;
import com.google.devtools.build.lib.rules.cpp.CppConfiguration;
import com.google.devtools.build.lib.rules.cpp.CppHelper;
import com.google.devtools.build.lib.rules.cpp.CppLinkAction;
+import com.google.devtools.build.lib.rules.cpp.CppLinkActionBuilder;
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;
@@ -146,8 +147,8 @@ public abstract class NativeDepsHelper {
ruleContext.getFeatures()),
configuration.getBinDirectory())
: nativeDeps;
- CppLinkAction.Builder builder = new CppLinkAction.Builder(
- ruleContext, sharedLibrary, configuration, toolchain);
+ CppLinkActionBuilder builder =
+ new CppLinkActionBuilder(ruleContext, sharedLibrary, configuration, toolchain);
if (useDynamicRuntime) {
builder.setRuntimeInputs(
toolchain.getDynamicRuntimeLinkMiddleman(), toolchain.getDynamicRuntimeLinkInputs());