From 60434dae746ed61f025fdfd0464993fa6a0ff1d2 Mon Sep 17 00:00:00 2001 From: Cal Peyser Date: Thu, 11 Feb 2016 15:45:46 +0000 Subject: -- MOS_MIGRATED_REVID=114436999 --- .../build/lib/rules/objc/CompilationSupport.java | 86 +++++++++++++--- .../devtools/build/lib/rules/objc/IosTest.java | 38 ++++--- .../build/lib/rules/objc/LibrariesToLink.java | 111 +++++++++++++++++++++ .../lib/rules/objc/ReleaseBundlingSupport.java | 14 ++- .../build/lib/rules/objc/XcTestAppProvider.java | 52 +++++++++- 5 files changed, 267 insertions(+), 34 deletions(-) create mode 100644 src/main/java/com/google/devtools/build/lib/rules/objc/LibrariesToLink.java (limited to 'src/main/java/com/google/devtools/build/lib/rules/objc') diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/CompilationSupport.java b/src/main/java/com/google/devtools/build/lib/rules/objc/CompilationSupport.java index 7f129f9e5c..eb7c1bf0ca 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/CompilationSupport.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/CompilationSupport.java @@ -16,7 +16,6 @@ package com.google.devtools.build.lib.rules.objc; import static com.google.devtools.build.lib.packages.ImplicitOutputsFunction.fromTemplates; import static com.google.devtools.build.lib.rules.objc.ObjcProvider.DEFINE; -import static com.google.devtools.build.lib.rules.objc.ObjcProvider.FORCE_LOAD_LIBRARY; import static com.google.devtools.build.lib.rules.objc.ObjcProvider.FRAMEWORK_DIR; import static com.google.devtools.build.lib.rules.objc.ObjcProvider.FRAMEWORK_FILE; import static com.google.devtools.build.lib.rules.objc.ObjcProvider.Flag.USES_CPP; @@ -702,6 +701,43 @@ public final class CompilationSupport { */ CompilationSupport registerLinkActions(ObjcProvider objcProvider, ExtraLinkArgs extraLinkArgs, Iterable extraLinkInputs) { + LibrariesToLink defaultLibrariesToLink = LibrariesToLink.defaultLibraries(objcProvider); + return registerLinkActions( + objcProvider, extraLinkArgs, extraLinkInputs, defaultLibrariesToLink); + } + + /** + * Registers any actions necessary to link a test rule and its dependencies, without redundantly + * linking libraries already linked into the test application. + * + *

Dsym bundle and breakpad files are generated if + * {@link ObjcConfiguration#generateDebugSymbols()} is set. + * + *

When Bazel flags {@code --compilation_mode=opt} and {@code --objc_enable_binary_stripping} + * are specified, additional optimizations will be performed on the linked binary: all-symbol + * stripping (using {@code /usr/bin/strip}) and dead-code stripping (using linker flags: + * {@code -dead_strip} and {@code -no_dead_strip_inits_and_terms}). + * + * @param objcProvider common information about this rule's attributes and its dependencies + * @param extraLinkArgs any additional arguments to pass to the linker + * @param extraLinkInputs any additional input artifacts to pass to the link action + * @param librariesToLink libraries that were not already linked into the test application + * + * @return this compilation support + */ + CompilationSupport registerLinkActionsForXcTest( + ObjcProvider objcProvider, + ExtraLinkArgs extraLinkArgs, + Iterable extraLinkInputs, + LibrariesToLink librariesToLink) { + return registerLinkActions(objcProvider, extraLinkArgs, extraLinkInputs, librariesToLink); + } + + private CompilationSupport registerLinkActions( + ObjcProvider objcProvider, + ExtraLinkArgs extraLinkArgs, + Iterable extraLinkInputs, + LibrariesToLink librariesToLink) { IntermediateArtifacts intermediateArtifacts = ObjcRuleClasses.intermediateArtifacts(ruleContext); Optional dsymBundle; @@ -724,6 +760,7 @@ public final class CompilationSupport { extraLinkArgs, extraLinkInputs, dsymBundle, + librariesToLink, prunedJ2ObjcArchives); return this; } @@ -786,8 +823,12 @@ public final class CompilationSupport { /*externDependencies=*/ true)); } - private void registerLinkAction(ObjcProvider objcProvider, ExtraLinkArgs extraLinkArgs, - Iterable extraLinkInputs, Optional dsymBundle, + private void registerLinkAction( + ObjcProvider objcProvider, + ExtraLinkArgs extraLinkArgs, + Iterable extraLinkInputs, + Optional dsymBundle, + LibrariesToLink librariesToLink, Iterable prunedJ2ObjcArchives) { ObjcConfiguration objcConfiguration = ObjcRuleClasses.objcConfiguration(ruleContext); IntermediateArtifacts intermediateArtifacts = @@ -804,18 +845,26 @@ public final class CompilationSupport { : intermediateArtifacts.strippedSingleArchitectureBinary(); ImmutableList ccLibraries = ccLibraries(objcProvider); - NestedSet bazelBuiltLibraries = Iterables.isEmpty(prunedJ2ObjcArchives) - ? objcProvider.get(LIBRARY) : substituteJ2ObjcPrunedLibraries(objcProvider); + Iterable bazelBuiltLibraries = + Iterables.isEmpty(prunedJ2ObjcArchives) + ? librariesToLink.getLibrariesToLink() + : substituteJ2ObjcPrunedLibraries(objcProvider, librariesToLink); ruleContext.registerAction( ObjcRuleClasses.spawnXcrunActionBuilder(ruleContext) .setMnemonic("ObjcLink") .setShellCommand(ImmutableList.of("/bin/bash", "-c")) .setCommandLine( - linkCommandLine(extraLinkArgs, objcProvider, binaryToLink, dsymBundle, ccLibraries, + linkCommandLine( + extraLinkArgs, + objcProvider, + binaryToLink, + dsymBundle, + ccLibraries, + librariesToLink, bazelBuiltLibraries)) .addOutput(binaryToLink) .addOutputs(dsymBundle.asSet()) - .addTransitiveInputs(bazelBuiltLibraries) + .addInputs(bazelBuiltLibraries) .addTransitiveInputs(objcProvider.get(IMPORTED_LIBRARY)) .addTransitiveInputs(objcProvider.get(FRAMEWORK_FILE)) .addInputs(ccLibraries) @@ -875,13 +924,14 @@ public final class CompilationSupport { * Returns a nested set of Bazel-built ObjC libraries with all unpruned J2ObjC libraries * substituted with pruned ones. */ - private NestedSet substituteJ2ObjcPrunedLibraries(ObjcProvider objcProvider) { + private NestedSet substituteJ2ObjcPrunedLibraries( + ObjcProvider objcProvider, LibrariesToLink librariesToLink) { ImmutableList.Builder libraries = new ImmutableList.Builder<>(); IntermediateArtifacts intermediateArtifacts = ObjcRuleClasses.intermediateArtifacts(ruleContext); Set unprunedJ2ObjcLibs = objcProvider.get(ObjcProvider.J2OBJC_LIBRARY).toSet(); - for (Artifact library : objcProvider.get(LIBRARY)) { + for (Artifact library : librariesToLink.getLibrariesToLink()) { // If we match an unpruned J2ObjC library, add the pruned version of the J2ObjC static library // instead. if (unprunedJ2ObjcLibs.contains(library)) { @@ -893,9 +943,14 @@ public final class CompilationSupport { return NestedSetBuilder.wrap(Order.NAIVE_LINK_ORDER, libraries.build()); } - private CommandLine linkCommandLine(ExtraLinkArgs extraLinkArgs, - ObjcProvider objcProvider, Artifact linkedBinary, Optional dsymBundle, - Iterable ccLibraries, Iterable bazelBuiltLibraries) { + private CommandLine linkCommandLine( + ExtraLinkArgs extraLinkArgs, + ObjcProvider objcProvider, + Artifact linkedBinary, + Optional dsymBundle, + Iterable ccLibraries, + LibrariesToLink librariesToLink, + Iterable bazelBuiltLibraries) { ObjcConfiguration objcConfiguration = ObjcRuleClasses.objcConfiguration(ruleContext); AppleConfiguration appleConfiguration = ruleContext.getFragment(AppleConfiguration.class); Iterable libraryNames = libraryNames(objcProvider); @@ -920,7 +975,7 @@ public final class CompilationSupport { if (objcConfiguration.shouldPrioritizeStaticLibs()) { commandLine .addExecPaths(bazelBuiltLibraries) - .addExecPaths(objcProvider.get(IMPORTED_LIBRARY)) + .addExecPaths(librariesToLink.getImportedLibrariesToLink()) .addExecPaths(ccLibraries); } @@ -939,13 +994,14 @@ public final class CompilationSupport { if (!objcConfiguration.shouldPrioritizeStaticLibs()) { commandLine .addExecPaths(bazelBuiltLibraries) - .addExecPaths(objcProvider.get(IMPORTED_LIBRARY)) + .addExecPaths(librariesToLink.getImportedLibrariesToLink()) .addExecPaths(ccLibraries); } commandLine .addExecPath("-o", linkedBinary) - .addBeforeEach("-force_load", Artifact.toExecPaths(objcProvider.get(FORCE_LOAD_LIBRARY))) + .addBeforeEach( + "-force_load", Artifact.toExecPaths(librariesToLink.getForceLoadLibrariesToLink())) .add(extraLinkArgs) .add(objcProvider.get(ObjcProvider.LINKOPT)) .build(); diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/IosTest.java b/src/main/java/com/google/devtools/build/lib/rules/objc/IosTest.java index cfb68733ba..891e80c1ba 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/IosTest.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/IosTest.java @@ -88,13 +88,15 @@ public final class IosTest implements RuleConfiguredTargetFactory { addResourceFilesToBuild(ruleContext, common.getObjcProvider(), filesToBuild); XcodeProductType productType = getProductType(ruleContext); - ExtraLinkArgs extraLinkArgs; - Iterable extraLinkInputs; String bundleFormat; if (!isXcTest(ruleContext)) { - extraLinkArgs = new ExtraLinkArgs(); - extraLinkInputs = ImmutableList.of(); bundleFormat = ReleaseBundlingSupport.APP_BUNDLE_DIR_FORMAT; + new CompilationSupport(ruleContext) + .registerLinkActions( + common.getObjcProvider(), new ExtraLinkArgs(), ImmutableList.of()) + .registerCompileAndArchiveActions(common) + .addXcodeSettings(xcodeProviderBuilder, common) + .validateAttributes(); } else { XcodeProvider appIpaXcodeProvider = ruleContext.getPrerequisite(XCTEST_APP, Mode.TARGET, XcodeProvider.class); @@ -104,28 +106,32 @@ public final class IosTest implements RuleConfiguredTargetFactory { XcTestAppProvider testApp = xcTestAppProvider(ruleContext); Artifact bundleLoader = testApp.getBundleLoader(); - + LibrariesToLink librariesToLink = + LibrariesToLink.xcTestLibraries(ruleContext, common.getObjcProvider()); + // -bundle causes this binary to be linked as a bundle and not require an entry point // (i.e. main()) // -bundle_loader causes the code in this test to have access to the symbols in the test rig, // or more specifically, the flag causes ld to consider the given binary when checking for // missing symbols. - extraLinkArgs = new ExtraLinkArgs( - "-bundle", - "-bundle_loader", bundleLoader.getExecPathString()); + ExtraLinkArgs extraLinkArgs = + new ExtraLinkArgs("-bundle", "-bundle_loader", bundleLoader.getExecPathString()); - extraLinkInputs = ImmutableList.of(bundleLoader); bundleFormat = ReleaseBundlingSupport.XCTEST_BUNDLE_DIR_FORMAT; - + filesToBuild.add(testApp.getIpa()); + + new CompilationSupport(ruleContext) + .registerLinkActionsForXcTest( + common.getObjcProvider(), + extraLinkArgs, + ImmutableList.of(bundleLoader), + librariesToLink) + .registerCompileAndArchiveActions(common) + .addXcodeSettings(xcodeProviderBuilder, common) + .validateAttributes(); } - new CompilationSupport(ruleContext) - .registerLinkActions(common.getObjcProvider(), extraLinkArgs, extraLinkInputs) - .registerCompileAndArchiveActions(common) - .addXcodeSettings(xcodeProviderBuilder, common) - .validateAttributes(); - ObjcConfiguration objcConfiguration = ObjcRuleClasses.objcConfiguration(ruleContext); new ReleaseBundlingSupport( ruleContext, diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/LibrariesToLink.java b/src/main/java/com/google/devtools/build/lib/rules/objc/LibrariesToLink.java new file mode 100644 index 0000000000..8c41c155ec --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/LibrariesToLink.java @@ -0,0 +1,111 @@ +// 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.objc; + +import static com.google.devtools.build.lib.rules.objc.ObjcProvider.FORCE_LOAD_LIBRARY; +import static com.google.devtools.build.lib.rules.objc.ObjcProvider.IMPORTED_LIBRARY; +import static com.google.devtools.build.lib.rules.objc.ObjcProvider.LIBRARY; + +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Sets; +import com.google.devtools.build.lib.actions.Artifact; +import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode; +import com.google.devtools.build.lib.analysis.RuleContext; + +/** + * Determines libraries that should be linked into an objc library. + */ +public class LibrariesToLink { + + private Iterable librariesToLink; + private Iterable importedLibrariesToLink; + private Iterable forceLoadLibrariesToLink; + + private LibrariesToLink( + Iterable librariesToLink, + Iterable importedLibrariesToLink, + Iterable forceLoadLibrariesToLink) { + this.librariesToLink = librariesToLink; + this.importedLibrariesToLink = importedLibrariesToLink; + this.forceLoadLibrariesToLink = forceLoadLibrariesToLink; + } + + /** + * Returns libraries not already linked in the test binary. + */ + public Iterable getLibrariesToLink() { + return librariesToLink; + } + + /** + * Returns imported libraries not already linked in the test binary. + */ + public Iterable getImportedLibrariesToLink() { + return importedLibrariesToLink; + } + + /** + * Returns libraries not already linked into the test binary with the --force_load option. + */ + public Iterable getForceLoadLibrariesToLink() { + return forceLoadLibrariesToLink; + } + + /** + * Creates an instance of LibrariesToLink for an XcTest application by removing libraries + * already linked into the test binary. Should only be used if the xctest_app attribute + * is set. + * + * @param ruleContext the RuleContext of the test rule + * @param objcProvider the ObjcProvider of the test rule + */ + public static LibrariesToLink xcTestLibraries( + RuleContext ruleContext, ObjcProvider objcProvider) { + + Iterable librariesToLink = objcProvider.get(LIBRARY); + Iterable importedLibrariesToLink = objcProvider.get(IMPORTED_LIBRARY); + Iterable forceLoadLibrariesToLink = objcProvider.get(FORCE_LOAD_LIBRARY); + + XcTestAppProvider xcTestAppProvider = + ruleContext.getPrerequisite(IosTest.XCTEST_APP, Mode.TARGET, XcTestAppProvider.class); + librariesToLink = + Sets.difference( + objcProvider.get(LIBRARY).toSet(), + ImmutableSet.copyOf(xcTestAppProvider.getLinkedLibraries())); + importedLibrariesToLink = + Sets.difference( + objcProvider.get(IMPORTED_LIBRARY).toSet(), + ImmutableSet.copyOf(xcTestAppProvider.getLinkedImportedLibraries())); + forceLoadLibrariesToLink = + Sets.difference( + objcProvider.get(FORCE_LOAD_LIBRARY).toSet(), + ImmutableSet.copyOf(xcTestAppProvider.getForceLoadLibraries())); + + return new LibrariesToLink(librariesToLink, importedLibrariesToLink, forceLoadLibrariesToLink); + } + + /** + * Creates an instance of LibrariesToLink without subtractions. For use when not linking an + * xctest app. + * + * @param objcProvider the provider for the rule + */ + public static LibrariesToLink defaultLibraries(ObjcProvider objcProvider) { + return new LibrariesToLink( + objcProvider.get(LIBRARY), + objcProvider.get(IMPORTED_LIBRARY), + objcProvider.get(FORCE_LOAD_LIBRARY)); + } +} diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ReleaseBundlingSupport.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ReleaseBundlingSupport.java index 1bfed353b3..7481f5a58d 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/ReleaseBundlingSupport.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ReleaseBundlingSupport.java @@ -521,8 +521,18 @@ public final class ReleaseBundlingSupport { .build(); // TODO(bazel-team): Handle the FRAMEWORK_DIR key properly. We probably want to add it to // framework search paths, but not actually link it with the -framework flag. - return new XcTestAppProvider(intermediateArtifacts.combinedArchitectureBinary(), - ruleContext.getImplicitOutputArtifact(IPA), partialObjcProvider); + + Iterable linkedArtifacts = objcProvider.get(ObjcProvider.LIBRARY); + Iterable linkedImportedLibraries = objcProvider.get(ObjcProvider.IMPORTED_LIBRARY); + Iterable forceLoadLibraries = objcProvider.get(ObjcProvider.FORCE_LOAD_LIBRARY); + + return new XcTestAppProvider( + intermediateArtifacts.combinedArchitectureBinary(), + ruleContext.getImplicitOutputArtifact(IPA), + partialObjcProvider, + linkedArtifacts, + linkedImportedLibraries, + forceLoadLibraries); } /** diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/XcTestAppProvider.java b/src/main/java/com/google/devtools/build/lib/rules/objc/XcTestAppProvider.java index 3d47454e2e..899ab0b3c3 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/XcTestAppProvider.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/XcTestAppProvider.java @@ -27,11 +27,36 @@ public final class XcTestAppProvider implements TransitiveInfoProvider { private final Artifact bundleLoader; private final Artifact ipa; private final ObjcProvider objcProvider; + private final Iterable linkedLibraries; + private final Iterable linkedImportedLibraries; + private final Iterable forceLoadLibraries; - XcTestAppProvider(Artifact bundleLoader, Artifact ipa, ObjcProvider objcProvider) { + /** + * Constructs XcTestAppProvider. + * + * @param bundleLoader the bundle loader to be passed into the linker of the test binary + * @param ipa the bundled test application + * @param objcProvider an objcProvider to be passed to the depending IosTest target + * @param linkedLibraries libraries already linked into the test application, that should not be + * linked into the IosTest binary + * @param linkedImportedLibraries imported Libraries already linked into the test application, + * that should not be linked into the IosTest binary + * @param forceLoadLibraries libraries already linked into the test application with --force_load + * that should not be linked into the IosTest binary + */ + XcTestAppProvider( + Artifact bundleLoader, + Artifact ipa, + ObjcProvider objcProvider, + Iterable linkedLibraries, + Iterable linkedImportedLibraries, + Iterable forceLoadLibraries) { this.bundleLoader = Preconditions.checkNotNull(bundleLoader); this.ipa = Preconditions.checkNotNull(ipa); this.objcProvider = Preconditions.checkNotNull(objcProvider); + this.linkedLibraries = linkedLibraries; + this.linkedImportedLibraries = linkedImportedLibraries; + this.forceLoadLibraries = forceLoadLibraries; } /** @@ -54,4 +79,29 @@ public final class XcTestAppProvider implements TransitiveInfoProvider { public ObjcProvider getObjcProvider() { return objcProvider; } + + /** + * Returns the list of libraries that were linked into the host application. These libraries + * should not also be linked into the test binary, so as to prevent ambiguous references. + */ + public Iterable getLinkedLibraries() { + return linkedLibraries; + } + + /** + * Returns the list of imported libraries that were linked into the host application. These + * libraries should not also be linked into the test binary, so as to + * prevent ambiguous references. + */ + public Iterable getLinkedImportedLibraries() { + return linkedImportedLibraries; + } + + /** + * Returns the list of libraries that were linked into the host application with the --force_load + * flag. + */ + public Iterable getForceLoadLibraries() { + return forceLoadLibraries; + } } -- cgit v1.2.3