aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main
diff options
context:
space:
mode:
authorGravatar Cal Peyser <cpeyser@google.com>2016-08-03 16:12:21 +0000
committerGravatar Yun Peng <pcloudy@google.com>2016-08-03 17:16:01 +0000
commit84de2cc636947aaa41fdec6c2d30232faeaabbdd (patch)
tree903b87d6ffc03b07fa6bbbf92304c8e5e2d9d0ec /src/main
parent3437d2f89730b72dfe89284a007798c67f75b162 (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')
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/cpp/CcBinary.java4
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainFeatures.java26
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/cpp/CppConfiguration.java22
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/cpp/CppLinkActionBuilder.java573
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/cpp/CppLinkActionConfigs.java297
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/cpp/CppModel.java12
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/cpp/LinkCommandLine.java690
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;
}
}