aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/rules/cpp/LinkCommandLine.java
diff options
context:
space:
mode:
authorGravatar cpeyser <cpeyser@google.com>2017-12-08 09:16:28 -0800
committerGravatar Copybara-Service <copybara-piper@google.com>2017-12-08 09:18:34 -0800
commit367f704e71f352b404df38161f4c367b9ff506c9 (patch)
treea9319876a96fcca04af89561263b085cc3db6ae0 /src/main/java/com/google/devtools/build/lib/rules/cpp/LinkCommandLine.java
parentbaa99fb3d21a9563c9da5c20b975b41111c5519f (diff)
Rollback of 178106899.
PiperOrigin-RevId: 178384991
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.java305
1 files changed, 299 insertions, 6 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 e1cde74d3d..3be6802314 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
@@ -15,15 +15,20 @@
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.Preconditions;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.devtools.build.lib.actions.ActionOwner;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.analysis.RuleContext;
import com.google.devtools.build.lib.analysis.actions.CommandLine;
import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
+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;
@@ -32,9 +37,13 @@ import com.google.devtools.build.lib.rules.cpp.Link.LinkStaticness;
import com.google.devtools.build.lib.rules.cpp.Link.LinkTargetType;
import com.google.devtools.build.lib.rules.cpp.Link.Staticness;
import com.google.devtools.build.lib.util.Pair;
+import com.google.devtools.build.lib.util.ShellEscaper;
import com.google.devtools.build.lib.vfs.PathFragment;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import javax.annotation.Nullable;
/**
@@ -45,10 +54,13 @@ import javax.annotation.Nullable;
public final class LinkCommandLine extends CommandLine {
private final String actionName;
private final String forcedToolPath;
+ private final boolean codeCoverageEnabled;
private final CppConfiguration cppConfiguration;
+ private final ActionOwner owner;
private final CcToolchainFeatures.Variables variables;
// The feature config can be null for tests.
@Nullable private final FeatureConfiguration featureConfiguration;
+ @Nullable private final Artifact output;
private final ImmutableList<Artifact> buildInfoHeaderArtifacts;
private final Iterable<? extends LinkerInput> linkerInputs;
private final Iterable<? extends LinkerInput> runtimeInputs;
@@ -56,6 +68,9 @@ public final class LinkCommandLine extends CommandLine {
private final LinkStaticness linkStaticness;
private final ImmutableList<String> linkopts;
private final ImmutableSet<String> features;
+ private final ImmutableMap<Artifact, Artifact> linkstamps;
+ private final ImmutableList<String> additionalLinkstampDefines;
+ @Nullable private final String fdoBuildStamp;
@Nullable private final PathFragment runtimeSolibDir;
private final boolean nativeDeps;
private final boolean useTestOnlyFlags;
@@ -67,6 +82,8 @@ public final class LinkCommandLine extends CommandLine {
String actionName,
String forcedToolPath,
BuildConfiguration configuration,
+ ActionOwner owner,
+ Artifact output,
ImmutableList<Artifact> buildInfoHeaderArtifacts,
Iterable<? extends LinkerInput> linkerInputs,
Iterable<? extends LinkerInput> runtimeInputs,
@@ -74,6 +91,9 @@ public final class LinkCommandLine extends CommandLine {
LinkStaticness linkStaticness,
ImmutableList<String> linkopts,
ImmutableSet<String> features,
+ ImmutableMap<Artifact, Artifact> linkstamps,
+ ImmutableList<String> additionalLinkstampDefines,
+ @Nullable String fdoBuildStamp,
@Nullable PathFragment runtimeSolibDir,
boolean nativeDeps,
boolean useTestOnlyFlags,
@@ -84,9 +104,12 @@ public final class LinkCommandLine extends CommandLine {
this.actionName = actionName;
this.forcedToolPath = forcedToolPath;
+ this.codeCoverageEnabled = configuration.isCodeCoverageEnabled();
this.cppConfiguration = configuration.getFragment(CppConfiguration.class);
this.variables = variables;
this.featureConfiguration = featureConfiguration;
+ this.owner = Preconditions.checkNotNull(owner);
+ this.output = output;
this.buildInfoHeaderArtifacts = Preconditions.checkNotNull(buildInfoHeaderArtifacts);
this.linkerInputs = Preconditions.checkNotNull(linkerInputs);
this.runtimeInputs = Preconditions.checkNotNull(runtimeInputs);
@@ -98,6 +121,9 @@ public final class LinkCommandLine extends CommandLine {
? ImmutableList.of()
: Preconditions.checkNotNull(linkopts);
this.features = Preconditions.checkNotNull(features);
+ this.linkstamps = Preconditions.checkNotNull(linkstamps);
+ this.additionalLinkstampDefines = additionalLinkstampDefines;
+ this.fdoBuildStamp = fdoBuildStamp;
this.runtimeSolibDir = runtimeSolibDir;
this.nativeDeps = nativeDeps;
this.useTestOnlyFlags = useTestOnlyFlags;
@@ -150,6 +176,11 @@ public final class LinkCommandLine extends CommandLine {
return linkopts;
}
+ /** See {@link CppLinkAction#getLinkstamps()} */
+ protected ImmutableMap<Artifact, Artifact> getLinkstamps() {
+ return linkstamps;
+ }
+
/**
* Returns the location of the C++ runtime solib symlinks. If null, the C++ dynamic runtime
* libraries either do not exist (because they do not come from the depot) or they are in the
@@ -342,7 +373,8 @@ public final class LinkCommandLine extends CommandLine {
/**
* Returns a raw link command for the given link invocation, including both command and arguments
- * (argv).
+ * (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.
*/
@@ -371,27 +403,244 @@ public final class LinkCommandLine extends CommandLine {
}
List<String> getCommandLine() {
+ List<String> commandlineArgs;
// Try to shorten the command line by use of a parameter file.
// This makes the output with --subcommands (et al) more readable.
if (paramFile != null) {
Pair<List<String>, List<String>> split = splitCommandline();
- return split.first;
+ commandlineArgs = split.first;
} else {
- return getRawLinkArgv();
+ commandlineArgs = getRawLinkArgv();
}
+ return finalizeWithLinkstampCommands(commandlineArgs);
}
@Override
public List<String> arguments() {
- return (getRawLinkArgv());
+ return finalizeWithLinkstampCommands(getRawLinkArgv());
+ }
+
+ /**
+ * Takes a raw link command line and gives the final link command that will also first compile any
+ * linkstamps necessary. Elements of rawLinkArgv are shell-escaped.
+ *
+ * @param rawLinkArgv raw link command line
+ * @return final link command line suitable for execution
+ */
+ public List<String> finalizeWithLinkstampCommands(List<String> rawLinkArgv) {
+ return addLinkstampingToCommand(getLinkstampCompileCommands(""), rawLinkArgv, true);
+ }
+
+ /**
+ * Takes a raw link command line and gives the final link command that will also first compile any
+ * linkstamps necessary. Elements of rawLinkArgv are not shell-escaped.
+ *
+ * @param rawLinkArgv raw link command line
+ * @param outputPrefix prefix to add before the linkstamp outputs' exec paths
+ * @return final link command line suitable for execution
+ */
+ public List<String> finalizeAlreadyEscapedWithLinkstampCommands(
+ List<String> rawLinkArgv, String outputPrefix) {
+ return addLinkstampingToCommand(getLinkstampCompileCommands(outputPrefix), rawLinkArgv, false);
+ }
+
+ /**
+ * Adds linkstamp compilation to the (otherwise) fully specified link command if {@link
+ * #getLinkstamps} is non-empty.
+ *
+ * <p>Linkstamps were historically compiled implicitly as part of the link command, but implicit
+ * compilation doesn't guarantee consistent outputs. For example, the command "gcc input.o input.o
+ * foo/linkstamp.cc -o myapp" causes gcc to implicitly run "gcc foo/linkstamp.cc -o
+ * /tmp/ccEtJHDB.o", for some internally decided output path /tmp/ccEtJHDB.o, then add that path
+ * to the linker's command line options. The name of this path can change even between
+ * equivalently specified gcc invocations.
+ *
+ * <p>So now we explicitly compile these files in their own command invocations before running the
+ * link command, thus giving us direct control over the naming of their outputs. This method adds
+ * those extra steps as necessary.
+ *
+ * @param linkstampCommands individual linkstamp compilation commands
+ * @param linkCommand the complete list of link command arguments (after .params file compacting)
+ * for an invocation
+ * @param escapeArgs if true, linkCommand arguments are shell escaped. if false, arguments are
+ * returned as-is
+ * @return The original argument list if no linkstamps compilation commands are given, otherwise
+ * an expanded list that adds the linkstamp compilation commands and funnels their outputs
+ * into the link step. Note that these outputs only need to persist for the duration of the
+ * link step.
+ */
+ private static List<String> addLinkstampingToCommand(
+ List<String> linkstampCommands, List<String> linkCommand, boolean escapeArgs) {
+ if (linkstampCommands.isEmpty()) {
+ return linkCommand;
+ } else {
+ List<String> batchCommand = Lists.newArrayListWithCapacity(3);
+ batchCommand.add("/bin/bash");
+ batchCommand.add("-c");
+ batchCommand.add(
+ Joiner.on(" && ").join(linkstampCommands)
+ + " && "
+ + (escapeArgs
+ ? ShellEscaper.escapeJoinAll(linkCommand)
+ : Joiner.on(" ").join(linkCommand)));
+ 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
+ * 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}.
+ *
+ * @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}
+ */
+ public List<String> getLinkstampCompileCommands(String outputPrefix) {
+ if (linkstamps.isEmpty()) {
+ return ImmutableList.of();
+ }
+
+ List<String> commands = Lists.newArrayListWithCapacity(linkstamps.size());
+
+ for (Map.Entry<Artifact, Artifact> linkstamp : linkstamps.entrySet()) {
+ Artifact sourceFile = linkstamp.getKey();
+ Artifact outputFile = linkstamp.getValue();
+ Variables linkstampVariables = collectLinkstampVariables(sourceFile, outputFile);
+
+ ImmutableList.Builder<String> linkstampCompileCommandLine = ImmutableList.builder();
+ linkstampCompileCommandLine.add(
+ featureConfiguration
+ .getToolForAction(CppCompileAction.LINKSTAMP_COMPILE)
+ .getToolPath(cppConfiguration.getCrosstoolTopPathFragment())
+ .getPathString());
+ linkstampCompileCommandLine.addAll(
+ featureConfiguration.getCommandLine(
+ CppCompileAction.LINKSTAMP_COMPILE, linkstampVariables));
+ // TODO(b/28946988): Remove -c/-o hardcoded flags from bazel
+ linkstampCompileCommandLine.add("-c");
+ linkstampCompileCommandLine.add(sourceFile.getExecPathString());
+ linkstampCompileCommandLine.add("-o");
+ // outputPrefix is there only for cc_fake_binary, and it can contain env var expansions
+ // (such as $TEST_TMPDIR) and cannot be escaped. When we move linkstamping to a separate
+ // action, there will no longer be bash around the invocation and therefore no need to do
+ // shell escaping.
+ String escapedCommandWithoutOutput =
+ ShellEscaper.escapeJoinAll(linkstampCompileCommandLine.build());
+ commands.add(
+ escapedCommandWithoutOutput
+ + " "
+ + outputPrefix
+ + ShellEscaper.escapeString(outputFile.getExecPathString()));
+ }
+
+ return commands;
+ }
+
+ private Variables collectLinkstampVariables(Artifact sourceFile, Artifact outputFile) {
+ // TODO(b/34761650): Remove all this hardcoding by separating a full blown compile action.
+ Preconditions.checkArgument(
+ featureConfiguration.actionIsConfigured(CppCompileAction.LINKSTAMP_COMPILE));
+
+ Variables.Builder linkstampVariables = new Variables.Builder(ccProvider.getBuildVariables());
+ // We need to force inclusion of build_info headers
+ linkstampVariables.addStringSequenceVariable(
+ CppModel.INCLUDES_VARIABLE_NAME,
+ buildInfoHeaderArtifacts
+ .stream()
+ .map(Artifact::getExecPathString)
+ .collect(ImmutableList.toImmutableList()));
+ // Input/Output files.
+ linkstampVariables.addStringVariable(
+ CppModel.SOURCE_FILE_VARIABLE_NAME, sourceFile.getExecPathString());
+ linkstampVariables.addStringVariable(
+ CppModel.OUTPUT_OBJECT_FILE_VARIABLE_NAME, outputFile.getExecPathString());
+ // Include directories for (normal includes with ".", empty quote- and system- includes).
+ linkstampVariables.addStringSequenceVariable(
+ CppModel.INCLUDE_PATHS_VARIABLE_NAME, ImmutableList.of("."));
+ linkstampVariables.addStringSequenceVariable(
+ CppModel.QUOTE_INCLUDE_PATHS_VARIABLE_NAME, ImmutableList.of());
+ linkstampVariables.addStringSequenceVariable(
+ CppModel.SYSTEM_INCLUDE_PATHS_VARIABLE_NAME, ImmutableList.of());
+ // Legacy flags coming from fields such as compiler_flag
+ linkstampVariables.addLazyStringSequenceVariable(
+ CppModel.LEGACY_COMPILE_FLAGS_VARIABLE_NAME,
+ CppModel.getLegacyCompileFlagsSupplier(
+ cppConfiguration, sourceFile.getExecPathString(), features));
+ // Unfilterable flags coming from unfiltered_cxx_flag fields
+ linkstampVariables.addLazyStringSequenceVariable(
+ CppModel.UNFILTERED_COMPILE_FLAGS_VARIABLE_NAME,
+ CppModel.getUnfilteredCompileFlagsSupplier(ccProvider, features));
+ // Collect all preprocessor defines, and in each replace ${LABEL} by labelReplacements, and
+ // ${OUTPUT_PATH} with outputPathReplacement.
+ linkstampVariables.addStringSequenceVariable(
+ CppModel.PREPROCESSOR_DEFINES_VARIABLE_NAME, computeAllLinkstampDefines());
+ // For dynamic libraries, produce position independent code.
+ if (cppConfiguration.forcePic()
+ || (linkTargetType == LinkTargetType.DYNAMIC_LIBRARY && ccProvider.toolchainNeedsPic())) {
+ linkstampVariables.addStringVariable(CppModel.PIC_VARIABLE_NAME, "");
+ }
+ return linkstampVariables.build();
+ }
+
+ private ImmutableList<String> computeAllLinkstampDefines() {
+ String labelReplacement =
+ Matcher.quoteReplacement(
+ isSharedNativeLibrary() ? output.getExecPathString() : Label.print(owner.getLabel()));
+ String outputPathReplacement = Matcher.quoteReplacement(output.getExecPathString());
+ String labelPattern = Pattern.quote("${LABEL}");
+ String outputPathPattern = Pattern.quote("${OUTPUT_PATH}");
+ ImmutableList.Builder<String> defines = ImmutableList.builder();
+ defines
+ .add("GPLATFORM=\"" + cppConfiguration + "\"")
+ .add("BUILD_COVERAGE_ENABLED=" + (codeCoverageEnabled ? "1" : "0"))
+ // G3_VERSION_INFO and G3_TARGET_NAME are C string literals that normally
+ // contain the label of the target being linked. However, they are set
+ // differently when using shared native deps. In that case, a single .so file
+ // is shared by multiple targets, and its contents cannot depend on which
+ // target(s) were specified on the command line. So in that case we have
+ // to use the (obscure) name of the .so file instead, or more precisely
+ // the path of the .so file relative to the workspace root.
+ .add("G3_VERSION_INFO=\"${LABEL}\"")
+ .add("G3_TARGET_NAME=\"${LABEL}\"")
+ // G3_BUILD_TARGET is a C string literal containing the output of this
+ // link. (An undocumented and untested invariant is that G3_BUILD_TARGET is the location of
+ // the executable, either absolutely, or relative to the directory part of BUILD_INFO.)
+ .add("G3_BUILD_TARGET=\"${OUTPUT_PATH}\"")
+ .addAll(additionalLinkstampDefines);
+
+ if (fdoBuildStamp != null) {
+ defines.add(CppConfiguration.FDO_STAMP_MACRO + "=\"" + fdoBuildStamp + "\"");
+ }
+
+ return defines
+ .build()
+ .stream()
+ .map(
+ define ->
+ define
+ .replaceAll(labelPattern, labelReplacement)
+ .replaceAll(outputPathPattern, outputPathReplacement))
+ .collect(ImmutableList.toImmutableList());
}
/** A builder for a {@link LinkCommandLine}. */
public static final class Builder {
private final BuildConfiguration configuration;
+ private final ActionOwner owner;
private final RuleContext ruleContext;
+
private String forcedToolPath;
+ @Nullable private Artifact output;
private ImmutableList<Artifact> buildInfoHeaderArtifacts = ImmutableList.of();
private Iterable<? extends LinkerInput> linkerInputs = ImmutableList.of();
private Iterable<? extends LinkerInput> runtimeInputs = ImmutableList.of();
@@ -399,30 +648,37 @@ public final class LinkCommandLine extends CommandLine {
private LinkStaticness linkStaticness = LinkStaticness.FULLY_STATIC;
private ImmutableList<String> linkopts = ImmutableList.of();
private ImmutableSet<String> features = ImmutableSet.of();
+ private ImmutableMap<Artifact, Artifact> linkstamps = ImmutableMap.of();
+ private ImmutableList<String> additionalLinkstampDefines = ImmutableList.of();
@Nullable private PathFragment runtimeSolibDir;
private boolean nativeDeps;
private boolean useTestOnlyFlags;
@Nullable private Artifact paramFile;
private CcToolchainProvider toolchain;
+ private FdoSupport fdoSupport;
private Variables variables;
private FeatureConfiguration featureConfiguration;
// 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, RuleContext ruleContext) {
+ public Builder(BuildConfiguration configuration, ActionOwner owner, RuleContext ruleContext) {
this.configuration = configuration;
+ this.owner = owner;
this.ruleContext = ruleContext;
}
public Builder(RuleContext ruleContext) {
- this(ruleContext.getConfiguration(), ruleContext);
+ this(ruleContext.getConfiguration(), ruleContext.getActionOwner(), ruleContext);
}
public LinkCommandLine build() {
if (linkTargetType.staticness() == Staticness.STATIC) {
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");
}
@@ -436,6 +692,11 @@ public final class LinkCommandLine extends CommandLine {
// The ruleContext can be null for some tests.
if (ruleContext != null) {
Preconditions.checkNotNull(featureConfiguration);
+
+ if (fdoSupport == null) {
+ fdoSupport =
+ CppHelper.getFdoSupportUsingDefaultCcToolchainAttribute(ruleContext).getFdoSupport();
+ }
}
if (variables == null) {
@@ -448,6 +709,8 @@ public final class LinkCommandLine extends CommandLine {
actionName,
forcedToolPath,
configuration,
+ owner,
+ output,
buildInfoHeaderArtifacts,
linkerInputs,
runtimeInputs,
@@ -455,6 +718,9 @@ public final class LinkCommandLine extends CommandLine {
linkStaticness,
linkopts,
features,
+ linkstamps,
+ additionalLinkstampDefines,
+ CppHelper.getFdoBuildStamp(ruleContext, fdoSupport),
runtimeSolibDir,
nativeDeps,
useTestOnlyFlags,
@@ -473,6 +739,11 @@ public final class LinkCommandLine extends CommandLine {
return this;
}
+ public Builder setFdoSupport(FdoSupport fdoSupport) {
+ this.fdoSupport = fdoSupport;
+ return this;
+ }
+
/** Use given tool path instead of the one from feature configuration */
public Builder forceToolPath(String forcedToolPath) {
this.forcedToolPath = forcedToolPath;
@@ -497,6 +768,12 @@ public final class LinkCommandLine extends CommandLine {
return this;
}
+ /** Sets the primary output artifact. This must be called before calling {@link #build}. */
+ public Builder setOutput(Artifact output) {
+ this.output = output;
+ return this;
+ }
+
/**
* Sets a list of linker inputs. These get turned into linker options depending on the
* staticness and the target type. This call makes an immutable copy of the inputs, if the
@@ -535,6 +812,22 @@ public final class LinkCommandLine extends CommandLine {
}
/**
+ * Sets the linkstamps. Linkstamps are additional C++ source files that are compiled as part of
+ * the link command. The {@link #build} method throws an exception if the linkstamps are
+ * non-empty for a static link (see {@link LinkTargetType#staticness()}}).
+ */
+ public Builder setLinkstamps(ImmutableMap<Artifact, Artifact> linkstamps) {
+ this.linkstamps = linkstamps;
+ return this;
+ }
+
+ /** Adds the given list of preprocessor defines to the linkstamp compilation. */
+ public Builder setAdditionalLinkstampDefines(ImmutableList<String> additionalLinkstampDefines) {
+ this.additionalLinkstampDefines = Preconditions.checkNotNull(additionalLinkstampDefines);
+ return this;
+ }
+
+ /**
* The build info header artifacts are generated header files that are used for link stamping.
* The {@link #build} method throws an exception if the build info header artifacts are
* non-empty for a static link (see {@link LinkTargetType#staticness()}}).