aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/rules/cpp/LinkCommandLine.java
diff options
context:
space:
mode:
authorGravatar Cal Peyser <cpeyser@google.com>2016-07-26 14:50:52 +0000
committerGravatar Damien Martin-Guillerez <dmarting@google.com>2016-07-27 11:14:07 +0000
commitf61d12e9e4f940810efbaf244911a94830ba6c05 (patch)
tree055027fdcc0b6ba6f284b34780fff5482cf41b69 /src/main/java/com/google/devtools/build/lib/rules/cpp/LinkCommandLine.java
parent2745f0bafd7d549688480cdee3f021de489c719e (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=128470872
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/rules/cpp/LinkCommandLine.java')
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/cpp/LinkCommandLine.java690
1 files changed, 160 insertions, 530 deletions
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;
}
}