From 5234eed84d845cda522256ad9674ec0a7076f7be Mon Sep 17 00:00:00 2001 From: Cal Peyser Date: Tue, 12 Jul 2016 18:22:13 +0000 Subject: Extract CppLinkAction.Builder to its own top level class. -- MOS_MIGRATED_REVID=127221256 --- .../devtools/build/lib/rules/cpp/CcBinary.java | 25 +- .../build/lib/rules/cpp/CppLinkAction.java | 890 +++---------------- .../build/lib/rules/cpp/CppLinkActionBuilder.java | 939 +++++++++++++++++++++ .../devtools/build/lib/rules/cpp/CppModel.java | 6 +- .../lib/rules/nativedeps/NativeDepsHelper.java | 5 +- 5 files changed, 1056 insertions(+), 809 deletions(-) create mode 100644 src/main/java/com/google/devtools/build/lib/rules/cpp/CppLinkActionBuilder.java (limited to 'src/main/java/com/google/devtools/build') 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 compilationPrerequisites, - boolean fake, Artifact binary, - LinkStaticness linkStaticness, List linkopts) { - CppLinkAction.Builder builder = new CppLinkAction.Builder(context, binary) - .setCrosstoolInputs(CppHelper.getToolchain(context).getLink()) - .addNonLibraryInputs(compilationPrerequisites); + boolean fake, + Artifact binary, + LinkStaticness linkStaticness, + List 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. * - *

This constructor is intentionally private and is only to be called from - * {@link Builder#build()}. + *

This constructor is intentionally private and is only to be called from {@link + * CppLinkActionBuilder#build()}. */ - private CppLinkAction( + CppLinkAction( ActionOwner owner, Iterable inputs, ImmutableList outputs, @@ -176,7 +161,7 @@ public final class CppLinkAction extends AbstractAction implements ExecutionInfo this.executionRequirements = executionRequirements; } - private static Iterable filterLinkerInputs(Iterable inputs) { + static Iterable filterLinkerInputs(Iterable inputs) { return Iterables.filter(inputs, new Predicate() { @Override public boolean apply(LinkerInput input) { @@ -185,7 +170,7 @@ public final class CppLinkAction extends AbstractAction implements ExecutionInfo }); } - private static Iterable filterLinkerInputArtifacts(Iterable inputs) { + static Iterable filterLinkerInputArtifacts(Iterable inputs) { return Iterables.filter(inputs, new Predicate() { @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 nonLibraries = new LinkedHashSet<>(); - private final NestedSetBuilder libraries = NestedSetBuilder.linkOrder(); - private NestedSet crosstoolInputs = NestedSetBuilder.emptySet(Order.STABLE_ORDER); - private Artifact runtimeMiddleman; - private NestedSet runtimeInputs = NestedSetBuilder.emptySet(Order.STABLE_ORDER); - private final NestedSetBuilder compilationInputs = NestedSetBuilder.stableOrder(); - private final Set linkstamps = new LinkedHashSet<>(); - private List linkstampOptions = new ArrayList<>(); - private final List linkopts = new ArrayList<>(); - private LinkTargetType linkType = LinkTargetType.STATIC_LIBRARY; - private LinkStaticness linkStaticness = LinkStaticness.FULLY_STATIC; - private List 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 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 nonLibraries; + final NestedSet libraries; + final NestedSet crosstoolInputs; + final Artifact runtimeMiddleman; + final NestedSet runtimeInputs; + final NestedSet compilationInputs; + final ImmutableSet linkstamps; + final ImmutableList 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 createLTOArtifacts( - PathFragment ltoOutputRootPrefix, NestedSet uniqueLibraries) { - Set 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 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 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 buildInfoHeaderArtifacts = !linkstamps.isEmpty() - ? analysisEnvironment.getBuildInfo(ruleContext, CppBuildInfo.KEY, configuration) - : ImmutableList.of(); - - boolean needWholeArchive = wholeArchive || needWholeArchive( - linkStaticness, linkType, linkopts, isNativeDeps, cppConfiguration); - - NestedSet uniqueLibraries = libraries.build(); - final Iterable filteredNonLibraryArtifacts = - filterLinkerInputArtifacts(LinkerInputs.toLibraryArtifacts(nonLibraries)); - - final Iterable linkerInputs = IterablesChain.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 features = (ruleContext == null) - ? ImmutableSet.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 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 actionOutputs; - if (isLTOIndexing) { - ImmutableList.Builder 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 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 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 expandedInputs = - LinkerInputs.toLibraryArtifacts( - Link.mergeInputsDependencies( - uniqueLibraries, needWholeArchive, cppConfiguration.archiveType())); - Iterable 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 ltoMapping = new HashMap<>(); - for (LTOBackendArtifacts a : allLTOArtifacts) { - ltoMapping.put(a.getBitcodeFile(), a.getObjectFile()); - } - - // Handle libraries. - List renamedInputs = new ArrayList<>(); - for (Artifact a : expandedInputs) { - Artifact renamed = ltoMapping.get(a); - renamedInputs.add(renamed == null ? a : renamed); - } - expandedInputs = renamedInputs; - - // Handle non-libraries. - List 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 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 inputsBuilder = - IterablesChain.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 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 executionRequirements = ImmutableSet.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 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 constructOutputs(Artifact primaryOutput, - Collection outputList, Artifact... outputs) { - return new ImmutableList.Builder() - .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 mapLinkstampsToOutputs( - Collection linkstamps, RuleContext ruleContext, BuildConfiguration configuration, - Artifact outputBinary, LinkArtifactFactory linkArtifactFactory) { - ImmutableMap.Builder 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.linkOrder() + .addTransitive(builder.getLibraries().build()).build(); + this.crosstoolInputs = + NestedSetBuilder.stableOrder().addTransitive(builder.getCrosstoolInputs()).build(); + this.runtimeMiddleman = builder.getRuntimeMiddleman(); + this.runtimeInputs = + NestedSetBuilder.stableOrder().addTransitive(builder.getRuntimeInputs()).build(); + this.compilationInputs = NestedSetBuilder.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 inputs) { - this.crosstoolInputs = inputs; - return this; + public ImmutableSet 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. - * - *

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 inputs) { - Preconditions.checkArgument((middleman == null) == inputs.isEmpty()); - this.runtimeMiddleman = middleman; - this.runtimeInputs = inputs; - return this; + public NestedSet 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 inputs) { - this.compilationInputs.addAll(inputs); - return this; - } - - public Builder addTransitiveCompilationInputs(NestedSet 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 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 inputs) { - for (Artifact input : inputs) { - addNonLibraryInput(LinkerInputs.simpleLinkerInput(input)); - } - return this; - } - - public Builder addFakeNonLibraryInputs(Iterable 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 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 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. - * - *

Link stamps are also automatically added to the inputs. - */ - public Builder addLinkstamps(Map> linkstamps) { - this.linkstamps.addAll(linkstamps.keySet()); - // Add inputs for linkstamping. - if (!linkstamps.isEmpty()) { - addTransitiveCompilationInputs(toolchain.getCompile()); - for (Map.Entry> entry : linkstamps.entrySet()) { - addCompilationInputs(entry.getValue()); - } - } - return this; - } - - public Builder addLinkstampCompilerOptions(ImmutableList 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 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 linkopts) { - this.linkopts.addAll(linkopts); - return this; + public NestedSet 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 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 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. - * - *

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

(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<name>.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 nonLibraries; - private final NestedSet libraries; - private final NestedSet crosstoolInputs; - private final Artifact runtimeMiddleman; - private final NestedSet runtimeInputs; - private final NestedSet compilationInputs; - private final ImmutableSet linkstamps; - private final ImmutableList 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.linkOrder() - .addTransitive(builder.libraries.build()).build(); - this.crosstoolInputs = - NestedSetBuilder.stableOrder().addTransitive(builder.crosstoolInputs).build(); - this.runtimeMiddleman = builder.runtimeMiddleman; - this.runtimeInputs = - NestedSetBuilder.stableOrder().addTransitive(builder.runtimeInputs).build(); - this.compilationInputs = NestedSetBuilder.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 nonLibraries = new LinkedHashSet<>(); + private final NestedSetBuilder libraries = NestedSetBuilder.linkOrder(); + private NestedSet crosstoolInputs = NestedSetBuilder.emptySet(Order.STABLE_ORDER); + private Artifact runtimeMiddleman; + private NestedSet runtimeInputs = NestedSetBuilder.emptySet(Order.STABLE_ORDER); + private final NestedSetBuilder compilationInputs = NestedSetBuilder.stableOrder(); + private final Set linkstamps = new LinkedHashSet<>(); + private List linkstampOptions = new ArrayList<>(); + private final List linkopts = new ArrayList<>(); + private LinkTargetType linkType = LinkTargetType.STATIC_LIBRARY; + private LinkStaticness linkStaticness = LinkStaticness.FULLY_STATIC; + private List 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 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 getNonLibraries() { + return nonLibraries; + } + + /** + * Returns linker inputs that are libraries. + */ + public NestedSetBuilder getLibraries() { + return libraries; + } + + /** + * Returns inputs arising from the crosstool. + */ + public NestedSet getCrosstoolInputs() { + return this.crosstoolInputs; + } + + /** + * Returns the runtime middleman artifact. + */ + public Artifact getRuntimeMiddleman() { + return this.runtimeMiddleman; + } + + /** + * Returns runtime inputs for this link action. + */ + public NestedSet getRuntimeInputs() { + return this.runtimeInputs; + } + + /** + * Returns compilation inputs for this link action. + */ + public final NestedSetBuilder getCompilationInputs() { + return this.compilationInputs; + } + + /** + * Returns linkstamps for this link action. + */ + public final Set getLinkstamps() { + return this.linkstamps; + } + /** + * Returns linkstamp options for this link action. + */ + public List getLinkstampOptions() { + return this.linkstampOptions; + } + + /** + * Returns command line options for this link action. + */ + public final List 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 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 createLTOArtifacts( + PathFragment ltoOutputRootPrefix, NestedSet uniqueLibraries) { + Set 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 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 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 buildInfoHeaderArtifacts = + !linkstamps.isEmpty() + ? analysisEnvironment.getBuildInfo(ruleContext, CppBuildInfo.KEY, configuration) + : ImmutableList.of(); + + boolean needWholeArchive = + wholeArchive + || needWholeArchive(linkStaticness, linkType, linkopts, isNativeDeps, cppConfiguration); + + NestedSet uniqueLibraries = libraries.build(); + final Iterable filteredNonLibraryArtifacts = + CppLinkAction.filterLinkerInputArtifacts(LinkerInputs.toLibraryArtifacts(nonLibraries)); + + final Iterable linkerInputs = + IterablesChain.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 features = + (ruleContext == null) ? ImmutableSet.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 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 actionOutputs; + if (isLTOIndexing) { + ImmutableList.Builder 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 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 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 expandedInputs = + LinkerInputs.toLibraryArtifacts( + Link.mergeInputsDependencies( + uniqueLibraries, needWholeArchive, cppConfiguration.archiveType())); + Iterable 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 ltoMapping = new HashMap<>(); + for (LTOBackendArtifacts a : allLTOArtifacts) { + ltoMapping.put(a.getBitcodeFile(), a.getObjectFile()); + } + + // Handle libraries. + List renamedInputs = new ArrayList<>(); + for (Artifact a : expandedInputs) { + Artifact renamed = ltoMapping.get(a); + renamedInputs.add(renamed == null ? a : renamed); + } + expandedInputs = renamedInputs; + + // Handle non-libraries. + List 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 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 inputsBuilder = + IterablesChain.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 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 executionRequirements = ImmutableSet.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 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 constructOutputs( + Artifact primaryOutput, Collection outputList, Artifact... outputs) { + return new ImmutableList.Builder() + .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 mapLinkstampsToOutputs( + Collection linkstamps, + RuleContext ruleContext, + BuildConfiguration configuration, + Artifact outputBinary, + LinkArtifactFactory linkArtifactFactory) { + ImmutableMap.Builder 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 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. + * + *

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 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 inputs) { + this.compilationInputs.addAll(inputs); + return this; + } + + public CppLinkActionBuilder addTransitiveCompilationInputs(NestedSet 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 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 inputs) { + for (Artifact input : inputs) { + addNonLibraryInput(LinkerInputs.simpleLinkerInput(input)); + } + return this; + } + + public CppLinkActionBuilder addFakeNonLibraryInputs(Iterable 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 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. + * + *

Link stamps are also automatically added to the inputs. + */ + public CppLinkActionBuilder addLinkstamps(Map> linkstamps) { + this.linkstamps.addAll(linkstamps.keySet()); + // Add inputs for linkstamping. + if (!linkstamps.isEmpty()) { + addTransitiveCompilationInputs(toolchain.getCompile()); + for (Map.Entry> entry : linkstamps.entrySet()) { + addCompilationInputs(entry.getValue()); + } + } + return this; + } + + public CppLinkActionBuilder addLinkstampCompilerOptions(ImmutableList 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 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. + * + *

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

(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<name>.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()); -- cgit v1.2.3