diff options
author | 2016-08-02 09:37:05 +0000 | |
---|---|---|
committer | 2016-08-02 11:15:58 +0000 | |
commit | 5592a0aae2bebfbea278fdf8125bf26831a65975 (patch) | |
tree | 823197e039564ef999550bc631885b7135a4afe4 /src | |
parent | b93a1220f7009a972305eb19f1e4d96fbb65e40c (diff) |
Rollback of commit f61d12e9e4f940810efbaf244911a94830ba6c05.
*** Reason for rollback ***
--
MOS_MIGRATED_REVID=129078102
Diffstat (limited to 'src')
9 files changed, 606 insertions, 1271 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 f25fc94e79..a763713938 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,6 +228,10 @@ 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 f935b4faac..9ba846a701 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 @@ -33,6 +33,7 @@ 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; @@ -755,17 +756,15 @@ 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 { @@ -1090,8 +1089,10 @@ public class CcToolchainFeatures implements Serializable { return new NestedView(viewMap, sequenceName, sequenceVariables.get(sequenceName)); } - /** Returns whether {@code variable} is set. */ - boolean isAvailable(String variable) { + /** + * Returns whether {@code variable} is set. + */ + private 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 03212b9fc2..1e14df9dc6 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,7 +38,6 @@ 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; @@ -54,6 +53,7 @@ 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; @@ -683,7 +683,6 @@ public class CppConfiguration extends BuildConfiguration.Fragment { return result.build(); } - private boolean linkActionsAreConfigured(CToolchain toolchain) { for (LinkTargetType type : LinkTargetType.values()) { boolean typeIsConfigured = false; @@ -714,17 +713,6 @@ 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) @@ -869,8 +857,12 @@ 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}'" @@ -956,8 +948,12 @@ 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 cda2e26233..3ec9843777 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,7 +17,6 @@ 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; @@ -38,8 +37,6 @@ 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; @@ -48,6 +45,7 @@ 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; @@ -55,57 +53,11 @@ 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; @@ -120,6 +72,8 @@ 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}. @@ -242,8 +196,10 @@ 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; } @@ -445,17 +401,6 @@ 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 = @@ -491,24 +436,6 @@ 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"); @@ -519,52 +446,28 @@ 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(runtimeLinkerInputs) + .setRuntimeInputs(ImmutableList.copyOf(LinkerInputs.simpleLinkerInputs(runtimeInputs))) .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) @@ -603,6 +506,13 @@ 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( @@ -635,7 +545,7 @@ public class CppLinkActionBuilder { for (LTOBackendArtifacts a : allLTOArtifacts) { List<String> argv = new ArrayList<>(); argv.addAll(cppConfiguration.getLinkOptions()); - argv.addAll(featureConfiguration.getCommandLine(getActionName(), Variables.EMPTY)); + argv.addAll(featureConfiguration.getCommandLine(getActionName(), buildVariables)); argv.addAll(cppConfiguration.getCompilerOptions(features)); a.setCommandLine(argv); } @@ -774,6 +684,12 @@ 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. * @@ -1020,439 +936,4 @@ 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)) { - 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 deleted file mode 100644 index a359350834..0000000000 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppLinkActionConfigs.java +++ /dev/null @@ -1,297 +0,0 @@ -// 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 13866e81d5..d3cbeaf3e0 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,6 +41,7 @@ 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; @@ -48,6 +49,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Pattern; + import javax.annotation.Nullable; /** @@ -360,6 +362,11 @@ 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, @@ -837,6 +844,7 @@ public final class CppModel { .setLinkType(linkType) .setLinkStaticness(LinkStaticness.FULLY_STATIC) .setFeatureConfiguration(featureConfiguration) + .setBuildVariables(linkBuildVariables()) .build(); env.registerAction(maybePicAction); result.addStaticLibrary(maybePicAction.getOutputLibrary()); @@ -861,6 +869,7 @@ public final class CppModel { .setLinkType(picLinkType) .setLinkStaticness(LinkStaticness.FULLY_STATIC) .setFeatureConfiguration(featureConfiguration) + .setBuildVariables(linkBuildVariables()) .build(); env.registerAction(picAction); result.addPicStaticLibrary(picAction.getOutputLibrary()); @@ -904,7 +913,8 @@ public final class CppModel { .setRuntimeInputs( CppHelper.getToolchain(ruleContext).getDynamicRuntimeLinkMiddleman(), CppHelper.getToolchain(ruleContext).getDynamicRuntimeLinkInputs()) - .setFeatureConfiguration(featureConfiguration); + .setFeatureConfiguration(featureConfiguration) + .setBuildVariables(linkBuildVariables()); 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 c5cb3017ed..96b5a3bb3c 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,6 +17,7 @@ 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; @@ -31,18 +32,23 @@ 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; /** @@ -60,6 +66,7 @@ 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; @@ -73,8 +80,9 @@ public final class LinkCommandLine extends CommandLine { @Nullable private final PathFragment runtimeSolibDir; private final boolean nativeDeps; private final boolean useTestOnlyFlags; - private final List<String> noWholeArchiveFlags; + private final boolean needWholeArchive; + @Nullable private final Iterable<LTOBackendArtifacts> allLTOArtifacts; @Nullable private final Artifact paramFile; @Nullable private final Artifact interfaceSoBuilder; @@ -84,6 +92,7 @@ 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, @@ -98,11 +107,33 @@ 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); @@ -116,6 +147,7 @@ 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); @@ -132,15 +164,16 @@ 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; } /** @@ -153,6 +186,15 @@ 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; @@ -236,12 +278,6 @@ 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 @@ -335,110 +371,76 @@ 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<>(); - 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()); + // 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)); argv.add(output.getExecPathString()); - 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(); + 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"); } return argv; @@ -535,23 +537,21 @@ 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,6 +626,369 @@ 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 { @@ -650,8 +1013,10 @@ 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(); @@ -665,18 +1030,17 @@ 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; @@ -687,16 +1051,6 @@ 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; @@ -704,7 +1058,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) { @@ -716,20 +1070,18 @@ 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, @@ -744,14 +1096,22 @@ 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. */ @@ -814,9 +1174,19 @@ 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) { @@ -883,6 +1253,16 @@ 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}). @@ -901,28 +1281,18 @@ public final class LinkCommandLine extends CommandLine { return this; } - public Builder setParamFile(Artifact paramFile) { - this.paramFile = paramFile; - return this; - } - - public Builder setBuildVariables(Variables variables) { - this.variables = variables; + public Builder setNeedWholeArchive(boolean needWholeArchive) { + this.needWholeArchive = needWholeArchive; return this; } - public Builder setRuntimeSolibDir(PathFragment runtimeSolibDir) { - this.runtimeSolibDir = runtimeSolibDir; + public Builder setParamFile(Artifact paramFile) { + this.paramFile = paramFile; return this; } - /** - * 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; + public Builder setAllLTOArtifacts(Iterable<LTOBackendArtifacts> allLTOArtifacts) { + this.allLTOBackendArtifacts = allLTOArtifacts; return this; } } diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/CppLinkActionTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/CppLinkActionTest.java index 7531632e8a..c4ea2be93f 100644 --- a/src/test/java/com/google/devtools/build/lib/rules/cpp/CppLinkActionTest.java +++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/CppLinkActionTest.java @@ -17,8 +17,8 @@ package com.google.devtools.build.lib.rules.cpp; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import com.google.common.base.Joiner; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.devtools.build.lib.actions.Action; @@ -34,17 +34,18 @@ import com.google.devtools.build.lib.analysis.util.BuildViewTestCase; import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; import com.google.devtools.build.lib.collect.nestedset.Order; import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.FeatureConfiguration; -import com.google.devtools.build.lib.rules.cpp.CppLinkActionConfigs.CppLinkPlatform; 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.LinkerInputs.LibraryToLink; import com.google.devtools.build.lib.vfs.PathFragment; -import java.io.IOException; + import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; -/** Tests for {@link CppLinkAction}. */ +/** + * Tests for {@link CppLinkAction}. + */ @RunWith(JUnit4.class) public class CppLinkActionTest extends BuildViewTestCase { private RuleContext createDummyRuleContext() throws Exception { @@ -65,18 +66,6 @@ public class CppLinkActionTest extends BuildViewTestCase { } }, masterConfig); } - - private final FeatureConfiguration getMockFeatureConfiguration() throws Exception { - return CcToolchainFeaturesTest.buildFeatures( - CppLinkActionConfigs.getCppLinkActionConfigs(CppLinkPlatform.LINUX)) - .getFeatureConfiguration( - Link.LinkTargetType.EXECUTABLE.getActionName(), - Link.LinkTargetType.DYNAMIC_LIBRARY.getActionName(), - Link.LinkTargetType.STATIC_LIBRARY.getActionName(), - Link.LinkTargetType.PIC_STATIC_LIBRARY.getActionName(), - Link.LinkTargetType.ALWAYS_LINK_STATIC_LIBRARY.getActionName(), - Link.LinkTargetType.ALWAYS_LINK_PIC_STATIC_LIBRARY.getActionName()); - } @Test public void testToolchainFeatureFlags() throws Exception { @@ -104,9 +93,9 @@ public class CppLinkActionTest extends BuildViewTestCase { "out", ImmutableList.<Artifact>of(), ImmutableList.<LibraryToLink>of(), - featureConfiguration, - false) + featureConfiguration) .build(); + assertThat(Joiner.on(" ").join(linkAction.getArgv())).contains("mock_tool"); assertThat(linkAction.getArgv()).contains("some_flag"); } @@ -129,8 +118,7 @@ public class CppLinkActionTest extends BuildViewTestCase { "out", ImmutableList.<Artifact>of(), ImmutableList.<LibraryToLink>of(), - featureConfiguration, - false) + featureConfiguration) .build(); assertThat(linkAction.getEnvironment()).containsEntry("foo", "bar"); } @@ -147,8 +135,6 @@ public class CppLinkActionTest extends BuildViewTestCase { final Artifact oFile = getSourceArtifact("cc/a.o"); final Artifact oFile2 = getSourceArtifact("cc/a2.o"); final Artifact interfaceSoBuilder = getBinArtifactWithNoOwner("foo/build_interface_so"); - final FeatureConfiguration featureConfiguration = getMockFeatureConfiguration(); - ActionTester.runTest( 128, new ActionCombinationFactory() { @@ -172,7 +158,7 @@ public class CppLinkActionTest extends BuildViewTestCase { builder.setWholeArchive((i & 16) == 0); builder.setFake((i & 32) == 0); builder.setRuntimeSolibDir((i & 64) == 0 ? null : new PathFragment("so1")); - builder.setFeatureConfiguration(featureConfiguration); + builder.setFeatureConfiguration(new FeatureConfiguration()); return builder.build(); } @@ -191,8 +177,6 @@ public class CppLinkActionTest extends BuildViewTestCase { final Artifact oFile = getSourceArtifact("cc/a.o"); final Artifact oFile2 = getSourceArtifact("cc/a2.o"); final Artifact interfaceSoBuilder = getBinArtifactWithNoOwner("foo/build_interface_so"); - final FeatureConfiguration featureConfiguration = getMockFeatureConfiguration(); - ActionTester.runTest( 4, new ActionCombinationFactory() { @@ -210,7 +194,6 @@ public class CppLinkActionTest extends BuildViewTestCase { (i & 1) == 0 ? ImmutableList.of(oFile) : ImmutableList.of(oFile2)); builder.setLinkType( (i & 2) == 0 ? LinkTargetType.STATIC_LIBRARY : LinkTargetType.DYNAMIC_LIBRARY); - builder.setFeatureConfiguration(featureConfiguration); return builder.build(); } }); @@ -271,8 +254,7 @@ public class CppLinkActionTest extends BuildViewTestCase { "binary2", objects.build(), ImmutableList.<LibraryToLink>of(), - new FeatureConfiguration(), - false) + new FeatureConfiguration()) .setFake(true) .build(); @@ -303,8 +285,7 @@ public class CppLinkActionTest extends BuildViewTestCase { String outputPath, Iterable<Artifact> nonLibraryInputs, ImmutableList<LibraryToLink> libraryInputs, - FeatureConfiguration featureConfiguration, - boolean shouldIncludeToolchain) + FeatureConfiguration featureConfiguration) throws Exception { RuleContext ruleContext = createDummyRuleContext(); CppLinkActionBuilder builder = @@ -313,7 +294,7 @@ public class CppLinkActionTest extends BuildViewTestCase { new Artifact( new PathFragment(outputPath), getTargetConfiguration().getBinDirectory()), ruleContext.getConfiguration(), - shouldIncludeToolchain ? CppHelper.getToolchain(ruleContext) : null) + null) .addNonLibraryInputs(nonLibraryInputs) .addLibraries(NestedSetBuilder.wrap(Order.LINK_ORDER, libraryInputs)) .setLinkType(type) @@ -325,17 +306,6 @@ public class CppLinkActionTest extends BuildViewTestCase { .setFeatureConfiguration(featureConfiguration); return builder; } - - private CppLinkActionBuilder createLinkBuilder(Link.LinkTargetType type) throws Exception { - PathFragment output = new PathFragment("dummyRuleContext/output/path.xyz"); - return createLinkBuilder( - type, - output.getPathString(), - ImmutableList.<Artifact>of(), - ImmutableList.<LibraryToLink>of(), - new FeatureConfiguration(), - true); - } public Artifact getOutputArtifact(String relpath) { return new Artifact( @@ -343,70 +313,4 @@ public class CppLinkActionTest extends BuildViewTestCase { getTargetConfiguration().getBinDirectory(), getTargetConfiguration().getBinFragment().getRelative(relpath)); } - - private Artifact scratchArtifact(String s) { - try { - return new Artifact( - scratch.overwriteFile(outputBase.getRelative("WORKSPACE").getRelative(s).toString()), - Root.asDerivedRoot(scratch.dir(outputBase.getRelative("WORKSPACE").toString()))); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - private void assertError(String expectedSubstring, CppLinkActionBuilder builder) { - try { - builder.build(); - fail(); - } catch (RuntimeException e) { - assertThat(e.getMessage()).contains(expectedSubstring); - } - } - - @Test - public void testInterfaceOutputWithoutBuildingDynamicLibraryIsError() throws Exception { - CppLinkActionBuilder builder = - createLinkBuilder(LinkTargetType.EXECUTABLE) - .setInterfaceOutput(scratchArtifact("FakeInterfaceOutput")); - - assertError("Interface output can only be used with non-fake DYNAMIC_LIBRARY targets", builder); - } - - @Test - public void testStaticLinkWithDynamicIsError() throws Exception { - CppLinkActionBuilder builder = - createLinkBuilder(LinkTargetType.STATIC_LIBRARY).setLinkStaticness(LinkStaticness.DYNAMIC); - - assertError("static library link must be static", builder); - } - - @Test - public void testStaticLinkWithSymbolsCountOutputIsError() throws Exception { - CppLinkActionBuilder builder = - createLinkBuilder(LinkTargetType.STATIC_LIBRARY) - .setLinkStaticness(LinkStaticness.FULLY_STATIC) - .setSymbolCountsOutput(scratchArtifact("dummySymbolCounts")); - - assertError("the symbol counts output must be null for static links", builder); - } - - @Test - public void testStaticLinkWithNativeDepsIsError() throws Exception { - CppLinkActionBuilder builder = - createLinkBuilder(LinkTargetType.STATIC_LIBRARY) - .setLinkStaticness(LinkStaticness.FULLY_STATIC) - .setNativeDeps(true); - - assertError("the native deps flag must be false for static links", builder); - } - - @Test - public void testStaticLinkWithWholeArchiveIsError() throws Exception { - CppLinkActionBuilder builder = - createLinkBuilder(LinkTargetType.STATIC_LIBRARY) - .setLinkStaticness(LinkStaticness.FULLY_STATIC) - .setWholeArchive(true); - - assertError("the need whole archive flag must be false for static links", builder); - } } diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/LinkBuildVariablesTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/LinkBuildVariablesTest.java deleted file mode 100644 index 50fdebad0f..0000000000 --- a/src/test/java/com/google/devtools/build/lib/rules/cpp/LinkBuildVariablesTest.java +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright 2016 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 static com.google.common.truth.Truth.assertThat; - -import com.google.common.collect.Iterables; -import com.google.devtools.build.lib.actions.Artifact; -import com.google.devtools.build.lib.analysis.ConfiguredTarget; -import com.google.devtools.build.lib.analysis.util.BuildViewTestCase; -import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.FeatureConfiguration; -import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.Variables; -import java.util.List; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -/** Tests that {@code CppLinkAction} is populated with the correct build variables. */ -@RunWith(JUnit4.class) -public class LinkBuildVariablesTest extends BuildViewTestCase { - - private CppLinkAction getCppLinkAction(ConfiguredTarget target, Link.LinkTargetType type) { - Artifact linkerOutput = null; - switch (type) { - case STATIC_LIBRARY: - case ALWAYS_LINK_STATIC_LIBRARY: - linkerOutput = getBinArtifact("lib" + target.getLabel().getName() + ".a", target); - break; - case PIC_STATIC_LIBRARY: - case ALWAYS_LINK_PIC_STATIC_LIBRARY: - linkerOutput = getBinArtifact("lib" + target.getLabel().getName() + "pic.a", target); - break; - case DYNAMIC_LIBRARY: - linkerOutput = getBinArtifact("lib" + target.getLabel().getName() + ".so", target); - break; - case EXECUTABLE: - linkerOutput = getExecutable(target); - break; - default: - throw new IllegalArgumentException( - String.format("Cannot get CppLinkAction for link type %s", type)); - } - return (CppLinkAction) getGeneratingAction(linkerOutput); - } - - private Variables getLinkBuildVariables(ConfiguredTarget target, Link.LinkTargetType type) { - return getCppLinkAction(target, type).getLinkCommandLine().getBuildVariables(); - } - - private List<String> getVariableValue(Variables variables, String variable) throws Exception { - FeatureConfiguration mockFeatureConfiguration = - CcToolchainFeaturesTest.buildFeatures( - "feature {", - " name: 'a'", - " flag_set {", - " action: 'foo'", - " flag_group {", - " flag: '%{" + variable + "}'", - " }", - " }", - "}") - .getFeatureConfiguration("a"); - return mockFeatureConfiguration.getCommandLine("foo", variables); - } - - @Test - public void testLinkstampBuildVariable() throws Exception { - scratch.file( - "x/BUILD", - "cc_binary(", - " name = 'bin',", - " srcs = ['a.cc'],", - " deps = [':lib'],", - ")", - "cc_library(", - " name = 'lib',", - " srcs = ['b.cc'],", - " linkstamp = 'c.cc',", - ")"); - scratch.file("x/a.cc"); - scratch.file("x/b.cc"); - scratch.file("x/c.cc"); - - ConfiguredTarget target = getConfiguredTarget("//x:bin"); - Variables variables = getLinkBuildVariables(target, Link.LinkTargetType.EXECUTABLE); - List<String> variableValue = - getVariableValue(variables, CppLinkActionBuilder.LINKSTAMP_PATHS_VARIABLE); - assertThat(Iterables.getOnlyElement(variableValue)).contains("c.o"); - } - - @Test - public void testForcePicBuildVariable() throws Exception { - useConfiguration("--force_pic"); - scratch.file("x/BUILD", "cc_binary(", " name = 'bin',", " srcs = ['a.cc'],", ")"); - scratch.file("x/a.cc"); - - ConfiguredTarget target = getConfiguredTarget("//x:bin"); - Variables variables = getLinkBuildVariables(target, Link.LinkTargetType.EXECUTABLE); - List<String> variableValue = - getVariableValue(variables, CppLinkActionBuilder.FORCE_PIC_VARIABLE); - assertThat(variableValue).contains(""); - } - - @Test - public void testWholeArchiveBuildVariables() throws Exception { - scratch.file( - "x/BUILD", - "cc_binary(", - " name = 'bin.so',", - " srcs = ['a.cc'],", - " linkopts = ['-shared'],", - " linkstatic = 1", - ")"); - scratch.file("x/a.cc"); - - ConfiguredTarget target = getConfiguredTarget("//x:bin.so"); - Variables variables = getLinkBuildVariables(target, Link.LinkTargetType.EXECUTABLE); - List<String> variableValue = - getVariableValue(variables, CppLinkActionBuilder.GLOBAL_WHOLE_ARCHIVE_VARIABLE); - assertThat(variableValue).contains(""); - } -} |