diff options
author | 2016-08-03 16:12:21 +0000 | |
---|---|---|
committer | 2016-08-03 17:16:01 +0000 | |
commit | 84de2cc636947aaa41fdec6c2d30232faeaabbdd (patch) | |
tree | 903b87d6ffc03b07fa6bbbf92304c8e5e2d9d0ec /src/main | |
parent | 3437d2f89730b72dfe89284a007798c67f75b162 (diff) |
Refactor CppLinkAction to construct its command line using the crosstool instead of a hardcoded switch. Add action configs to CppConfiguration for each link type.
--
MOS_MIGRATED_REVID=129221108
Diffstat (limited to 'src/main')
7 files changed, 1029 insertions, 595 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 a763713938..f25fc94e79 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 @@ -228,10 +228,6 @@ public abstract class CcBinary implements RuleConfiguredTargetFactory { linkActionBuilder.setLinkStaticness(linkStaticness); linkActionBuilder.setFake(fake); linkActionBuilder.setFeatureConfiguration(featureConfiguration); - CcToolchainFeatures.Variables.Builder buildVariables = - new CcToolchainFeatures.Variables.Builder(); - buildVariables.addAllVariables(CppHelper.getToolchain(ruleContext).getBuildVariables()); - linkActionBuilder.setBuildVariables(buildVariables.build()); if (CppLinkAction.enableSymbolsCounts(cppConfiguration, fake, linkType)) { linkActionBuilder.setSymbolCountsOutput(ruleContext.getPackageRelativeArtifact( diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainFeatures.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainFeatures.java index 9ba846a701..b6f2716f88 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainFeatures.java +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainFeatures.java @@ -32,8 +32,6 @@ import com.google.devtools.build.lib.analysis.config.InvalidConfigurationExcepti import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; import com.google.devtools.build.lib.vfs.PathFragment; import com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig.CToolchain; -import com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig.CToolchain.Tool; - import java.io.IOException; import java.io.ObjectInputStream; import java.io.Serializable; @@ -756,15 +754,17 @@ public class CcToolchainFeatures implements Serializable { */ @Immutable public static class Variables { - + + /** An empty variables instance. */ + public static final Variables EMPTY = new Variables.Builder().build(); + /** - * Variables can be set as an arbitrarily deeply nested recursive sequence, which - * we represent as a tree of {@code Sequence} nodes. - * The nodes are {@code NestedSequence} objects, while the leafs are {@code ValueSequence} - * objects. We do not allow {@code Value} objects in the tree, as the object memory overhead - * is too large when we have millions of values. - * If we find single element {@code ValueSequence} in memory profiles in the future, we - * can introduce another special case type. + * Variables can be set as an arbitrarily deeply nested recursive sequence, which we represent + * as a tree of {@code Sequence} nodes. The nodes are {@code NestedSequence} objects, while the + * leafs are {@code ValueSequence} objects. We do not allow {@code Value} objects in the tree, + * as the object memory overhead is too large when we have millions of values. If we find single + * element {@code ValueSequence} in memory profiles in the future, we can introduce another + * special case type. */ interface Sequence { @@ -1089,10 +1089,8 @@ public class CcToolchainFeatures implements Serializable { return new NestedView(viewMap, sequenceName, sequenceVariables.get(sequenceName)); } - /** - * Returns whether {@code variable} is set. - */ - private boolean isAvailable(String variable) { + /** Returns whether {@code variable} is set. */ + boolean isAvailable(String variable) { return variables.containsKey(variable) || sequenceVariables.containsKey(variable); } } diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppConfiguration.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppConfiguration.java index a02c7a92e5..1ba5a38807 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppConfiguration.java +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppConfiguration.java @@ -38,6 +38,7 @@ import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; import com.google.devtools.build.lib.events.Event; import com.google.devtools.build.lib.events.EventHandler; import com.google.devtools.build.lib.rules.cpp.CppConfigurationLoader.CppConfigurationParameters; +import com.google.devtools.build.lib.rules.cpp.CppLinkActionConfigs.CppLinkPlatform; import com.google.devtools.build.lib.rules.cpp.Link.LinkTargetType; import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable; import com.google.devtools.build.lib.skylarkinterface.SkylarkModule; @@ -53,7 +54,6 @@ import com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig.LipoM import com.google.devtools.common.options.OptionsParsingException; import com.google.protobuf.TextFormat; import com.google.protobuf.TextFormat.ParseException; - import java.io.Serializable; import java.util.ArrayList; import java.util.EnumSet; @@ -673,6 +673,7 @@ public class CppConfiguration extends BuildConfiguration.Fragment { return result.build(); } + private boolean linkActionsAreConfigured(CToolchain toolchain) { for (LinkTargetType type : LinkTargetType.values()) { boolean typeIsConfigured = false; @@ -703,6 +704,17 @@ public class CppConfiguration extends BuildConfiguration.Fragment { return toolchain; } try { + + if (!linkActionsAreConfigured(toolchain)) { + if (getTargetLibc().equals("macosx")) { + TextFormat.merge( + CppLinkActionConfigs.getCppLinkActionConfigs(CppLinkPlatform.MAC), toolchainBuilder); + } else { + TextFormat.merge( + CppLinkActionConfigs.getCppLinkActionConfigs(CppLinkPlatform.LINUX), + toolchainBuilder); + } + } if (!features.contains("dependency_file")) { // Gcc options: // -MD turns on .d file output as a side-effect (doesn't imply -E) @@ -847,12 +859,8 @@ public class CppConfiguration extends BuildConfiguration.Fragment { + " flag_set {" + " action: 'c-compile'" + " action: 'c++-compile'" - + " action: 'c++-link-static-library'" - + " action: 'c++-link-pic-static-library'" + " action: 'c++-link-interface-dynamic-library'" + " action: 'c++-link-dynamic-library'" - + " action: 'c++-link-alwayslink-static-library'" - + " action: 'c++-link-alwayslink-pic-static-library'" + " action: 'c++-link-executable'" + " flag_group {" + " flag: '-fprofile-generate=%{fdo_instrument_path}'" @@ -938,12 +946,8 @@ public class CppConfiguration extends BuildConfiguration.Fragment { + " }" + " }" + " flag_set {" - + " action: 'c++-link-static-library'" - + " action: 'c++-link-pic-static-library'" + " action: 'c++-link-interface-dynamic-library'" + " action: 'c++-link-dynamic-library'" - + " action: 'c++-link-always-link-static-library'" - + " action: 'c++-link-always-link-pic-static-library'" + " action: 'c++-link-executable'" + " flag_group {" + " flag: '-lgcov'" 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 index 3ec9843777..bde2ee0ce3 100644 --- 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 @@ -17,6 +17,7 @@ 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.base.Strings; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; @@ -37,6 +38,8 @@ 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.CcToolchainFeatures.Variables; +import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.Variables.VariablesExtension; 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; @@ -45,7 +48,6 @@ 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; @@ -53,11 +55,57 @@ 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 { + + /** A build variable for clang flags that set the root of the linker search path. */ + public static final String RUNTIME_ROOT_FLAGS_VARIABLE = "runtime_root_flags"; + + /** A build variable for entries in the linker search path. */ + public static final String RUNTIME_ROOT_ENTRIES_VARIABLE = "runtime_root_entries"; + + /** + * A build variable for options applying to specific libraries in the linker invocation that + * either identify a library to be linked or add a directory to the runtime library search path. + */ + public static final String LIBOPTS_VARIABLE = "libopts"; + + /** + * A build variable for flags providing files to link as inputs in the linker invocation that + * should not go in a -whole_archive block. + */ + public static final String LINKER_INPUT_PARAMS_VARIABLE = "linker_input_params"; + + /** + * A build variable for flags providing files to link as inputs in the linker invocation that + * should go in a -whole_archive block. + */ + public static final String WHOLE_ARCHIVE_LINKER_INPUT_PARAMS_VARIABLE = + "whole_archive_linker_params"; + + /** A build variable whose presence indicates that whole archive flags should be applied. */ + public static final String GLOBAL_WHOLE_ARCHIVE_VARIABLE = "global_whole_archive"; + + /** A build variable for the execpath of the output of the linker. */ + public static final String OUTPUT_EXECPATH_VARIABLE = "output_execpath"; + + /** + * A build variable that is set to indicate a mostly static linking for which the linked binary + * should be piped to /dev/null. + */ + public static final String SKIP_MOSTLY_STATIC_VARIABLE = "skip_mostly_static"; + + /** A build variable giving a path to which to write symbol counts. */ + public static final String SYMBOL_COUNTS_OUTPUT_VARIABLE = "symbol_counts_output"; + + /** A build variable giving linkstamp paths. */ + public static final String LINKSTAMP_PATHS_VARIABLE = "linkstamp_paths"; + + /** A build variable whose presence indicates that PIC code should be generated. */ + public static final String FORCE_PIC_VARIABLE = "force_pic"; + // Builder-only // Null when invoked from tests (e.g. via createTestBuilder). @Nullable private final RuleContext ruleContext; @@ -72,8 +120,6 @@ public class CppLinkActionBuilder { 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}. @@ -196,10 +242,8 @@ public class CppLinkActionBuilder { private String getActionName() { return linkType.getActionName(); } - - /** - * Returns linker inputs that are not libraries. - */ + + /** Returns linker inputs that are not libraries. */ public Set<LinkerInput> getNonLibraries() { return nonLibraries; } @@ -401,6 +445,17 @@ public class CppLinkActionBuilder { final ImmutableSet<String> features = (ruleContext == null) ? ImmutableSet.<String>of() : ruleContext.getFeatures(); + // For backwards compatibility, and for tests, we permit the link action to be + // instantiated without a feature configuration. + if (featureConfiguration == null) { + if (toolchain != null) { + featureConfiguration = + CcCommon.configureFeatures(ruleContext, toolchain, CcLibraryHelper.SourceCategory.CC); + } else { + featureConfiguration = CcCommon.configureFeatures(ruleContext); + } + } + final LibraryToLink outputLibrary = LinkerInputs.newInputLibrary(output, filteredNonLibraryArtifacts, this.ltoBitcodeFiles); final LibraryToLink interfaceOutputLibrary = @@ -436,6 +491,24 @@ public class CppLinkActionBuilder { symbolCounts); } + ImmutableList<LinkerInput> runtimeLinkerInputs = + ImmutableList.copyOf(LinkerInputs.simpleLinkerInputs(runtimeInputs)); + + // Add build variables necessary to template link args into the crosstool. + Variables.Builder buildVariablesBuilder = new Variables.Builder(); + CppLinkVariablesExtension variablesExtension = + isLTOIndexing + ? new CppLinkVariablesExtension( + linkstampMap, needWholeArchive, linkerInputs, runtimeLinkerInputs, null) + : new CppLinkVariablesExtension( + linkstampMap, + needWholeArchive, + linkerInputs, + runtimeLinkerInputs, + outputLibrary.getArtifact()); + variablesExtension.addVariables(buildVariablesBuilder); + Variables buildVariables = buildVariablesBuilder.build(); + PathFragment paramRootPath = ParameterFile.derivePath( outputLibrary.getArtifact().getRootRelativePath(), (isLTOIndexing) ? "lto" : "2"); @@ -446,28 +519,52 @@ public class CppLinkActionBuilder { ? linkArtifactFactory.create(ruleContext, configuration, paramRootPath) : null; + Preconditions.checkArgument( + linkType != LinkTargetType.INTERFACE_DYNAMIC_LIBRARY, + "you can't link an interface dynamic library directly"); + if (linkType != LinkTargetType.DYNAMIC_LIBRARY) { + Preconditions.checkArgument( + interfaceOutput == null, + "interface output may only be non-null for dynamic library links"); + } + if (linkType.isStaticLibraryLink()) { + // solib dir must be null for static links + runtimeSolibDir = null; + + Preconditions.checkArgument( + linkStaticness == LinkStaticness.FULLY_STATIC, "static library link must be static"); + Preconditions.checkArgument( + symbolCounts == null, "the symbol counts output must be null for static links"); + Preconditions.checkArgument( + !isNativeDeps, "the native deps flag must be false for static links"); + Preconditions.checkArgument( + !needWholeArchive, "the need whole archive flag must be false for static links"); + } + LinkCommandLine.Builder linkCommandLineBuilder = new LinkCommandLine.Builder(configuration, getOwner(), ruleContext) - .setActionName(getActionName()) .setLinkerInputs(linkerInputs) - .setRuntimeInputs(ImmutableList.copyOf(LinkerInputs.simpleLinkerInputs(runtimeInputs))) + .setRuntimeInputs(runtimeLinkerInputs) .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) + .setBuildVariables(buildVariables) .setFeatureConfiguration(featureConfiguration); + // TODO(b/30228443): Refactor noWholeArchiveInputs into action_configs, and remove this. + if (needWholeArchive) { + linkCommandLineBuilder.setNoWholeArchiveFlags(variablesExtension.getNoWholeArchiveInputs()); + } + if (!isLTOIndexing) { linkCommandLineBuilder .setOutput(outputLibrary.getArtifact()) .setInterfaceOutput(interfaceOutput) - .setSymbolCountsOutput(symbolCounts) .setBuildInfoHeaderArtifacts(buildInfoHeaderArtifacts) .setInterfaceSoBuilder(getInterfaceSoBuilder()) .setLinkstamps(linkstampMap) @@ -506,13 +603,6 @@ public class CppLinkActionBuilder { 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( @@ -545,7 +635,7 @@ public class CppLinkActionBuilder { for (LTOBackendArtifacts a : allLTOArtifacts) { List<String> argv = new ArrayList<>(); argv.addAll(cppConfiguration.getLinkOptions()); - argv.addAll(featureConfiguration.getCommandLine(getActionName(), buildVariables)); + argv.addAll(featureConfiguration.getCommandLine(getActionName(), Variables.EMPTY)); argv.addAll(cppConfiguration.getCompilerOptions(features)); a.setCommandLine(argv); } @@ -684,12 +774,6 @@ public class CppLinkActionBuilder { 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. * @@ -936,4 +1020,439 @@ public class CppLinkActionBuilder { this.runtimeSolibDir = runtimeSolibDir; return this; } + + private static class LinkArgCollector { + String rpathRoot; + List<String> rpathEntries; + Set<String> libopts; + List<String> linkerInputParams; + List<String> wholeArchiveLinkerInputParams; + List<String> noWholeArchiveInputs; + + public void setRpathRoot(String rPathRoot) { + this.rpathRoot = rPathRoot; + } + + public void setRpathEntries(List<String> rpathEntries) { + this.rpathEntries = rpathEntries; + } + + public void setLibopts(Set<String> libopts) { + this.libopts = libopts; + } + + public void setLinkerInputParams(List<String> linkerInputParams) { + this.linkerInputParams = linkerInputParams; + } + + public void setWholeArchiveLinkerInputParams(List<String> wholeArchiveInputParams) { + this.wholeArchiveLinkerInputParams = wholeArchiveInputParams; + } + + public void setNoWholeArchiveInputs(List<String> noWholeArchiveInputs) { + this.noWholeArchiveInputs = noWholeArchiveInputs; + } + + public String getRpathRoot() { + return rpathRoot; + } + + public List<String> getRpathEntries() { + return rpathEntries; + } + + public Set<String> getLibopts() { + return libopts; + } + + public List<String> getLinkerInputParams() { + return linkerInputParams; + } + + public List<String> getWholeArchiveLinkerInputParams() { + return wholeArchiveLinkerInputParams; + } + + public List<String> getNoWholeArchiveInputs() { + return noWholeArchiveInputs; + } + } + + private class CppLinkVariablesExtension implements VariablesExtension { + + private final ImmutableMap<Artifact, Artifact> linkstampMap; + private final boolean needWholeArchive; + private final Iterable<LinkerInput> linkerInputs; + private final ImmutableList<LinkerInput> runtimeLinkerInputs; + private final Artifact outputArtifact; + + private final LinkArgCollector linkArgCollector = new LinkArgCollector(); + + public CppLinkVariablesExtension( + ImmutableMap<Artifact, Artifact> linkstampMap, + boolean needWholeArchive, + Iterable<LinkerInput> linkerInputs, + ImmutableList<LinkerInput> runtimeLinkerInputs, + Artifact output) { + this.linkstampMap = linkstampMap; + this.needWholeArchive = needWholeArchive; + this.linkerInputs = linkerInputs; + this.runtimeLinkerInputs = runtimeLinkerInputs; + this.outputArtifact = output; + + addInputFileLinkOptions(linkArgCollector); + } + + /** + * Returns linker parameters indicating libraries that should not be linked inside a + * --whole_archive block. + * + * <p>TODO(b/30228443): Refactor into action configs + */ + public List<String> getNoWholeArchiveInputs() { + return linkArgCollector.getNoWholeArchiveInputs(); + } + + @Override + public void addVariables(Variables.Builder buildVariables) { + + // symbol counting + if (symbolCounts != null) { + buildVariables.addVariable(SYMBOL_COUNTS_OUTPUT_VARIABLE, symbolCounts.getExecPathString()); + } + + // linkstamp + ImmutableSet.Builder<String> linkstampPaths = ImmutableSet.<String>builder(); + for (Artifact linkstampOutput : linkstampMap.values()) { + linkstampPaths.add(linkstampOutput.getExecPathString()); + } + + buildVariables.addSequenceVariable(LINKSTAMP_PATHS_VARIABLE, linkstampPaths.build()); + + // pic + boolean forcePic = cppConfiguration.forcePic(); + if (forcePic) { + buildVariables.addVariable(FORCE_PIC_VARIABLE, ""); + } + + // rpath + if (linkArgCollector.getRpathRoot() != null) { + buildVariables.addVariable(RUNTIME_ROOT_FLAGS_VARIABLE, linkArgCollector.getRpathRoot()); + } + + if (linkArgCollector.getRpathEntries() != null) { + buildVariables.addSequenceVariable( + RUNTIME_ROOT_ENTRIES_VARIABLE, linkArgCollector.getRpathEntries()); + } + + buildVariables.addSequenceVariable(LIBOPTS_VARIABLE, linkArgCollector.getLibopts()); + buildVariables.addSequenceVariable( + LINKER_INPUT_PARAMS_VARIABLE, linkArgCollector.getLinkerInputParams()); + buildVariables.addSequenceVariable( + WHOLE_ARCHIVE_LINKER_INPUT_PARAMS_VARIABLE, + linkArgCollector.getWholeArchiveLinkerInputParams()); + + // global archive + if (needWholeArchive) { + buildVariables.addVariable(GLOBAL_WHOLE_ARCHIVE_VARIABLE, ""); + } + + // mostly static + if (linkStaticness == LinkStaticness.MOSTLY_STATIC && cppConfiguration.skipStaticOutputs()) { + buildVariables.addVariable(SKIP_MOSTLY_STATIC_VARIABLE, ""); + } + + // output exec path + if (this.outputArtifact != null) { + buildVariables.addVariable( + OUTPUT_EXECPATH_VARIABLE, this.outputArtifact.getExecPathString()); + } + + // Variables arising from the toolchain + buildVariables + .addAllVariables(CppHelper.getToolchain(ruleContext).getBuildVariables()) + .build(); + CppHelper.getFdoSupport(ruleContext).getLinkOptions(featureConfiguration, buildVariables); + } + + private boolean isDynamicLibrary(LinkerInput linkInput) { + Artifact libraryArtifact = linkInput.getArtifact(); + String name = libraryArtifact.getFilename(); + return Link.SHARED_LIBRARY_FILETYPES.matches(name) && name.startsWith("lib"); + } + + private boolean isSharedNativeLibrary() { + return isNativeDeps && cppConfiguration.shareNativeDeps(); + } + + /** + * When linking a shared library fully or mostly static then we need to link in *all* dependent + * files, not just what the shared library needs for its own code. This is done by wrapping all + * objects/libraries with -Wl,-whole-archive and -Wl,-no-whole-archive. For this case the + * globalNeedWholeArchive parameter must be set to true. Otherwise only library objects (.lo) + * need to be wrapped with -Wl,-whole-archive and -Wl,-no-whole-archive. + * + * <p>TODO: Factor out of the bazel binary into build variables for crosstool action_configs. + */ + private void addInputFileLinkOptions(LinkArgCollector linkArgCollector) { + + // Used to collect -L and -Wl,-rpath options, ensuring that each used only once. + Set<String> libOpts = new LinkedHashSet<>(); + + // List of command line parameters to link input files (either directly or using -l). + List<String> linkerInputParameters = new ArrayList<>(); + + // List of command line parameters that need to be placed *outside* of + // --whole-archive ... --no-whole-archive. + List<String> noWholeArchiveInputs = new ArrayList<>(); + + PathFragment solibDir = + configuration + .getBinDirectory() + .getExecPath() + .getRelative(cppConfiguration.getSolibDirectory()); + String runtimeSolibName = runtimeSolibDir != null ? runtimeSolibDir.getBaseName() : null; + boolean runtimeRpath = + runtimeSolibDir != null + && (linkType == LinkTargetType.DYNAMIC_LIBRARY + || (linkType == LinkTargetType.EXECUTABLE + && linkStaticness == LinkStaticness.DYNAMIC)); + + String rpathRoot = null; + List<String> runtimeRpathEntries = new ArrayList<>(); + + if (output != null) { + String origin = + useTestOnlyFlags && cppConfiguration.supportsExecOrigin() + ? "$EXEC_ORIGIN/" + : "$ORIGIN/"; + if (runtimeRpath) { + runtimeRpathEntries.add("-Wl,-rpath," + origin + runtimeSolibName + "/"); + } + + // Calculate the correct relative value for the "-rpath" link option (which sets + // the search path for finding shared libraries). + if (isSharedNativeLibrary()) { + // For shared native libraries, special symlinking is applied to ensure C++ + // runtimes are available under $ORIGIN/_solib_[arch]. So we set the RPATH to find + // them. + // + // Note that we have to do this because $ORIGIN points to different paths for + // different targets. In other words, blaze-bin/d1/d2/d3/a_shareddeps.so and + // blaze-bin/d4/b_shareddeps.so have different path depths. The first could + // reference a standard blaze-bin/_solib_[arch] via $ORIGIN/../../../_solib[arch], + // and the second could use $ORIGIN/../_solib_[arch]. But since this is a shared + // artifact, both are symlinks to the same place, so + // there's no *one* RPATH setting that fits all targets involved in the sharing. + rpathRoot = + "-Wl,-rpath," + origin + ":" + origin + cppConfiguration.getSolibDirectory() + "/"; + if (runtimeRpath) { + runtimeRpathEntries.add("-Wl,-rpath," + origin + "../" + runtimeSolibName + "/"); + } + } else { + // For all other links, calculate the relative path from the output file to _solib_[arch] + // (the directory where all shared libraries are stored, which resides under the blaze-bin + // directory. In other words, given blaze-bin/my/package/binary, rpathRoot would be + // "../../_solib_[arch]". + if (runtimeRpath) { + runtimeRpathEntries.add( + "-Wl,-rpath," + + origin + + Strings.repeat("../", output.getRootRelativePath().segmentCount() - 1) + + runtimeSolibName + + "/"); + } + + rpathRoot = + "-Wl,-rpath," + + origin + + Strings.repeat("../", output.getRootRelativePath().segmentCount() - 1) + + cppConfiguration.getSolibDirectory() + + "/"; + + if (isNativeDeps) { + // We also retain the $ORIGIN/ path to solibs that are in _solib_<arch>, as opposed to + // the package directory) + if (runtimeRpath) { + runtimeRpathEntries.add("-Wl,-rpath," + origin + "../" + runtimeSolibName + "/"); + } + rpathRoot += ":" + origin; + } + } + } + + boolean includeSolibDir = false; + + Map<Artifact, Artifact> ltoMap = null; + if (!isLTOIndexing && (allLTOArtifacts != null)) { + // TODO(bazel-team): The LTO final link can only work if there are individual .o files on + // the command line. Rather than crashing, this should issue a nice error. We will get + // this by + // 1) moving supports_start_end_lib to a toolchain feature + // 2) having thin_lto require start_end_lib + // As a bonus, we can rephrase --nostart_end_lib as --features=-start_end_lib and get rid + // of a command line option. + + Preconditions.checkState(cppConfiguration.useStartEndLib()); + ltoMap = new HashMap<>(); + for (LTOBackendArtifacts l : allLTOArtifacts) { + ltoMap.put(l.getBitcodeFile(), l.getObjectFile()); + } + } + + for (LinkerInput input : linkerInputs) { + if (isDynamicLibrary(input)) { + PathFragment libDir = input.getArtifact().getExecPath().getParentDirectory(); + Preconditions.checkState( + libDir.startsWith(solibDir), + "Artifact '%s' is not under directory '%s'.", + input.getArtifact(), + solibDir); + if (libDir.equals(solibDir)) { + includeSolibDir = true; + } + addDynamicInputLinkOptions(input, linkerInputParameters, libOpts, solibDir, rpathRoot); + } else { + addStaticInputLinkOptions(input, linkerInputParameters, ltoMap); + } + } + + boolean includeRuntimeSolibDir = false; + + for (LinkerInput input : runtimeLinkerInputs) { + List<String> optionsList = needWholeArchive ? noWholeArchiveInputs : linkerInputParameters; + + if (isDynamicLibrary(input)) { + PathFragment libDir = input.getArtifact().getExecPath().getParentDirectory(); + Preconditions.checkState( + runtimeSolibDir != null && libDir.equals(runtimeSolibDir), + "Artifact '%s' is not under directory '%s'.", + input.getArtifact(), + solibDir); + includeRuntimeSolibDir = true; + addDynamicInputLinkOptions(input, optionsList, libOpts, solibDir, rpathRoot); + } else { + addStaticInputLinkOptions(input, optionsList, ltoMap); + } + } + + // rpath ordering matters for performance; first add the one where most libraries are found. + if (includeSolibDir && rpathRoot != null) { + linkArgCollector.setRpathRoot(rpathRoot); + } + if (includeRuntimeSolibDir) { + linkArgCollector.setRpathEntries(runtimeRpathEntries); + } + + linkArgCollector.setLibopts(libOpts); + + ImmutableList.Builder<String> wholeArchiveInputParams = ImmutableList.builder(); + ImmutableList.Builder<String> standardArchiveInputParams = ImmutableList.builder(); + for (String param : linkerInputParameters) { + if (!wholeArchive && Link.LINK_LIBRARY_FILETYPES.matches(param) && !needWholeArchive) { + wholeArchiveInputParams.add(param); + } else { + standardArchiveInputParams.add(param); + } + } + + linkArgCollector.setLinkerInputParams(standardArchiveInputParams.build()); + linkArgCollector.setWholeArchiveLinkerInputParams(wholeArchiveInputParams.build()); + linkArgCollector.setNoWholeArchiveInputs(noWholeArchiveInputs); + + if (ltoMap != null) { + Preconditions.checkState(ltoMap.isEmpty(), "Still have LTO objects left: %s", ltoMap); + } + } + + /** Adds command-line options for a dynamic library input file into options and libOpts. */ + private void addDynamicInputLinkOptions( + LinkerInput input, + List<String> options, + Set<String> libOpts, + PathFragment solibDir, + String rpathRoot) { + Preconditions.checkState(isDynamicLibrary(input)); + Preconditions.checkState(!Link.useStartEndLib(input, cppConfiguration.archiveType())); + + Artifact inputArtifact = input.getArtifact(); + PathFragment libDir = inputArtifact.getExecPath().getParentDirectory(); + if (rpathRoot != null + && !libDir.equals(solibDir) + && (runtimeSolibDir == null || !runtimeSolibDir.equals(libDir))) { + String dotdots = ""; + PathFragment commonParent = solibDir; + while (!libDir.startsWith(commonParent)) { + dotdots += "../"; + commonParent = commonParent.getParentDirectory(); + } + + libOpts.add(rpathRoot + dotdots + libDir.relativeTo(commonParent).getPathString()); + } + + libOpts.add("-L" + inputArtifact.getExecPath().getParentDirectory().getPathString()); + + String name = inputArtifact.getFilename(); + if (CppFileTypes.SHARED_LIBRARY.matches(name)) { + String libName = name.replaceAll("(^lib|\\.(so|dylib)$)", ""); + options.add("-l" + libName); + } else { + // Interface shared objects have a non-standard extension + // that the linker won't be able to find. So use the + // filename directly rather than a -l option. Since the + // library has an SONAME attribute, this will work fine. + options.add(inputArtifact.getExecPathString()); + } + } + + /** + * Adds command-line options for a static library or non-library input into options. + * + * @param ltoMap is a mutable list of exec paths that should be on the command-line, which must + * be supplied for LTO final links. + */ + private void addStaticInputLinkOptions( + LinkerInput input, List<String> options, @Nullable Map<Artifact, Artifact> ltoMap) { + Preconditions.checkState(!isDynamicLibrary(input)); + + // start-lib/end-lib library: adds its input object files. + if (Link.useStartEndLib(input, cppConfiguration.archiveType())) { + Iterable<Artifact> archiveMembers = input.getObjectFiles(); + if (!Iterables.isEmpty(archiveMembers)) { + options.add("-Wl,--start-lib"); + for (Artifact member : archiveMembers) { + if (ltoMap != null) { + Artifact backend = ltoMap.remove(member); + + if (backend != null) { + // If the backend artifact is missing, we can't print a warning because this may + // happen normally, due libraries that list .o files explicitly, or generate .o + // files from assembler. + member = backend; + } + } + + options.add(member.getExecPathString()); + } + options.add("-Wl,--end-lib"); + } + } else { + // For anything else, add the input directly. + Artifact inputArtifact = input.getArtifact(); + + if (ltoMap != null) { + Artifact ltoArtifact = ltoMap.remove(inputArtifact); + if (ltoArtifact != null) { + inputArtifact = ltoArtifact; + } + } + + if (input.isFake()) { + options.add(Link.FAKE_OBJECT_PREFIX + inputArtifact.getExecPathString()); + } else { + options.add(inputArtifact.getExecPathString()); + } + } + } + } } diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppLinkActionConfigs.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppLinkActionConfigs.java new file mode 100644 index 0000000000..a359350834 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppLinkActionConfigs.java @@ -0,0 +1,297 @@ +// Copyright 2014 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.lib.rules.cpp; + +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableList; + +/** + * A helper class for creating action_configs for the c++ link action. + * + * <p>TODO(b/30109612): Replace this with action_configs in the crosstool instead of putting it in + * legacy features. + */ +public class CppLinkActionConfigs { + + /** A platform for linker invocations. */ + public static enum CppLinkPlatform { + LINUX, + MAC + } + + public static String getCppLinkActionConfigs(CppLinkPlatform platform) { + return Joiner.on("\n") + .join( + ImmutableList.of( + "action_config {", + " config_name: 'c++-link-executable'", + " action_name: 'c++-link-executable'", + " tool {", + " tool_path: 'DUMMY_TOOL'", + " }", + " implies: 'symbol_counts'", + " implies: 'linkstamps'", + " implies: 'output_execpath_flags_executable'", + " implies: 'global_whole_archive_open'", + " implies: 'runtime_root_flags'", + " implies: 'input_param_flags'", + " implies: 'global_whole_archive_close'", + " implies: 'force_pic_flags'", + "}", + "action_config {", + " config_name: 'c++-link-dynamic-library'", + " action_name: 'c++-link-dynamic-library'", + " tool {", + " tool_path: 'DUMMY_TOOL'", + " }", + " implies: 'symbol_counts'", + " implies: 'shared_flag'", + " implies: 'linkstamps'", + " implies: 'output_execpath_flags'", + " implies: 'global_whole_archive_open'", + " implies: 'runtime_root_flags'", + " implies: 'input_param_flags'", + " implies: 'global_whole_archive_close'", + "}", + "action_config {", + " config_name: 'c++-link-static-library'", + " action_name: 'c++-link-static-library'", + " tool {", + " tool_path: 'DUMMY_TOOL'", + " }", + " implies: 'global_whole_archive_open'", + " implies: 'runtime_root_flags'", + " implies: 'input_param_flags'", + " implies: 'global_whole_archive_close'", + "}", + "action_config {", + " config_name: 'c++-link-alwayslink-static-library'", + " action_name: 'c++-link-alwayslink-static-library'", + " tool {", + " tool_path: 'DUMMY_TOOL'", + " }", + " implies: 'global_whole_archive_open'", + " implies: 'runtime_root_flags'", + " implies: 'input_param_flags'", + " implies: 'global_whole_archive_close'", + "}", + "action_config {", + " config_name: 'c++-link-pic-static-library'", + " action_name: 'c++-link-pic-static-library'", + " tool {", + " tool_path: 'DUMMY_TOOL'", + " }", + " implies: 'global_whole_archive_open'", + " implies: 'runtime_root_flags'", + " implies: 'input_param_flags'", + " implies: 'global_whole_archive_close'", + "}", + "action_config {", + " config_name: 'c++-link-alwayslink-pic-static-library'", + " action_name: 'c++-link-alwayslink-pic-static-library'", + " tool {", + " tool_path: 'DUMMY_TOOL'", + " }", + " implies: 'global_whole_archive_open'", + " implies: 'runtime_root_flags'", + " implies: 'input_param_flags'", + " implies: 'global_whole_archive_close'", + "}", + "feature {", + " name: 'symbol_counts'", + " flag_set {", + " expand_if_all_available: 'symbol_counts_output'", + " action: 'c++-link-executable'", + " action: 'c++-link-dynamic-library'", + " flag_group {", + " flag: '-Wl,--print-symbol-counts=%{symbol_counts_output}'", + " }", + " }", + "}", + "feature {", + " name: 'shared_flag'", + " flag_set {", + " action: 'c++-link-dynamic-library'", + " flag_group {", + " flag: '-shared'", + " }", + " }", + "}", + "feature {", + " name: 'linkstamps'", + " flag_set {", + " action: 'c++-link-executable'", + " action: 'c++-link-dynamic-library'", + " expand_if_all_available: 'linkstamp_paths'", + " flag_group {", + " flag: '%{linkstamp_paths}'", + " }", + " }", + "}", + "feature {", + " name: 'output_execpath_flags'", + " flag_set {", + " expand_if_all_available: 'output_execpath'", + " action: 'c++-link-dynamic-library'", + " flag_group {", + " flag: '-o'", + " flag: '%{output_execpath}'", + " }", + " }", + "}", + "feature {", + " name: 'output_execpath_flags_executable'", + " flag_set {", + " expand_if_all_available: 'output_execpath'", + " action: 'c++-link-executable'", + " flag_group {", + " flag: '-o'", + " }", + " }", + " flag_set {", + " expand_if_all_available: 'skip_mostly_static'", + " expand_if_all_available: 'output_execpath'", + " action: 'c++-link-executable'", + " flag_group {", + " flag: '/dev/null'", + " flag: '-MMD'", + " flag: '-MF'", + " }", + " }", + " flag_set {", + " expand_if_all_available: 'output_execpath'", + " action: 'c++-link-executable'", + " flag_group {", + " flag: '%{output_execpath}'", + " }", + " }", + "}", + "feature {", + " name: 'global_whole_archive_open'", + " flag_set {", + " expand_if_all_available: 'global_whole_archive'", + " action: 'c++-link-executable'", + " action: 'c++-link-dynamic-library'", + " action: 'c++-link-static-library'", + " action: 'c++-link-alwayslink-static-library'", + " action: 'c++-link-pic-static-library'", + " action: 'c++-link-alwayslink-pic-static-library'", + " flag_group {", + // TODO: Factor platform difference into respective linux and OSX crosstools. + String.format( + " flag:'%s'", + platform == CppLinkPlatform.LINUX ? "-Wl,-whole-archive" : "-Wl,-all_load"), + " }", + " }", + "}", + "feature {", + " name: 'runtime_root_flags',", + " flag_set {", + " expand_if_all_available: 'runtime_root_flags'", + " action: 'c++-link-executable'", + " action: 'c++-link-dynamic-library'", + " action: 'c++-link-static-library'", + " action: 'c++-link-alwayslink-static-library'", + " action: 'c++-link-pic-static-library'", + " action: 'c++-link-alwayslink-pic-static-library'", + " flag_group {", + " flag: '%{runtime_root_flags}'", + " }", + " }", + " flag_set {", + " expand_if_all_available: 'runtime_root_entries'", + " action: 'c++-link-executable'", + " action: 'c++-link-dynamic-library'", + " action: 'c++-link-static-library'", + " action: 'c++-link-alwayslink-static-library'", + " action: 'c++-link-pic-static-library'", + " action: 'c++-link-alwayslink-pic-static-library'", + " flag_group {", + " flag: '%{runtime_root_entries}'", + " }", + " }", + "}", + "feature {", + " name: 'input_param_flags'", + " flag_set {", + " expand_if_all_available: 'libopts'", + " action: 'c++-link-executable'", + " action: 'c++-link-dynamic-library'", + " action: 'c++-link-static-library'", + " action: 'c++-link-alwayslink-static-library'", + " action: 'c++-link-pic-static-library'", + " action: 'c++-link-alwayslink-pic-static-library'", + " flag_group {", + " flag: '%{libopts}'", + " }", + " }", + " flag_set {", + " expand_if_all_available: 'whole_archive_linker_params'", + " action: 'c++-link-executable'", + " action: 'c++-link-dynamic-library'", + " action: 'c++-link-static-library'", + " action: 'c++-link-alwayslink-static-library'", + " action: 'c++-link-pic-static-library'", + " action: 'c++-link-alwayslink-pic-static-library'", + " flag_group {", + platform == CppLinkPlatform.LINUX + ? " flag: '-Wl,-whole-archive'\n" + + " flag: '%{whole_archive_linker_params}'\n" + + " flag: '-Wl,-no-whole-archive'" + : " flag: '-Wl,-force_load,%{whole_archive_linker_params}'", + " }", + " }", + " flag_set {", + " expand_if_all_available: 'linker_input_params'", + " action: 'c++-link-executable'", + " action: 'c++-link-dynamic-library'", + " action: 'c++-link-static-library'", + " action: 'c++-link-alwayslink-static-library'", + " action: 'c++-link-pic-static-library'", + " action: 'c++-link-alwayslink-pic-static-library'", + " flag_group {", + " flag: '%{linker_input_params}'", + " }", + " }", + "}", + "feature {", + " name: 'global_whole_archive_close'", + " flag_set {", + " expand_if_all_available: 'global_whole_archive'", + " action: 'c++-link-executable'", + " action: 'c++-link-dynamic-library'", + " action: 'c++-link-static-library'", + " action: 'c++-link-alwayslink-static-library'", + " action: 'c++-link-pic-static-library'", + " action: 'c++-link-alwayslink-pic-static-library'", + " flag_group {", + // TODO: Factor platform difference into respective linux and OSX crosstools. + String.format( + " flag: '%s'", + platform == CppLinkPlatform.LINUX ? "-Wl,-no-whole-archive" : ""), + " }", + " }", + "}", + "feature {", + " name: 'force_pic_flags'", + " flag_set {", + " expand_if_all_available: 'force_pic'", + " action: 'c++-link-executable'", + " flag_group {", + " flag: '-pie'", + " }", + " }", + "}")); + } +} 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 d3cbeaf3e0..13866e81d5 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 @@ -41,7 +41,6 @@ import com.google.devtools.build.lib.util.FileType; import com.google.devtools.build.lib.util.Preconditions; import com.google.devtools.build.lib.vfs.FileSystemUtils; import com.google.devtools.build.lib.vfs.PathFragment; - import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashSet; @@ -49,7 +48,6 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Pattern; - import javax.annotation.Nullable; /** @@ -362,11 +360,6 @@ public final class CppModel { return result; } - private CcToolchainFeatures.Variables linkBuildVariables() { - return new CcToolchainFeatures.Variables.Builder() - .addAllVariables(CppHelper.getToolchain(ruleContext).getBuildVariables()).build(); - } - private void setupCompileBuildVariables( CppCompileActionBuilder builder, boolean usePic, @@ -844,7 +837,6 @@ public final class CppModel { .setLinkType(linkType) .setLinkStaticness(LinkStaticness.FULLY_STATIC) .setFeatureConfiguration(featureConfiguration) - .setBuildVariables(linkBuildVariables()) .build(); env.registerAction(maybePicAction); result.addStaticLibrary(maybePicAction.getOutputLibrary()); @@ -869,7 +861,6 @@ public final class CppModel { .setLinkType(picLinkType) .setLinkStaticness(LinkStaticness.FULLY_STATIC) .setFeatureConfiguration(featureConfiguration) - .setBuildVariables(linkBuildVariables()) .build(); env.registerAction(picAction); result.addPicStaticLibrary(picAction.getOutputLibrary()); @@ -913,8 +904,7 @@ public final class CppModel { .setRuntimeInputs( CppHelper.getToolchain(ruleContext).getDynamicRuntimeLinkMiddleman(), CppHelper.getToolchain(ruleContext).getDynamicRuntimeLinkInputs()) - .setFeatureConfiguration(featureConfiguration) - .setBuildVariables(linkBuildVariables()); + .setFeatureConfiguration(featureConfiguration); if (!ccOutputs.getLtoBitcodeFiles().isEmpty() && featureConfiguration.isEnabled(CppRuleClasses.THIN_LTO)) { diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/LinkCommandLine.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/LinkCommandLine.java index 96b5a3bb3c..c5cb3017ed 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/LinkCommandLine.java +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/LinkCommandLine.java @@ -17,7 +17,6 @@ package com.google.devtools.build.lib.rules.cpp; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Joiner; import com.google.common.base.Predicates; -import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; @@ -32,23 +31,18 @@ import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.collect.CollectionUtils; import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.FeatureConfiguration; +import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.Variables; 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.util.Pair; import com.google.devtools.build.lib.util.Preconditions; import com.google.devtools.build.lib.util.ShellEscaper; import com.google.devtools.build.lib.vfs.PathFragment; - import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedHashSet; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; - import javax.annotation.Nullable; /** @@ -66,7 +60,6 @@ public final class LinkCommandLine extends CommandLine { @Nullable private final FeatureConfiguration featureConfiguration; @Nullable private final Artifact output; @Nullable private final Artifact interfaceOutput; - @Nullable private final Artifact symbolCountsOutput; private final ImmutableList<Artifact> buildInfoHeaderArtifacts; private final Iterable<? extends LinkerInput> linkerInputs; private final Iterable<? extends LinkerInput> runtimeInputs; @@ -80,9 +73,8 @@ public final class LinkCommandLine extends CommandLine { @Nullable private final PathFragment runtimeSolibDir; private final boolean nativeDeps; private final boolean useTestOnlyFlags; - private final boolean needWholeArchive; + private final List<String> noWholeArchiveFlags; - @Nullable private final Iterable<LTOBackendArtifacts> allLTOArtifacts; @Nullable private final Artifact paramFile; @Nullable private final Artifact interfaceSoBuilder; @@ -92,7 +84,6 @@ public final class LinkCommandLine extends CommandLine { ActionOwner owner, Artifact output, @Nullable Artifact interfaceOutput, - @Nullable Artifact symbolCountsOutput, ImmutableList<Artifact> buildInfoHeaderArtifacts, Iterable<? extends LinkerInput> linkerInputs, Iterable<? extends LinkerInput> runtimeInputs, @@ -107,33 +98,11 @@ public final class LinkCommandLine extends CommandLine { boolean nativeDeps, boolean useTestOnlyFlags, boolean needWholeArchive, - @Nullable Iterable<LTOBackendArtifacts> allLTOArtifacts, @Nullable Artifact paramFile, Artifact interfaceSoBuilder, + List<String> noWholeArchiveFlags, CcToolchainFeatures.Variables variables, @Nullable FeatureConfiguration featureConfiguration) { - Preconditions.checkArgument(linkTargetType != LinkTargetType.INTERFACE_DYNAMIC_LIBRARY, - "you can't link an interface dynamic library directly"); - if (linkTargetType != LinkTargetType.DYNAMIC_LIBRARY) { - Preconditions.checkArgument(interfaceOutput == null, - "interface output may only be non-null for dynamic library links"); - } - if (linkTargetType.isStaticLibraryLink()) { - Preconditions.checkArgument(linkstamps.isEmpty(), - "linkstamps may only be present on dynamic library or executable links"); - Preconditions.checkArgument(linkStaticness == LinkStaticness.FULLY_STATIC, - "static library link must be static"); - Preconditions.checkArgument(buildInfoHeaderArtifacts.isEmpty(), - "build info headers may only be present on dynamic library or executable links"); - Preconditions.checkArgument(symbolCountsOutput == null, - "the symbol counts output must be null for static links"); - Preconditions.checkArgument(runtimeSolibDir == null, - "the runtime solib directory must be null for static links"); - Preconditions.checkArgument(!nativeDeps, - "the native deps flag must be false for static links"); - Preconditions.checkArgument(!needWholeArchive, - "the need whole archive flag must be false for static links"); - } this.actionName = actionName; this.configuration = Preconditions.checkNotNull(configuration); @@ -147,7 +116,6 @@ public final class LinkCommandLine extends CommandLine { Preconditions.checkNotNull(this.output); } - this.symbolCountsOutput = symbolCountsOutput; this.buildInfoHeaderArtifacts = Preconditions.checkNotNull(buildInfoHeaderArtifacts); this.linkerInputs = Preconditions.checkNotNull(linkerInputs); this.runtimeInputs = Preconditions.checkNotNull(runtimeInputs); @@ -164,16 +132,15 @@ public final class LinkCommandLine extends CommandLine { this.runtimeSolibDir = runtimeSolibDir; this.nativeDeps = nativeDeps; this.useTestOnlyFlags = useTestOnlyFlags; - this.needWholeArchive = needWholeArchive; - this.allLTOArtifacts = allLTOArtifacts; this.paramFile = paramFile; + this.noWholeArchiveFlags = noWholeArchiveFlags; // For now, silently ignore interfaceSoBuilder if we don't build an interface dynamic library. this.interfaceSoBuilder = ((linkTargetType == LinkTargetType.DYNAMIC_LIBRARY) && (interfaceOutput != null)) - ? Preconditions.checkNotNull(interfaceSoBuilder, - "cannot build interface dynamic library without builder") - : null; + ? Preconditions.checkNotNull( + interfaceSoBuilder, "cannot build interface dynamic library without builder") + : null; } /** @@ -186,15 +153,6 @@ public final class LinkCommandLine extends CommandLine { return interfaceOutput; } - /** - * Returns an artifact containing the number of symbols used per object file passed to the linker. - * This is currently a gold only feature, and is only produced for executables. If another target - * is being linked, or if symbol counts output is disabled, this will be null. - */ - @Nullable public Artifact getSymbolCountsOutput() { - return symbolCountsOutput; - } - @Nullable public Artifact getParamFile() { return paramFile; @@ -278,6 +236,12 @@ public final class LinkCommandLine extends CommandLine { return useTestOnlyFlags; } + /** Returns the build variables used to template the crosstool for this linker invocation. */ + @VisibleForTesting + public Variables getBuildVariables() { + return this.variables; + } + /** * Splits the link command-line into a part to be written to a parameter file, and the remaining * actual command line to be executed (which references the parameter file). Should only be used @@ -371,76 +335,110 @@ public final class LinkCommandLine extends CommandLine { } } } + + private void addToolchainFlags(List<String> argv) { + boolean fullyStatic = (linkStaticness == LinkStaticness.FULLY_STATIC); + boolean mostlyStatic = (linkStaticness == LinkStaticness.MOSTLY_STATIC); + boolean sharedLinkopts = + linkTargetType == LinkTargetType.DYNAMIC_LIBRARY + || linkopts.contains("-shared") + || cppConfiguration.getLinkOptions().contains("-shared"); + + /* + * For backwards compatibility, linkopts come _after_ inputFiles. + * This is needed to allow linkopts to contain libraries and + * positional library-related options such as + * -Wl,--begin-group -lfoo -lbar -Wl,--end-group + * or + * -Wl,--as-needed -lfoo -Wl,--no-as-needed + * + * As for the relative order of the three different flavours of linkopts + * (global defaults, per-target linkopts, and command-line linkopts), + * we have no idea what the right order should be, or if anyone cares. + */ + argv.addAll(linkopts); + // Extra toolchain link options based on the output's link staticness. + if (fullyStatic) { + argv.addAll(cppConfiguration.getFullyStaticLinkOptions(features, sharedLinkopts)); + } else if (mostlyStatic) { + argv.addAll(cppConfiguration.getMostlyStaticLinkOptions(features, sharedLinkopts)); + } else { + argv.addAll(cppConfiguration.getDynamicLinkOptions(features, sharedLinkopts)); + } + + // Extra test-specific link options. + if (useTestOnlyFlags) { + argv.addAll(cppConfiguration.getTestOnlyLinkOptions()); + } + + argv.addAll(cppConfiguration.getLinkOptions()); + + // -pie is not compatible with shared and should be + // removed when the latter is part of the link command. Should we need to further + // distinguish between shared libraries and executables, we could add additional + // command line / CROSSTOOL flags that distinguish them. But as long as this is + // the only relevant use case we're just special-casing it here. + if (linkTargetType == LinkTargetType.DYNAMIC_LIBRARY) { + Iterables.removeIf(argv, Predicates.equalTo("-pie")); + } + + // Fission mode: debug info is in .dwo files instead of .o files. Inform the linker of this. + if (!linkTargetType.isStaticLibraryLink() && cppConfiguration.useFission()) { + argv.add("-Wl,--gdb-index"); + } + } /** - * Returns a raw link command for the given link invocation, including both command and - * arguments (argv). After any further usage-specific processing, this can be passed to - * {@link #finalizeWithLinkstampCommands} to give the final command line. + * Returns a raw link command for the given link invocation, including both command and arguments + * (argv). After any further usage-specific processing, this can be passed to {@link + * #finalizeWithLinkstampCommands} to give the final command line. * * @return raw link command line. */ public List<String> getRawLinkArgv() { List<String> argv = new ArrayList<>(); - // We create the command line from the feature configuration. If no configuration is present, - // we use hard-coded flags. - if (featureConfiguration.actionIsConfigured(actionName)) { - argv.add(featureConfiguration - .getToolForAction(actionName) - .getToolPath(cppConfiguration.getCrosstoolTopPathFragment()) - .getPathString()); - argv.addAll(featureConfiguration.getCommandLine(actionName, variables)); - } else { - // TODO(b/28928350): In order to simplify the extraction of this logic into the linux - // crosstool, this switch statement should be reframed as a group of action_configs and - // features. Until they can be migrated into the linux crosstool, those features should - // be added to CppConfiguration.addLegacyFeatures(). - switch (linkTargetType) { - case EXECUTABLE: - addCppArgv(argv); - break; - - case DYNAMIC_LIBRARY: - if (interfaceOutput != null) { - argv.add(configuration.getShExecutable().getPathString()); - argv.add("-c"); - argv.add( - "build_iface_so=\"$0\"; impl=\"$1\"; iface=\"$2\"; cmd=\"$3\"; shift 3; " - + "\"$cmd\" \"$@\" && \"$build_iface_so\" \"$impl\" \"$iface\""); - argv.add(interfaceSoBuilder.getExecPathString()); - argv.add(output.getExecPathString()); - argv.add(interfaceOutput.getExecPathString()); - } - addCppArgv(argv); - // -pie is not compatible with -shared and should be - // removed when the latter is part of the link command. Should we need to further - // distinguish between shared libraries and executables, we could add additional - // command line / CROSSTOOL flags that distinguish them. But as long as this is - // the only relevant use case we're just special-casing it here. - Iterables.removeIf(argv, Predicates.equalTo("-pie")); - break; - - case STATIC_LIBRARY: - case PIC_STATIC_LIBRARY: - case ALWAYS_LINK_STATIC_LIBRARY: - case ALWAYS_LINK_PIC_STATIC_LIBRARY: - // The static library link command follows this template: - // ar <cmd> <output_archive> <input_files...> - argv.add(cppConfiguration.getArExecutable().getPathString()); - argv.addAll( - cppConfiguration.getArFlags(cppConfiguration.archiveType() == Link.ArchiveType.THIN)); + switch (linkTargetType) { + case EXECUTABLE: + argv.add(cppConfiguration.getCppExecutable().getPathString()); + argv.addAll(featureConfiguration.getCommandLine(actionName, variables)); + argv.addAll(noWholeArchiveFlags); + addToolchainFlags(argv); + break; + + case DYNAMIC_LIBRARY: + if (interfaceOutput != null) { + argv.add(configuration.getShExecutable().getPathString()); + argv.add("-c"); + argv.add( + "build_iface_so=\"$0\"; impl=\"$1\"; iface=\"$2\"; cmd=\"$3\"; shift 3; " + + "\"$cmd\" \"$@\" && \"$build_iface_so\" \"$impl\" \"$iface\""); + argv.add(interfaceSoBuilder.getExecPathString()); argv.add(output.getExecPathString()); - addInputFileLinkOptions(argv, /*needWholeArchive=*/ false); - break; - - default: - throw new IllegalArgumentException(); - } - } - - // Fission mode: debug info is in .dwo files instead of .o files. Inform the linker of this. - if (!linkTargetType.isStaticLibraryLink() && cppConfiguration.useFission()) { - argv.add("-Wl,--gdb-index"); + argv.add(interfaceOutput.getExecPathString()); + } + argv.add(cppConfiguration.getCppExecutable().getPathString()); + argv.addAll(featureConfiguration.getCommandLine(actionName, variables)); + argv.addAll(noWholeArchiveFlags); + addToolchainFlags(argv); + break; + + case STATIC_LIBRARY: + case PIC_STATIC_LIBRARY: + case ALWAYS_LINK_STATIC_LIBRARY: + case ALWAYS_LINK_PIC_STATIC_LIBRARY: + // The static library link command follows this template: + // ar <cmd> <output_archive> <input_files...> + argv.add(cppConfiguration.getArExecutable().getPathString()); + argv.addAll( + cppConfiguration.getArFlags(cppConfiguration.archiveType() == Link.ArchiveType.THIN)); + argv.add(output.getExecPathString()); + argv.addAll(featureConfiguration.getCommandLine(actionName, variables)); + argv.addAll(noWholeArchiveFlags); + break; + + default: + throw new IllegalArgumentException(); } return argv; @@ -537,21 +535,23 @@ public final class LinkCommandLine extends CommandLine { return ImmutableList.copyOf(batchCommand); } } + + private boolean isSharedNativeLibrary() { + return nativeDeps && cppConfiguration.shareNativeDeps(); + } /** - * Computes, for each C++ source file in - * {@link #getLinkstamps}, the command necessary to compile + * Computes, for each C++ source file in {@link #getLinkstamps}, the command necessary to compile * that file such that the output is correctly fed into the link command. * - * <p>As these options (as well as all others) are taken into account when - * computing the action key, they do not directly contain volatile build - * information to avoid unnecessary relinking. Instead this information is - * passed as an additional header generated by - * {@link com.google.devtools.build.lib.rules.cpp.WriteBuildInfoHeaderAction}. + * <p>As these options (as well as all others) are taken into account when computing the action + * key, they do not directly contain volatile build information to avoid unnecessary relinking. + * Instead this information is passed as an additional header generated by {@link + * com.google.devtools.build.lib.rules.cpp.WriteBuildInfoHeaderAction}. * * @param outputPrefix prefix to add before the linkstamp outputs' exec paths - * @return a list of shell-escaped compiler commmands, one for each entry - * in {@link #getLinkstamps} + * @return a list of shell-escaped compiler commmands, one for each entry in {@link + * #getLinkstamps} */ public List<String> getLinkstampCompileCommands(String outputPrefix) { if (linkstamps.isEmpty()) { @@ -626,369 +626,6 @@ public final class LinkCommandLine extends CommandLine { } /** - * Determine the arguments to pass to the C++ compiler when linking. - * Add them to the {@code argv} parameter. - */ - private void addCppArgv(List<String> argv) { - argv.add(cppConfiguration.getCppExecutable().getPathString()); - - // When using gold to link an executable, output the number of used and unused symbols. - if (symbolCountsOutput != null) { - argv.add("-Wl,--print-symbol-counts=" + symbolCountsOutput.getExecPathString()); - } - - if (linkTargetType == LinkTargetType.DYNAMIC_LIBRARY) { - argv.add("-shared"); - } - - // Add the outputs of any associated linkstamp compilations. - for (Artifact linkstampOutput : linkstamps.values()) { - argv.add(linkstampOutput.getExecPathString()); - } - - boolean fullyStatic = (linkStaticness == LinkStaticness.FULLY_STATIC); - boolean mostlyStatic = (linkStaticness == LinkStaticness.MOSTLY_STATIC); - boolean sharedLinkopts = - linkTargetType == LinkTargetType.DYNAMIC_LIBRARY - || linkopts.contains("-shared") - || cppConfiguration.getLinkOptions().contains("-shared"); - - if (output != null) { - argv.add("-o"); - String execpath = output.getExecPathString(); - if (mostlyStatic - && linkTargetType == LinkTargetType.EXECUTABLE - && cppConfiguration.skipStaticOutputs()) { - // Linked binary goes to /dev/null; bogus dependency info in its place. - Collections.addAll(argv, "/dev/null", "-MMD", "-MF", execpath); - } else { - argv.add(execpath); - } - } - - addInputFileLinkOptions(argv, needWholeArchive); - - /* - * For backwards compatibility, linkopts come _after_ inputFiles. - * This is needed to allow linkopts to contain libraries and - * positional library-related options such as - * -Wl,--begin-group -lfoo -lbar -Wl,--end-group - * or - * -Wl,--as-needed -lfoo -Wl,--no-as-needed - * - * As for the relative order of the three different flavours of linkopts - * (global defaults, per-target linkopts, and command-line linkopts), - * we have no idea what the right order should be, or if anyone cares. - */ - argv.addAll(linkopts); - - // Extra toolchain link options based on the output's link staticness. - if (fullyStatic) { - argv.addAll(cppConfiguration.getFullyStaticLinkOptions(features, sharedLinkopts)); - } else if (mostlyStatic) { - argv.addAll(cppConfiguration.getMostlyStaticLinkOptions(features, sharedLinkopts)); - } else { - argv.addAll(cppConfiguration.getDynamicLinkOptions(features, sharedLinkopts)); - } - - // Extra test-specific link options. - if (useTestOnlyFlags) { - argv.addAll(cppConfiguration.getTestOnlyLinkOptions()); - } - - if (linkTargetType == LinkTargetType.EXECUTABLE && cppConfiguration.forcePic()) { - argv.add("-pie"); - } - - argv.addAll(cppConfiguration.getLinkOptions()); - // The feature config can be null for tests. - if (featureConfiguration != null) { - argv.addAll(featureConfiguration.getCommandLine(actionName, variables)); - } - } - - private static boolean isDynamicLibrary(LinkerInput linkInput) { - Artifact libraryArtifact = linkInput.getArtifact(); - String name = libraryArtifact.getFilename(); - return Link.SHARED_LIBRARY_FILETYPES.matches(name) && name.startsWith("lib"); - } - - private boolean isSharedNativeLibrary() { - return nativeDeps && cppConfiguration.shareNativeDeps(); - } - - /** - * When linking a shared library fully or mostly static then we need to link in - * *all* dependent files, not just what the shared library needs for its own - * code. This is done by wrapping all objects/libraries with - * -Wl,-whole-archive and -Wl,-no-whole-archive. For this case the - * globalNeedWholeArchive parameter must be set to true. Otherwise only - * library objects (.lo) need to be wrapped with -Wl,-whole-archive and - * -Wl,-no-whole-archive. - */ - private void addInputFileLinkOptions(List<String> argv, boolean globalNeedWholeArchive) { - // The Apple ld doesn't support -whole-archive/-no-whole-archive. It - // does have -all_load/-noall_load, but -all_load is a global setting - // that affects all subsequent files, and -noall_load is simply ignored. - // TODO(bazel-team): Not sure what the implications of this are, other than - // bloated binaries. - boolean macosx = cppConfiguration.getTargetLibc().equals("macosx"); - if (globalNeedWholeArchive) { - argv.add(macosx ? "-Wl,-all_load" : "-Wl,-whole-archive"); - } - - // Used to collect -L and -Wl,-rpath options, ensuring that each used only once. - Set<String> libOpts = new LinkedHashSet<>(); - - // List of command line parameters to link input files (either directly or using -l). - List<String> linkerInputs = new ArrayList<>(); - - // List of command line parameters that need to be placed *outside* of - // --whole-archive ... --no-whole-archive. - List<String> noWholeArchiveInputs = new ArrayList<>(); - - PathFragment solibDir = configuration.getBinDirectory().getExecPath() - .getRelative(cppConfiguration.getSolibDirectory()); - String runtimeSolibName = runtimeSolibDir != null ? runtimeSolibDir.getBaseName() : null; - boolean runtimeRpath = runtimeSolibDir != null - && (linkTargetType == LinkTargetType.DYNAMIC_LIBRARY - || (linkTargetType == LinkTargetType.EXECUTABLE - && linkStaticness == LinkStaticness.DYNAMIC)); - - String rpathRoot = null; - List<String> runtimeRpathEntries = new ArrayList<>(); - - if (output != null) { - String origin = - useTestOnlyFlags && cppConfiguration.supportsExecOrigin() ? "$EXEC_ORIGIN/" : "$ORIGIN/"; - if (runtimeRpath) { - runtimeRpathEntries.add("-Wl,-rpath," + origin + runtimeSolibName + "/"); - } - - // Calculate the correct relative value for the "-rpath" link option (which sets - // the search path for finding shared libraries). - if (isSharedNativeLibrary()) { - // For shared native libraries, special symlinking is applied to ensure C++ - // runtimes are available under $ORIGIN/_solib_[arch]. So we set the RPATH to find - // them. - // - // Note that we have to do this because $ORIGIN points to different paths for - // different targets. In other words, blaze-bin/d1/d2/d3/a_shareddeps.so and - // blaze-bin/d4/b_shareddeps.so have different path depths. The first could - // reference a standard blaze-bin/_solib_[arch] via $ORIGIN/../../../_solib[arch], - // and the second could use $ORIGIN/../_solib_[arch]. But since this is a shared - // artifact, both are symlinks to the same place, so - // there's no *one* RPATH setting that fits all targets involved in the sharing. - rpathRoot = "-Wl,-rpath," + origin + ":" - + origin + cppConfiguration.getSolibDirectory() + "/"; - if (runtimeRpath) { - runtimeRpathEntries.add("-Wl,-rpath," + origin + "../" + runtimeSolibName + "/"); - } - } else { - // For all other links, calculate the relative path from the output file to _solib_[arch] - // (the directory where all shared libraries are stored, which resides under the blaze-bin - // directory. In other words, given blaze-bin/my/package/binary, rpathRoot would be - // "../../_solib_[arch]". - if (runtimeRpath) { - runtimeRpathEntries.add("-Wl,-rpath," + origin - + Strings.repeat("../", output.getRootRelativePath().segmentCount() - 1) - + runtimeSolibName + "/"); - } - - rpathRoot = "-Wl,-rpath," - + origin + Strings.repeat("../", output.getRootRelativePath().segmentCount() - 1) - + cppConfiguration.getSolibDirectory() + "/"; - - if (nativeDeps) { - // We also retain the $ORIGIN/ path to solibs that are in _solib_<arch>, as opposed to - // the package directory) - if (runtimeRpath) { - runtimeRpathEntries.add("-Wl,-rpath," + origin + "../" + runtimeSolibName + "/"); - } - rpathRoot += ":" + origin; - } - } - } - - boolean includeSolibDir = false; - - - Map<Artifact, Artifact> ltoMap = null; - if (allLTOArtifacts != null) { - // TODO(bazel-team): The LTO final link can only work if there are individual .o files on the - // command line. Rather than crashing, this should issue a nice error. We will get this by - // 1) moving supports_start_end_lib to a toolchain feature - // 2) having thin_lto require start_end_lib - // As a bonus, we can rephrase --nostart_end_lib as --features=-start_end_lib and get rid - // of a command line option. - - Preconditions.checkState(cppConfiguration.useStartEndLib()); - ltoMap = new HashMap<>(); - for (LTOBackendArtifacts l : allLTOArtifacts) { - ltoMap.put(l.getBitcodeFile(), l.getObjectFile()); - } - } - - for (LinkerInput input : getLinkerInputs()) { - if (isDynamicLibrary(input)) { - PathFragment libDir = input.getArtifact().getExecPath().getParentDirectory(); - Preconditions.checkState( - libDir.startsWith(solibDir), - "Artifact '%s' is not under directory '%s'.", input.getArtifact(), solibDir); - if (libDir.equals(solibDir)) { - includeSolibDir = true; - } - addDynamicInputLinkOptions(input, linkerInputs, libOpts, solibDir, rpathRoot); - } else { - addStaticInputLinkOptions(input, linkerInputs, ltoMap); - } - } - - boolean includeRuntimeSolibDir = false; - - for (LinkerInput input : runtimeInputs) { - List<String> optionsList = globalNeedWholeArchive - ? noWholeArchiveInputs - : linkerInputs; - - if (isDynamicLibrary(input)) { - PathFragment libDir = input.getArtifact().getExecPath().getParentDirectory(); - Preconditions.checkState(runtimeSolibDir != null && libDir.equals(runtimeSolibDir), - "Artifact '%s' is not under directory '%s'.", input.getArtifact(), solibDir); - includeRuntimeSolibDir = true; - addDynamicInputLinkOptions(input, optionsList, libOpts, solibDir, rpathRoot); - } else { - addStaticInputLinkOptions(input, optionsList, ltoMap); - } - } - - // rpath ordering matters for performance; first add the one where most libraries are found. - if (includeSolibDir && rpathRoot != null) { - argv.add(rpathRoot); - } - if (includeRuntimeSolibDir) { - argv.addAll(runtimeRpathEntries); - } - argv.addAll(libOpts); - - // Need to wrap static libraries with whole-archive option - for (String option : linkerInputs) { - if (!globalNeedWholeArchive && Link.LINK_LIBRARY_FILETYPES.matches(option)) { - if (macosx) { - argv.add("-Wl,-force_load," + option); - } else { - argv.add("-Wl,-whole-archive"); - argv.add(option); - argv.add("-Wl,-no-whole-archive"); - } - } else { - argv.add(option); - } - } - - if (globalNeedWholeArchive) { - argv.add(macosx ? "-Wl,-noall_load" : "-Wl,-no-whole-archive"); - argv.addAll(noWholeArchiveInputs); - } - - if (ltoMap != null) { - Preconditions.checkState( - ltoMap.size() == 0, "Still have LTO objects left: %s, command-line: %s", ltoMap, argv); - } - } - - /** - * Adds command-line options for a dynamic library input file into - * options and libOpts. - */ - private void addDynamicInputLinkOptions(LinkerInput input, List<String> options, - Set<String> libOpts, PathFragment solibDir, String rpathRoot) { - Preconditions.checkState(isDynamicLibrary(input)); - Preconditions.checkState( - !Link.useStartEndLib(input, cppConfiguration.archiveType())); - - Artifact inputArtifact = input.getArtifact(); - PathFragment libDir = inputArtifact.getExecPath().getParentDirectory(); - if (rpathRoot != null - && !libDir.equals(solibDir) - && (runtimeSolibDir == null || !runtimeSolibDir.equals(libDir))) { - String dotdots = ""; - PathFragment commonParent = solibDir; - while (!libDir.startsWith(commonParent)) { - dotdots += "../"; - commonParent = commonParent.getParentDirectory(); - } - - libOpts.add(rpathRoot + dotdots + libDir.relativeTo(commonParent).getPathString()); - } - - libOpts.add("-L" + inputArtifact.getExecPath().getParentDirectory().getPathString()); - - String name = inputArtifact.getFilename(); - if (CppFileTypes.SHARED_LIBRARY.matches(name)) { - String libName = name.replaceAll("(^lib|\\.(so|dylib)$)", ""); - options.add("-l" + libName); - } else { - // Interface shared objects have a non-standard extension - // that the linker won't be able to find. So use the - // filename directly rather than a -l option. Since the - // library has an SONAME attribute, this will work fine. - options.add(inputArtifact.getExecPathString()); - } - } - - /** - * Adds command-line options for a static library or non-library input - * into options. - * - * @param ltoMap is a mutable list of exec paths that should be on the command-line, which - * must be supplied for LTO final links. - */ - private void addStaticInputLinkOptions( - LinkerInput input, List<String> options, @Nullable Map<Artifact, Artifact> ltoMap) { - Preconditions.checkState(!isDynamicLibrary(input)); - - // start-lib/end-lib library: adds its input object files. - if (Link.useStartEndLib(input, cppConfiguration.archiveType())) { - Iterable<Artifact> archiveMembers = input.getObjectFiles(); - if (!Iterables.isEmpty(archiveMembers)) { - options.add("-Wl,--start-lib"); - for (Artifact member : archiveMembers) { - if (ltoMap != null) { - Artifact backend = ltoMap.remove(member); - - if (backend != null) { - // If the backend artifact is missing, we can't print a warning because this may - // happen normally, due libraries that list .o files explicitly, or generate .o - // files from assembler. - member = backend; - } - } - - options.add(member.getExecPathString()); - } - options.add("-Wl,--end-lib"); - } - } else { - // For anything else, add the input directly. - Artifact inputArtifact = input.getArtifact(); - - if (ltoMap != null) { - Artifact ltoArtifact = ltoMap.remove(inputArtifact); - if (ltoArtifact != null) { - inputArtifact = ltoArtifact; - } - } - - if (input.isFake()) { - options.add(Link.FAKE_OBJECT_PREFIX + inputArtifact.getExecPathString()); - } else { - options.add(inputArtifact.getExecPathString()); - } - } - } - - /** * A builder for a {@link LinkCommandLine}. */ public static final class Builder { @@ -1013,10 +650,8 @@ public final class LinkCommandLine extends CommandLine { private final ActionOwner owner; @Nullable private final RuleContext ruleContext; - private String actionName; @Nullable private Artifact output; @Nullable private Artifact interfaceOutput; - @Nullable private Artifact symbolCountsOutput; private ImmutableList<Artifact> buildInfoHeaderArtifacts = ImmutableList.of(); private Iterable<? extends LinkerInput> linkerInputs = ImmutableList.of(); private Iterable<? extends LinkerInput> runtimeInputs = ImmutableList.of(); @@ -1030,17 +665,18 @@ public final class LinkCommandLine extends CommandLine { private boolean nativeDeps; private boolean useTestOnlyFlags; private boolean needWholeArchive; - @Nullable private Iterable<LTOBackendArtifacts> allLTOBackendArtifacts; @Nullable private Artifact paramFile; @Nullable private Artifact interfaceSoBuilder; @Nullable private CcToolchainProvider toolchain; + private Variables variables; private FeatureConfiguration featureConfiguration; + private List<String> noWholeArchiveFlags = ImmutableList.of(); // This interface is needed to support tests that don't create a // ruleContext, in which case the configuration and action owner // cannot be accessed off of the give ruleContext. - public Builder(BuildConfiguration configuration, ActionOwner owner, - @Nullable RuleContext ruleContext) { + public Builder( + BuildConfiguration configuration, ActionOwner owner, @Nullable RuleContext ruleContext) { this.configuration = configuration; this.owner = owner; this.ruleContext = ruleContext; @@ -1051,6 +687,16 @@ public final class LinkCommandLine extends CommandLine { } public LinkCommandLine build() { + + if (linkTargetType.isStaticLibraryLink()) { + Preconditions.checkArgument( + linkstamps.isEmpty(), + "linkstamps may only be present on dynamic library or executable links"); + Preconditions.checkArgument( + buildInfoHeaderArtifacts.isEmpty(), + "build info headers may only be present on dynamic library or executable links"); + } + ImmutableList<String> actualLinkstampCompileOptions; if (linkstampCompileOptions.isEmpty()) { actualLinkstampCompileOptions = DEFAULT_LINKSTAMP_OPTIONS; @@ -1058,7 +704,7 @@ public final class LinkCommandLine extends CommandLine { actualLinkstampCompileOptions = ImmutableList.copyOf( Iterables.concat(DEFAULT_LINKSTAMP_OPTIONS, linkstampCompileOptions)); } - CcToolchainFeatures.Variables variables = null; + // The ruleContext can be null for some tests. if (ruleContext != null) { if (featureConfiguration == null) { @@ -1070,18 +716,20 @@ public final class LinkCommandLine extends CommandLine { featureConfiguration = CcCommon.configureFeatures(ruleContext); } } - CcToolchainFeatures.Variables.Builder buildVariables = - new CcToolchainFeatures.Variables.Builder(); - CppHelper.getFdoSupport(ruleContext).getLinkOptions(featureConfiguration, buildVariables); - variables = buildVariables.build(); } + + if (variables == null) { + variables = Variables.EMPTY; + } + + String actionName = linkTargetType.getActionName(); + return new LinkCommandLine( actionName, configuration, owner, output, interfaceOutput, - symbolCountsOutput, buildInfoHeaderArtifacts, linkerInputs, runtimeInputs, @@ -1096,22 +744,14 @@ public final class LinkCommandLine extends CommandLine { nativeDeps, useTestOnlyFlags, needWholeArchive, - allLTOBackendArtifacts, paramFile, interfaceSoBuilder, + noWholeArchiveFlags, variables, featureConfiguration); } /** - * Sets the name of the action for the purposes of querying the crosstool. - */ - public Builder setActionName(String actionName) { - this.actionName = actionName; - return this; - } - - /** * Sets the toolchain to use for link flags. If this is not called, the toolchain * is retrieved from the rule. */ @@ -1174,19 +814,9 @@ public final class LinkCommandLine extends CommandLine { } /** - * Sets an additional output artifact that contains symbol counts. The {@link #build} method - * throws an exception if this is non-null for a static link (see - * {@link LinkTargetType#isStaticLibraryLink}). - */ - public Builder setSymbolCountsOutput(Artifact symbolCountsOutput) { - this.symbolCountsOutput = symbolCountsOutput; - return this; - } - - /** * Sets the linker options. These are passed to the linker in addition to the other linker - * options like linker inputs, symbol count options, etc. The {@link #build} method - * throws an exception if the linker options are non-empty for a static link (see {@link + * options like linker inputs, symbol count options, etc. The {@link #build} method throws an + * exception if the linker options are non-empty for a static link (see {@link * LinkTargetType#isStaticLibraryLink}). */ public Builder setLinkopts(ImmutableList<String> linkopts) { @@ -1253,16 +883,6 @@ public final class LinkCommandLine extends CommandLine { } /** - * Sets the directory of the dynamic runtime libraries, which is added to the rpath. The {@link - * #build} method throws an exception if the runtime dir is non-null for a static link (see - * {@link LinkTargetType#isStaticLibraryLink}). - */ - public Builder setRuntimeSolibDir(PathFragment runtimeSolibDir) { - this.runtimeSolibDir = runtimeSolibDir; - return this; - } - - /** * Whether the resulting library is intended to be used as a native library from another * programming language. This influences the rpath. The {@link #build} method throws an * exception if this is true for a static link (see {@link LinkTargetType#isStaticLibraryLink}). @@ -1281,18 +901,28 @@ public final class LinkCommandLine extends CommandLine { return this; } - public Builder setNeedWholeArchive(boolean needWholeArchive) { - this.needWholeArchive = needWholeArchive; + public Builder setParamFile(Artifact paramFile) { + this.paramFile = paramFile; + return this; + } + + public Builder setBuildVariables(Variables variables) { + this.variables = variables; return this; } - public Builder setParamFile(Artifact paramFile) { - this.paramFile = paramFile; + public Builder setRuntimeSolibDir(PathFragment runtimeSolibDir) { + this.runtimeSolibDir = runtimeSolibDir; return this; } - public Builder setAllLTOArtifacts(Iterable<LTOBackendArtifacts> allLTOArtifacts) { - this.allLTOBackendArtifacts = allLTOArtifacts; + /** + * Set flags that should not be in a --whole_archive block. + * + * <p>TODO(b/30228443): Refactor into action_configs. + */ + public Builder setNoWholeArchiveFlags(List<String> noWholeArchiveFlags) { + this.noWholeArchiveFlags = noWholeArchiveFlags; return this; } } |