// 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.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; 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.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; /** * Represents the command line of a linker invocation. It supports executables and dynamic * libraries as well as static libraries. */ @Immutable 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 buildInfoHeaderArtifacts; private final Iterable linkerInputs; private final Iterable runtimeInputs; private final LinkTargetType linkTargetType; private final LinkStaticness linkStaticness; private final ImmutableList linkopts; private final ImmutableSet features; private final ImmutableMap linkstamps; private final ImmutableList additionalLinkstampDefines; @Nullable private final String fdoBuildStamp; @Nullable private final PathFragment runtimeSolibDir; private final boolean nativeDeps; private final boolean useTestOnlyFlags; private final CcToolchainProvider ccProvider; @Nullable private final Artifact paramFile; private LinkCommandLine( String actionName, String forcedToolPath, BuildConfiguration configuration, ActionOwner owner, Artifact output, ImmutableList buildInfoHeaderArtifacts, Iterable linkerInputs, Iterable runtimeInputs, LinkTargetType linkTargetType, LinkStaticness linkStaticness, ImmutableList linkopts, ImmutableSet features, ImmutableMap linkstamps, ImmutableList additionalLinkstampDefines, @Nullable String fdoBuildStamp, @Nullable PathFragment runtimeSolibDir, boolean nativeDeps, boolean useTestOnlyFlags, @Nullable Artifact paramFile, CcToolchainFeatures.Variables variables, @Nullable FeatureConfiguration featureConfiguration, CcToolchainProvider ccProvider) { 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); this.linkTargetType = Preconditions.checkNotNull(linkTargetType); this.linkStaticness = Preconditions.checkNotNull(linkStaticness); // For now, silently ignore linkopts if this is a static library link. this.linkopts = linkTargetType.staticness() == Staticness.STATIC ? 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; this.paramFile = paramFile; this.ccProvider = ccProvider; } @Nullable public Artifact getParamFile() { return paramFile; } /** See {@link CppLinkAction#getBuildInfoHeaderArtifacts()} */ public ImmutableList getBuildInfoHeaderArtifacts() { return buildInfoHeaderArtifacts; } /** * Returns the (ordered, immutable) list of paths to the linker's input files. */ public Iterable getLinkerInputs() { return linkerInputs; } /** * Returns the runtime inputs to the linker. */ public Iterable getRuntimeInputs() { return runtimeInputs; } /** * Returns the current type of link target set. */ public LinkTargetType getLinkTargetType() { return linkTargetType; } /** * Returns the "staticness" of the link. */ public LinkStaticness getLinkStaticness() { return linkStaticness; } /** * Returns the additional linker options for this link. */ public ImmutableList getLinkopts() { return linkopts; } /** See {@link CppLinkAction#getLinkstamps()} */ protected ImmutableMap 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 * regular solib directory. */ @Nullable public PathFragment getRuntimeSolibDir() { return runtimeSolibDir; } /** * Returns true for libraries linked as native dependencies for other languages. */ public boolean isNativeDeps() { return nativeDeps; } /** * Returns true if this link should use test-specific flags (e.g. $EXEC_ORIGIN as the root for * finding shared libraries or lazy binding); false by default. See bug "Please use * $EXEC_ORIGIN instead of $ORIGIN when linking cc_tests" for further context. */ public boolean useTestOnlyFlags() { 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 * if getParamFile() is not null. */ @VisibleForTesting final Pair, List> splitCommandline() { List args = getRawLinkArgv(); if (linkTargetType.staticness() == Staticness.STATIC) { // Ar link commands can also generate huge command lines. List paramFileArgs = new ArrayList<>(); List commandlineArgs = new ArrayList<>(); extractArgumentsForStaticLinkParamFile(args, commandlineArgs, paramFileArgs); return Pair.of(commandlineArgs, paramFileArgs); } else { // Gcc link commands tend to generate humongous commandlines for some targets, which may // not fit on some remote execution machines. To work around this we will employ the help of // a parameter file and pass any linker options through it. List paramFileArgs = new ArrayList<>(); List commandlineArgs = new ArrayList<>(); extractArgumentsForDynamicLinkParamFile(args, commandlineArgs, paramFileArgs); return Pair.of(commandlineArgs, paramFileArgs); } } /** * Returns just the .params file portion of the command-line as a {@link CommandLine}. */ CommandLine paramCmdLine() { Preconditions.checkNotNull(paramFile); return new CommandLine() { @Override public Iterable arguments() { return splitCommandline().getSecond(); } }; } public static void extractArgumentsForStaticLinkParamFile( List args, List commandlineArgs, List paramFileArgs) { commandlineArgs.add(args.get(0)); // ar command, must not be moved! int argsSize = args.size(); for (int i = 1; i < argsSize; i++) { String arg = args.get(i); if (arg.startsWith("@")) { commandlineArgs.add(arg); // params file, keep it in the command line } else { paramFileArgs.add(arg); // the rest goes to the params file } } } public static void extractArgumentsForDynamicLinkParamFile( List args, List commandlineArgs, List paramFileArgs) { // Note, that it is not important that all linker arguments are extracted so that // they can be moved into a parameter file, but the vast majority should. commandlineArgs.add(args.get(0)); // gcc command, must not be moved! int argsSize = args.size(); for (int i = 1; i < argsSize; i++) { String arg = args.get(i); if (arg.equals("-Wl,-no-whole-archive")) { paramFileArgs.add("-no-whole-archive"); } else if (arg.equals("-Wl,-whole-archive")) { paramFileArgs.add("-whole-archive"); } else if (arg.equals("-Wl,--start-group")) { paramFileArgs.add("--start-group"); } else if (arg.equals("-Wl,--end-group")) { paramFileArgs.add("--end-group"); } else if (arg.equals("-Wl,--start-lib")) { paramFileArgs.add("--start-lib"); } else if (arg.equals("-Wl,--end-lib")) { paramFileArgs.add("--end-lib"); } else if (arg.equals("--incremental-unchanged")) { paramFileArgs.add(arg); } else if (arg.equals("--incremental-changed")) { paramFileArgs.add(arg); } else if (arg.charAt(0) == '-') { if (arg.startsWith("-l")) { paramFileArgs.add(arg); } else { // Anything else starting with a '-' can stay on the commandline. commandlineArgs.add(arg); if (arg.equals("-o")) { // Special case for '-o': add the following argument as well - it is the output file! commandlineArgs.add(args.get(++i)); } } } else if (arg.endsWith(".a") || arg.endsWith(".lo") || arg.endsWith(".so") || arg.endsWith(".ifso") || arg.endsWith(".o") || CppFileTypes.VERSIONED_SHARED_LIBRARY.matches(arg)) { // All objects of any kind go into the linker parameters. paramFileArgs.add(arg); } else { // Everything that's left stays conservatively on the commandline. commandlineArgs.add(arg); } } } private ImmutableList getToolchainFlags() { if (Staticness.STATIC.equals(linkTargetType.staticness())) { return ImmutableList.of(); } boolean fullyStatic = (linkStaticness == LinkStaticness.FULLY_STATIC); boolean mostlyStatic = (linkStaticness == LinkStaticness.MOSTLY_STATIC); boolean sharedLinkopts = linkTargetType == LinkTargetType.DYNAMIC_LIBRARY || linkopts.contains("-shared") || cppConfiguration.hasSharedLinkOption(); List toolchainFlags = new ArrayList<>(); /* * 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. */ toolchainFlags.addAll(linkopts); // Extra toolchain link options based on the output's link staticness. if (fullyStatic) { toolchainFlags.addAll(cppConfiguration.getFullyStaticLinkOptions(features, sharedLinkopts)); } else if (mostlyStatic) { toolchainFlags.addAll(cppConfiguration.getMostlyStaticLinkOptions(features, sharedLinkopts)); } else { toolchainFlags.addAll(cppConfiguration.getDynamicLinkOptions(features, sharedLinkopts)); } // Extra test-specific link options. if (useTestOnlyFlags) { toolchainFlags.addAll(ccProvider.getTestOnlyLinkOptions()); } toolchainFlags.addAll(ccProvider.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(toolchainFlags, Predicates.equalTo("-pie")); } return ImmutableList.copyOf(toolchainFlags); } /** * 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 getRawLinkArgv() { List argv = new ArrayList<>(); if (forcedToolPath != null) { argv.add(forcedToolPath); } else { Preconditions.checkArgument( featureConfiguration.actionIsConfigured(actionName), String.format("Expected action_config for '%s' to be configured", actionName)); argv.add( featureConfiguration .getToolForAction(linkTargetType.getActionName()) .getToolPath(cppConfiguration.getCrosstoolTopPathFragment()) .getPathString()); } argv.addAll( featureConfiguration.getCommandLine( actionName, new Variables.Builder(variables) .addStringSequenceVariable( CppLinkActionBuilder.LEGACY_LINK_FLAGS_VARIABLE, getToolchainFlags()) .build())); return argv; } List getCommandLine() { List 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> split = splitCommandline(); commandlineArgs = split.first; } else { commandlineArgs = getRawLinkArgv(); } return finalizeWithLinkstampCommands(commandlineArgs); } @Override public List arguments() { 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 finalizeWithLinkstampCommands(List 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 finalizeAlreadyEscapedWithLinkstampCommands( List 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. * *

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. * *

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 addLinkstampingToCommand( List linkstampCommands, List linkCommand, boolean escapeArgs) { if (linkstampCommands.isEmpty()) { return linkCommand; } else { List 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. * *

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 getLinkstampCompileCommands(String outputPrefix) { if (linkstamps.isEmpty()) { return ImmutableList.of(); } List commands = Lists.newArrayListWithCapacity(linkstamps.size()); for (Map.Entry linkstamp : linkstamps.entrySet()) { Artifact sourceFile = linkstamp.getKey(); Artifact outputFile = linkstamp.getValue(); Variables linkstampVariables = collectLinkstampVariables(sourceFile, outputFile); ImmutableList.Builder 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 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 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 buildInfoHeaderArtifacts = ImmutableList.of(); private Iterable linkerInputs = ImmutableList.of(); private Iterable runtimeInputs = ImmutableList.of(); @Nullable private LinkTargetType linkTargetType; private LinkStaticness linkStaticness = LinkStaticness.FULLY_STATIC; private ImmutableList linkopts = ImmutableList.of(); private ImmutableSet features = ImmutableSet.of(); private ImmutableMap linkstamps = ImmutableMap.of(); private ImmutableList 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, ActionOwner owner, RuleContext ruleContext) { this.configuration = configuration; this.owner = owner; this.ruleContext = ruleContext; } public Builder(RuleContext 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"); } if (toolchain == null) { toolchain = Preconditions.checkNotNull( CppHelper.getToolchainUsingDefaultCcToolchainAttribute(ruleContext)); } // 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) { variables = Variables.EMPTY; } String actionName = linkTargetType.getActionName(); return new LinkCommandLine( actionName, forcedToolPath, configuration, owner, output, buildInfoHeaderArtifacts, linkerInputs, runtimeInputs, linkTargetType, linkStaticness, linkopts, features, linkstamps, additionalLinkstampDefines, CppHelper.getFdoBuildStamp(ruleContext, fdoSupport), runtimeSolibDir, nativeDeps, useTestOnlyFlags, paramFile, variables, featureConfiguration, toolchain); } /** * Sets the toolchain to use for link flags. If this is not called, the toolchain * is retrieved from the rule. */ public Builder setToolchain(CcToolchainProvider toolchain) { this.toolchain = toolchain; 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; return this; } /** Sets the feature configuration for this link action. */ public Builder setFeatureConfiguration(FeatureConfiguration featureConfiguration) { this.featureConfiguration = featureConfiguration; return this; } /** * Sets the type of the link. It is an error to try to set this to {@link * LinkTargetType#INTERFACE_DYNAMIC_LIBRARY}. Note that all the static target types (see {@link * LinkTargetType#staticness}) are equivalent, and there is no check that the output * artifact matches the target type extension. */ public Builder setLinkTargetType(LinkTargetType linkTargetType) { Preconditions.checkArgument(linkTargetType != LinkTargetType.INTERFACE_DYNAMIC_LIBRARY); this.linkTargetType = linkTargetType; 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 * provided Iterable isn't already immutable (see {@link CollectionUtils#makeImmutable}). */ public Builder setLinkerInputs(Iterable linkerInputs) { this.linkerInputs = CollectionUtils.makeImmutable(linkerInputs); return this; } public Builder setRuntimeInputs(ImmutableList runtimeInputs) { this.runtimeInputs = runtimeInputs; 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 * LinkTargetType#staticness()}). */ public Builder setLinkopts(ImmutableList linkopts) { this.linkopts = linkopts; return this; } /** * Sets how static the link is supposed to be. For static target types (see {@link * LinkTargetType#staticness()}}), the {@link #build} method throws an exception if this * is not {@link LinkStaticness#FULLY_STATIC}. The default setting is {@link * LinkStaticness#FULLY_STATIC}. */ public Builder setLinkStaticness(LinkStaticness linkStaticness) { this.linkStaticness = linkStaticness; return this; } /** * 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 linkstamps) { this.linkstamps = linkstamps; return this; } /** Adds the given list of preprocessor defines to the linkstamp compilation. */ public Builder setAdditionalLinkstampDefines(ImmutableList 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()}}). */ public Builder setBuildInfoHeaderArtifacts(ImmutableList buildInfoHeaderArtifacts) { this.buildInfoHeaderArtifacts = buildInfoHeaderArtifacts; return this; } /** * Sets the features enabled for the rule. */ public Builder setFeatures(ImmutableSet features) { this.features = features; 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#staticness()}}). */ public Builder setNativeDeps(boolean nativeDeps) { this.nativeDeps = nativeDeps; return this; } /** * Sets whether to use test-specific linker flags, e.g. {@code $EXEC_ORIGIN} instead of * {@code $ORIGIN} in the rpath or lazy binding. */ public Builder setUseTestOnlyFlags(boolean useTestOnlyFlags) { this.useTestOnlyFlags = useTestOnlyFlags; return this; } public Builder setParamFile(Artifact paramFile) { this.paramFile = paramFile; return this; } public Builder setBuildVariables(Variables variables) { this.variables = variables; return this; } public Builder setRuntimeSolibDir(PathFragment runtimeSolibDir) { this.runtimeSolibDir = runtimeSolibDir; return this; } } }