diff options
author | 2017-12-08 09:16:28 -0800 | |
---|---|---|
committer | 2017-12-08 09:18:34 -0800 | |
commit | 367f704e71f352b404df38161f4c367b9ff506c9 (patch) | |
tree | a9319876a96fcca04af89561263b085cc3db6ae0 /src/main/java/com/google/devtools/build/lib/rules/cpp/LinkCommandLine.java | |
parent | baa99fb3d21a9563c9da5c20b975b41111c5519f (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.java | 305 |
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()}}). |