diff options
15 files changed, 386 insertions, 18 deletions
diff --git a/compile.sh b/compile.sh index aaa4ba6e10..047d0e2b67 100755 --- a/compile.sh +++ b/compile.sh @@ -111,6 +111,8 @@ if [ $DO_TOOLS_COMPILATION ]; then tools/objc/precomp_actoolzip_deploy.jar bazel_bootstrap //src/tools/xcode-common/java/com/google/devtools/build/xcode/ibtoolzip:ibtoolzip_deploy.jar \ tools/objc/precomp_ibtoolzip_deploy.jar + bazel_bootstrap //src/tools/xcode-common/java/com/google/devtools/build/xcode/swiftstdlibtoolzip:swiftstdlibtoolzip_deploy.jar \ + tools/objc/precomp_swiftstdlibtoolzip_deploy.jar bazel_bootstrap //src/objc_tools/momczip:momczip_deploy.jar \ tools/objc/precomp_momczip_deploy.jar bazel_bootstrap //src/objc_tools/bundlemerge:bundlemerge_deploy.jar \ diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/Bundling.java b/src/main/java/com/google/devtools/build/lib/rules/objc/Bundling.java index d3545fa7dd..b336096838 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/Bundling.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/Bundling.java @@ -16,6 +16,7 @@ package com.google.devtools.build.lib.rules.objc; import static com.google.devtools.build.lib.rules.objc.ObjcProvider.ASSET_CATALOG; import static com.google.devtools.build.lib.rules.objc.ObjcProvider.BUNDLE_FILE; +import static com.google.devtools.build.lib.rules.objc.ObjcProvider.Flag.USES_SWIFT; import static com.google.devtools.build.lib.rules.objc.ObjcProvider.IMPORTED_LIBRARY; import static com.google.devtools.build.lib.rules.objc.ObjcProvider.LIBRARY; import static com.google.devtools.build.lib.rules.objc.ObjcProvider.MERGE_ZIP; @@ -166,6 +167,11 @@ final class Bundling { for (Artifact storyboard : objcProvider.get(STORYBOARD)) { mergeZipBuilder.add(intermediateArtifacts.compiledStoryboardZip(storyboard)); } + + if (objcProvider.is(USES_SWIFT)) { + mergeZipBuilder.add(intermediateArtifacts.swiftFrameworksFileZip()); + } + return mergeZipBuilder.build(); } @@ -434,7 +440,7 @@ final class Bundling { public NestedSet<Artifact> getBundleContentArtifacts() { return bundleContentArtifacts; } - + /** * Returns primary bundle ID to use, can be null. */ diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/CompilationArtifacts.java b/src/main/java/com/google/devtools/build/lib/rules/objc/CompilationArtifacts.java index 5aac139e57..7d1377b5a5 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/CompilationArtifacts.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/CompilationArtifacts.java @@ -16,6 +16,7 @@ package com.google.devtools.build.lib.rules.objc; import com.google.common.base.Optional; import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.devtools.build.lib.actions.Artifact; @@ -68,6 +69,7 @@ final class CompilationArtifacts { private final Iterable<Artifact> nonArcSrcs; private final Optional<Artifact> archive; private final Optional<Artifact> pchFile; + private final boolean hasSwiftSources; private CompilationArtifacts( Iterable<Artifact> srcs, @@ -78,6 +80,12 @@ final class CompilationArtifacts { this.nonArcSrcs = Preconditions.checkNotNull(nonArcSrcs); this.archive = Preconditions.checkNotNull(archive); this.pchFile = Preconditions.checkNotNull(pchFile); + this.hasSwiftSources = Iterables.any(this.srcs, new Predicate<Artifact>() { + @Override + public boolean apply(Artifact artifact) { + return ObjcRuleClasses.SWIFT_SOURCES.matches(artifact.getExecPath()); + } + }); } public Iterable<Artifact> getSrcs() { @@ -95,4 +103,11 @@ final class CompilationArtifacts { public Optional<Artifact> getPchFile() { return pchFile; } + + /** + * Returns true if any of this target's srcs are Swift source files. + */ + public boolean hasSwiftSources() { + return hasSwiftSources; + } } 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 bfd4b1819c..d6a5e8eab5 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 @@ -20,6 +20,7 @@ import static com.google.devtools.build.lib.rules.objc.ObjcProvider.FORCE_LOAD_L 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; +import static com.google.devtools.build.lib.rules.objc.ObjcProvider.Flag.USES_SWIFT; import static com.google.devtools.build.lib.rules.objc.ObjcProvider.HEADER; import static com.google.devtools.build.lib.rules.objc.ObjcProvider.IMPORTED_LIBRARY; import static com.google.devtools.build.lib.rules.objc.ObjcProvider.INCLUDE; @@ -32,12 +33,14 @@ import static com.google.devtools.build.lib.rules.objc.ObjcRuleClasses.CLANG_PLU import static com.google.devtools.build.lib.rules.objc.ObjcRuleClasses.DSYMUTIL; import static com.google.devtools.build.lib.rules.objc.ObjcRuleClasses.NON_ARC_SRCS_TYPE; import static com.google.devtools.build.lib.rules.objc.ObjcRuleClasses.SRCS_TYPE; +import static com.google.devtools.build.lib.rules.objc.ObjcRuleClasses.SWIFT; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Joiner; import com.google.common.base.Optional; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.devtools.build.lib.actions.Action; import com.google.devtools.build.lib.actions.Artifact; @@ -146,17 +149,24 @@ final class CompilationSupport { for (Artifact sourceFile : compilationArtifacts.getSrcs()) { Artifact objFile = intermediateArtifacts.objFile(sourceFile); objFiles.add(objFile); - registerCompileAction(sourceFile, objFile, compilationArtifacts.getPchFile(), - objcProvider, intermediateArtifacts, ImmutableList.of("-fobjc-arc"), - isCodeCoverageEnabled); + if (ObjcRuleClasses.SWIFT_SOURCES.matches(sourceFile.getFilename())) { + registerSwiftCompileAction(sourceFile, objFile, intermediateArtifacts); + } else { + registerCompileAction(sourceFile, objFile, objcProvider, intermediateArtifacts, + compilationArtifacts, ImmutableList.of("-fobjc-arc"), isCodeCoverageEnabled); + } } for (Artifact nonArcSourceFile : compilationArtifacts.getNonArcSrcs()) { Artifact objFile = intermediateArtifacts.objFile(nonArcSourceFile); objFiles.add(objFile); - registerCompileAction(nonArcSourceFile, objFile, compilationArtifacts.getPchFile(), - objcProvider, intermediateArtifacts, ImmutableList.of("-fno-objc-arc"), - isCodeCoverageEnabled); + registerCompileAction(nonArcSourceFile, objFile, objcProvider, intermediateArtifacts, + compilationArtifacts, ImmutableList.of("-fno-objc-arc"), isCodeCoverageEnabled); + } + + if (compilationArtifacts.hasSwiftSources()) { + registerSwiftModuleMergeAction(intermediateArtifacts, compilationArtifacts); } + for (Artifact archive : compilationArtifacts.getArchive().asSet()) { registerArchiveActions(intermediateArtifacts, objFiles, archive); } @@ -165,14 +175,15 @@ final class CompilationSupport { private void registerCompileAction( Artifact sourceFile, Artifact objFile, - Optional<Artifact> pchFile, ObjcProvider objcProvider, IntermediateArtifacts intermediateArtifacts, + CompilationArtifacts compilationArtifacts, Iterable<String> otherFlags, boolean isCodeCoverageEnabled) { ObjcConfiguration objcConfiguration = ObjcRuleClasses.objcConfiguration(ruleContext); ImmutableList.Builder<String> coverageFlags = new ImmutableList.Builder<>(); ImmutableList.Builder<Artifact> gcnoFiles = new ImmutableList.Builder<>(); + ImmutableList.Builder<Artifact> additionalInputs = new ImmutableList.Builder<>(); if (isCodeCoverageEnabled) { coverageFlags.addAll(CLANG_COVERAGE_FLAGS); gcnoFiles.add(intermediateArtifacts.gcnoFile(sourceFile)); @@ -181,6 +192,14 @@ final class CompilationSupport { if (ObjcRuleClasses.CPP_SOURCES.matches(sourceFile.getExecPath())) { commandLine.add("-stdlib=libc++"); } + + if (compilationArtifacts.hasSwiftSources()) { + // Add the directory that contains merged TargetName-Swift.h header to search path, in case + // any of ObjC files use it. + commandLine.add("-I"); + commandLine.addPath(intermediateArtifacts.swiftHeader().getExecPath().getParentDirectory()); + additionalInputs.add(intermediateArtifacts.swiftHeader()); + } commandLine .add(IosSdkCommands.compileFlagsForClang(objcConfiguration)) .add(IosSdkCommands.commonLinkAndCompileFlagsForClang( @@ -188,7 +207,7 @@ final class CompilationSupport { .add(objcConfiguration.getCoptsForCompilationMode()) .addBeforeEachPath( "-iquote", ObjcCommon.userHeaderSearchPaths(ruleContext.getConfiguration())) - .addBeforeEachExecPath("-include", pchFile.asSet()) + .addBeforeEachExecPath("-include", compilationArtifacts.getPchFile().asSet()) .addBeforeEachPath("-I", objcProvider.get(INCLUDE)) .add(otherFlags) .addFormatEach("-D%s", objcProvider.get(DEFINE)) @@ -204,11 +223,114 @@ final class CompilationSupport { .setExecutable(CLANG) .setCommandLine(commandLine.build()) .addInput(sourceFile) + .addInputs(additionalInputs.build()) .addOutput(objFile) .addOutputs(gcnoFiles.build()) .addTransitiveInputs(objcProvider.get(HEADER)) .addTransitiveInputs(objcProvider.get(FRAMEWORK_FILE)) - .addInputs(pchFile.asSet()) + .addInputs(compilationArtifacts.getPchFile().asSet()) + .build(ruleContext)); + } + + /** + * Compiles a single swift file. + * + * @param sourceFile the artifact to compile + * @param objFile the resulting object artifact + */ + private void registerSwiftCompileAction( + Artifact sourceFile, + Artifact objFile, + IntermediateArtifacts intermediateArtifacts) { + ObjcConfiguration objcConfiguration = ObjcRuleClasses.objcConfiguration(ruleContext); + + // Compiling a single swift file requires knowledge of all of the other + // swift files in the same module. The primary file ({@code sourceFile}) is + // compiled to an object file, while the remaining files are used to resolve + // symbols (they behave like c/c++ headers in this context). + ImmutableSet.Builder<Artifact> otherSwiftSourcesBuilder = ImmutableSet.builder(); + for (Artifact otherSourceFile : compilationArtifacts(ruleContext).getSrcs()) { + if (ObjcRuleClasses.SWIFT_SOURCES.matches(otherSourceFile.getFilename()) + && otherSourceFile != sourceFile) { + otherSwiftSourcesBuilder.add(otherSourceFile); + } + } + ImmutableSet<Artifact> otherSwiftSources = otherSwiftSourcesBuilder.build(); + + CustomCommandLine.Builder commandLine = new CustomCommandLine.Builder() + .add("-frontend") + .add("-emit-object") + .add("-target").add(IosSdkCommands.swiftTarget(objcConfiguration)) + .add("-sdk").add(IosSdkCommands.sdkDir(objcConfiguration)) + .add("-enable-objc-interop"); + + if (objcConfiguration.generateDebugSymbols()) { + commandLine.add("-g"); + } + + commandLine + .add("-Onone") + .add("-module-name").add(getModuleName()) + .add("-parse-as-library") + .addExecPath("-primary-file", sourceFile) + .addExecPaths(otherSwiftSources) + .addExecPath("-o", objFile) + .addExecPath("-emit-module-path", intermediateArtifacts.swiftModuleFile(sourceFile)); + + // Add all ObjC headers to the compiler, in case Swift code is calling into Objc + // TODO(bazel-team): This can be augmented by an explicit bridging header field in the rule. + commandLine.addBeforeEachExecPath("-import-objc-header", attributes.hdrs()); + + ruleContext.registerAction(ObjcRuleClasses.spawnOnDarwinActionBuilder() + .setMnemonic("SwiftCompile") + .setExecutable(SWIFT) + .setCommandLine(commandLine.build()) + .addInput(sourceFile) + .addInputs(otherSwiftSources) + .addInputs(attributes.hdrs()) + .addOutput(objFile) + .addOutput(intermediateArtifacts.swiftModuleFile(sourceFile)) + .build(ruleContext)); + } + + /** + * Merges multiple .partial_swiftmodule files together. Also produces a swift header that can be + * used by Objective-C code. + */ + private void registerSwiftModuleMergeAction( + IntermediateArtifacts intermediateArtifacts, + CompilationArtifacts compilationArtifacts) { + ObjcConfiguration objcConfiguration = ObjcRuleClasses.objcConfiguration(ruleContext); + + ImmutableList.Builder<Artifact> moduleFiles = new ImmutableList.Builder<>(); + for (Artifact src : compilationArtifacts.getSrcs()) { + if (ObjcRuleClasses.SWIFT_SOURCES.matches(src.getFilename())) { + moduleFiles.add(intermediateArtifacts.swiftModuleFile(src)); + } + } + + CustomCommandLine.Builder commandLine = new CustomCommandLine.Builder(); + commandLine.add("-frontend"); + commandLine.add("-emit-module"); + commandLine.add("-sdk").add(IosSdkCommands.sdkDir(objcConfiguration)); + commandLine.add("-target").add(IosSdkCommands.swiftTarget(objcConfiguration)); + if (objcConfiguration.generateDebugSymbols()) { + commandLine.add("-g"); + } + + commandLine.add("-module-name").add(getModuleName()); + commandLine.add("-parse-as-library"); + commandLine.addExecPaths(moduleFiles.build()); + commandLine.addExecPath("-o", intermediateArtifacts.swiftModule()); + commandLine.addExecPath("-emit-objc-header-path", intermediateArtifacts.swiftHeader()); + + ruleContext.registerAction(ObjcRuleClasses.spawnOnDarwinActionBuilder() + .setMnemonic("SwiftModuleMerge") + .setExecutable(SWIFT) + .setCommandLine(commandLine.build()) + .addInputs(moduleFiles.build()) + .addOutput(intermediateArtifacts.swiftModule()) + .addOutput(intermediateArtifacts.swiftHeader()) .build(ruleContext)); } @@ -345,6 +467,13 @@ final class CompilationSupport { commandLine.add(LINKER_COVERAGE_FLAGS); } + if (objcProvider.is(USES_SWIFT)) { + commandLine + .add("-L").add(IosSdkCommands.swiftLibDir(objcConfiguration)) + .add("-Xlinker").add("-rpath") + .add("-Xlinker").add("@executable_path/Frameworks"); + } + // Call to dsymutil for debug symbol generation must happen in the link action. // All debug symbol information is encoded in object files inside archive files. To generate // the debug symbol bundle, dsymutil will look inside the linked binary for the encoded @@ -565,4 +694,11 @@ final class CompilationSupport { // TODO(bazel-team): Throw instead of returning null? return str.endsWith(suffix) ? str.substring(0, str.length() - suffix.length()) : null; } + + /** + * Returns the name of Swift module for this target. + */ + private String getModuleName() { + return ruleContext.getLabel().getName(); + } } diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/IntermediateArtifacts.java b/src/main/java/com/google/devtools/build/lib/rules/objc/IntermediateArtifacts.java index 9e1372e8d4..4af59d6ad5 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/IntermediateArtifacts.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/IntermediateArtifacts.java @@ -183,6 +183,28 @@ final class IntermediateArtifacts { } /** + * The swift module produced by compiling the {@code source} artifact. + */ + public Artifact swiftModuleFile(Artifact source) { + return analysisEnvironment.getDerivedArtifact(inUniqueObjsDir(source, ".partial_swiftmodule"), + binDirectory); + } + + /** + * Integrated swift module for this target. + */ + public Artifact swiftModule() { + return appendExtension(".swiftmodule"); + } + + /** + * Integrated swift header for this target. + */ + public Artifact swiftHeader() { + return appendExtension("-Swift.h"); + } + + /** * The artifact for the .gcno file that should be generated when compiling the {@code source} * artifact. */ @@ -236,6 +258,14 @@ final class IntermediateArtifacts { } /** + * Returns the artifact which is the output of running swift-stdlib-tool and copying resulting + * dylibs. + */ + public Artifact swiftFrameworksFileZip() { + return appendExtension(".swiftstdlib.zip"); + } + + /** * Debug symbol plist generated for a linked binary. */ public Artifact dsymPlist() { diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/IosSdkCommands.java b/src/main/java/com/google/devtools/build/lib/rules/objc/IosSdkCommands.java index 32c24c1395..b788f7c0e0 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/IosSdkCommands.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/IosSdkCommands.java @@ -84,6 +84,28 @@ public class IosSdkCommands { return platformDir(configuration) + "/Developer/Library/Frameworks"; } + /** + * Returns swift libraries path. + */ + public static String swiftLibDir(ObjcConfiguration configuration) { + return DEVELOPER_DIR + "/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/" + + swiftPlatform(configuration); + } + + /** + * Returns a platform name string suitable for use in Swift tools. + */ + public static String swiftPlatform(ObjcConfiguration configuration) { + return getPlatformPlistName(configuration).toLowerCase(); + } + + /** + * Returns the target string for swift compiler. For example, "x86_64-apple-ios8.2" + */ + public static String swiftTarget(ObjcConfiguration configuration) { + return configuration.getIosCpu() + "-apple-" + "ios" + configuration.getIosSdkVersion(); + } + private static Iterable<PathFragment> uniqueParentDirectories(Iterable<PathFragment> paths) { ImmutableSet.Builder<PathFragment> parents = new ImmutableSet.Builder<>(); for (PathFragment path : paths) { diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcCommon.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcCommon.java index e311bc3473..3b317ab8dd 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcCommon.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcCommon.java @@ -26,6 +26,7 @@ import static com.google.devtools.build.lib.rules.objc.ObjcProvider.FORCE_LOAD_L 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; +import static com.google.devtools.build.lib.rules.objc.ObjcProvider.Flag.USES_SWIFT; import static com.google.devtools.build.lib.rules.objc.ObjcProvider.GCNO; import static com.google.devtools.build.lib.rules.objc.ObjcProvider.GENERAL_RESOURCE_FILE; import static com.google.devtools.build.lib.rules.objc.ObjcProvider.HEADER; @@ -455,13 +456,20 @@ public final class ObjcCommon { } boolean usesCpp = false; + boolean usesSwift = false; for (Artifact sourceFile : Iterables.concat(artifacts.getSrcs(), artifacts.getNonArcSrcs())) { usesCpp = usesCpp || ObjcRuleClasses.CPP_SOURCES.matches(sourceFile.getExecPath()); + usesSwift = usesSwift || ObjcRuleClasses.SWIFT_SOURCES.matches(sourceFile.getExecPath()); } + if (usesCpp) { objcProvider.add(FLAG, USES_CPP); } + + if (usesSwift) { + objcProvider.add(FLAG, USES_SWIFT); + } } if (alwayslink) { diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProvider.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProvider.java index e2fc6e9363..92fce30111 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProvider.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProvider.java @@ -199,7 +199,13 @@ public final class ObjcProvider implements TransitiveInfoProvider { * Indicates that C++ (or Objective-C++) is used in any source file. This affects how the linker * is invoked. */ - USES_CPP; + USES_CPP, + + /** + * Indicates that Swift source files are present. This affects bundling, compiling and linking + * actions. + */ + USES_SWIFT } private final ImmutableMap<Key<?>, NestedSet<?>> items; diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcRuleClasses.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcRuleClasses.java index dec8cc582c..bd2ecc6566 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcRuleClasses.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcRuleClasses.java @@ -61,10 +61,13 @@ public class ObjcRuleClasses { IosSdkCommands.DEVELOPER_DIR + "/Toolchains/XcodeDefault.xctoolchain/usr/bin"; static final PathFragment CLANG = new PathFragment(BIN_DIR + "/clang"); static final PathFragment CLANG_PLUSPLUS = new PathFragment(BIN_DIR + "/clang++"); + static final PathFragment SWIFT = new PathFragment(BIN_DIR + "/swift"); static final PathFragment LIBTOOL = new PathFragment(BIN_DIR + "/libtool"); static final PathFragment DSYMUTIL = new PathFragment(BIN_DIR + "/dsymutil"); static final PathFragment LIPO = new PathFragment(BIN_DIR + "/lipo"); static final PathFragment IBTOOL = new PathFragment(IosSdkCommands.IBTOOL_PATH); + static final PathFragment SWIFT_STDLIB_TOOL = new PathFragment(BIN_DIR + "/swift-stdlib-tool"); + private static final PathFragment JAVA = new PathFragment("/usr/bin/java"); private ObjcRuleClasses() { @@ -312,9 +315,11 @@ public class ObjcRuleClasses { */ static final FileType CPP_SOURCES = FileType.of(".cc", ".cpp", ".mm", ".cxx", ".C"); + static final FileType SWIFT_SOURCES = FileType.of(".swift"); + private static final FileType NON_CPP_SOURCES = FileType.of(".m", ".c"); - static final FileTypeSet SRCS_TYPE = FileTypeSet.of(NON_CPP_SOURCES, CPP_SOURCES); + static final FileTypeSet SRCS_TYPE = FileTypeSet.of(NON_CPP_SOURCES, CPP_SOURCES, SWIFT_SOURCES); static final FileTypeSet NON_ARC_SRCS_TYPE = FileTypeSet.of(FileType.of(".m", ".mm")); @@ -452,6 +457,8 @@ public class ObjcRuleClasses { .value(env.getLabel("//tools/objc:actoolzip_deploy.jar"))) .add(attr("$ibtoolzip_deploy", LABEL).cfg(HOST) .value(env.getLabel("//tools/objc:ibtoolzip_deploy.jar"))) + .add(attr("$swiftstdlibtoolzip_deploy", LABEL).cfg(HOST) + .value(env.getLabel("//tools/objc:swiftstdlibtoolzip_deploy.jar"))) .build(); } @Override 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 f403daa7db..e10dee00f6 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 @@ -16,6 +16,7 @@ package com.google.devtools.build.lib.rules.objc; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.devtools.build.lib.packages.ImplicitOutputsFunction.fromTemplates; +import static com.google.devtools.build.lib.rules.objc.ObjcProvider.Flag.USES_SWIFT; import static com.google.devtools.build.lib.rules.objc.TargetDeviceFamily.UI_DEVICE_FAMILY_VALUES; import com.google.common.annotations.VisibleForTesting; @@ -203,6 +204,7 @@ public final class ReleaseBundlingSupport { registerCombineArchitecturesAction(); registerTransformAndCopyBreakpadFilesAction(); + registerSwiftStdlibActionsIfNecessary(); ObjcConfiguration objcConfiguration = ObjcRuleClasses.objcConfiguration(ruleContext); Artifact ipaOutput = ruleContext.getImplicitOutputArtifact(IPA); @@ -505,23 +507,38 @@ public final class ReleaseBundlingSupport { private ReleaseBundlingSupport registerSignBundleAction( Artifact entitlements, Artifact ipaOutput, Artifact ipaUnsigned) { // TODO(bazel-team): Support variable substitution + + ImmutableList.Builder<String> dirsToSign = new ImmutableList.Builder<>(); + + // Explicitly sign Swift frameworks. Unfortunately --deep option on codesign doesn't do this + // automatically. + // The order here is important. The innermost code must singed first. + if (objcProvider.is(USES_SWIFT)) { + dirsToSign.add(bundling.getBundleDir() + "/Frameworks/*"); + } + dirsToSign.add(bundling.getBundleDir()); + + StringBuilder codesignCommandLineBuilder = new StringBuilder(); + for (String dir : dirsToSign.build()) { + codesignCommandLineBuilder + .append(codesignCommand(attributes.provisioningProfile(), entitlements, "${t}/" + dir)) + .append(" && "); + } + ruleContext.registerAction(ObjcRuleClasses.spawnOnDarwinActionBuilder() .setMnemonic("IosSignBundle") .setProgressMessage("Signing iOS bundle: " + ruleContext.getLabel()) .setExecutable(new PathFragment("/bin/bash")) .addArgument("-c") - // TODO(bazel-team): Support --resource-rules for resources + // TODO(bazel-team): Support nested code signing. .addArgument("set -e && " + "t=$(mktemp -d -t signing_intermediate) && " // Get an absolute path since we need to cd into the temp directory for zip. + "signed_ipa=${PWD}/" + ipaOutput.getExecPathString() + " && " + "unzip -qq " + ipaUnsigned.getExecPathString() + " -d ${t} && " - + codesignCommand( - attributes.provisioningProfile(), - entitlements, - "${t}/" + bundling.getBundleDir()) + + codesignCommandLineBuilder.toString() // Using zip since we need to preserve permissions - + " && cd \"${t}\" && /usr/bin/zip -q -r \"${signed_ipa}\" .") + + "cd \"${t}\" && /usr/bin/zip -q -r \"${signed_ipa}\" .") .addInput(ipaUnsigned) .addInput(attributes.provisioningProfile()) .addInput(entitlements) @@ -668,6 +685,31 @@ public final class ReleaseBundlingSupport { .build(ruleContext)); } + /** + * Registers an action to copy Swift standard library dylibs into app bundle. + */ + private void registerSwiftStdlibActionsIfNecessary() { + if (!objcProvider.is(USES_SWIFT)) { + return; + } + + ObjcConfiguration objcConfiguration = ObjcRuleClasses.objcConfiguration(ruleContext); + + CustomCommandLine.Builder commandLine = CustomCommandLine.builder() + .addPath(intermediateArtifacts.swiftFrameworksFileZip().getExecPath()) + .add("Frameworks") + .addPath(ObjcRuleClasses.SWIFT_STDLIB_TOOL) + .add("--platform").add(IosSdkCommands.swiftPlatform(objcConfiguration)) + .addExecPath("--scan-executable", intermediateArtifacts.singleArchitectureBinary()); + + ruleContext.registerAction( + ObjcRuleClasses.spawnJavaOnDarwinActionBuilder(attributes.swiftStdlibToolDeployJar()) + .setMnemonic("SwiftStdlibCopy") + .setCommandLine(commandLine.build()) + .addOutput(intermediateArtifacts.swiftFrameworksFileZip()) + .addInput(intermediateArtifacts.singleArchitectureBinary()) + .build(ruleContext)); + } private String extractPlistCommand(Artifact provisioningProfile) { return "security cms -D -i " + ShellUtils.shellEscape(provisioningProfile.getExecPathString()); @@ -757,6 +799,13 @@ public final class ReleaseBundlingSupport { ruleContext.getPrerequisiteArtifact("$runner_script_template", Mode.HOST)); } + /** + * Returns the location of the swiftstdlibtoolzip deploy jar. + */ + Artifact swiftStdlibToolDeployJar() { + return ruleContext.getPrerequisiteArtifact("$swiftstdlibtoolzip_deploy", Mode.HOST); + } + String bundleId() { return checkNotNull(stringAttribute("bundle_id")); } diff --git a/src/tools/xcode-common/BUILD b/src/tools/xcode-common/BUILD index b34518bb66..187e8c1dfe 100644 --- a/src/tools/xcode-common/BUILD +++ b/src/tools/xcode-common/BUILD @@ -6,6 +6,7 @@ filegroup( "//src/tools/xcode-common/java/com/google/devtools/build/xcode/actoolzip:srcs", "//src/tools/xcode-common/java/com/google/devtools/build/xcode/common:srcs", "//src/tools/xcode-common/java/com/google/devtools/build/xcode/ibtoolzip:srcs", + "//src/tools/xcode-common/java/com/google/devtools/build/xcode/swiftstdlibtoolzip:srcs", "//src/tools/xcode-common/java/com/google/devtools/build/xcode/util:srcs", "//src/tools/xcode-common/java/com/google/devtools/build/xcode/zip:srcs", "//src/tools/xcode-common/java/com/google/devtools/build/xcode/zippingoutput:srcs", diff --git a/src/tools/xcode-common/java/com/google/devtools/build/xcode/swiftstdlibtoolzip/BUILD b/src/tools/xcode-common/java/com/google/devtools/build/xcode/swiftstdlibtoolzip/BUILD new file mode 100644 index 0000000000..b42968804a --- /dev/null +++ b/src/tools/xcode-common/java/com/google/devtools/build/xcode/swiftstdlibtoolzip/BUILD @@ -0,0 +1,17 @@ +package(default_visibility = ["//src:__subpackages__"]) + +filegroup( + name = "srcs", + srcs = glob(["**"]), +) + +java_binary( + name = "swiftstdlibtoolzip", + srcs = ["SwiftStdlibToolZip.java"], + main_class = "com.google.devtools.build.xcode.swiftstdlibtoolzip.SwiftStdlibToolZip", + visibility = ["//visibility:public"], + deps = [ + "//src/tools/xcode-common/java/com/google/devtools/build/xcode/zippingoutput", + "//third_party:guava", + ], +) diff --git a/src/tools/xcode-common/java/com/google/devtools/build/xcode/swiftstdlibtoolzip/README b/src/tools/xcode-common/java/com/google/devtools/build/xcode/swiftstdlibtoolzip/README new file mode 100644 index 0000000000..0c8b3f082d --- /dev/null +++ b/src/tools/xcode-common/java/com/google/devtools/build/xcode/swiftstdlibtoolzip/README @@ -0,0 +1,4 @@ +swiftstdlibtoolzip runs swift-stdlib-tool, which scans executables and copies required Swift +framework dylibs to the specified path, then zips up the output for further bundle merging. + +swift-stdlib-tool only runs on Darwin, so swiftstdlibtoolzip only runs on Darwin. diff --git a/src/tools/xcode-common/java/com/google/devtools/build/xcode/swiftstdlibtoolzip/SwiftStdlibToolZip.java b/src/tools/xcode-common/java/com/google/devtools/build/xcode/swiftstdlibtoolzip/SwiftStdlibToolZip.java new file mode 100644 index 0000000000..2043a08fd3 --- /dev/null +++ b/src/tools/xcode-common/java/com/google/devtools/build/xcode/swiftstdlibtoolzip/SwiftStdlibToolZip.java @@ -0,0 +1,59 @@ +// Copyright 2015 Google Inc. 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.xcode.swiftstdlibtoolzip; + +import com.google.common.collect.ImmutableList; +import com.google.devtools.build.xcode.zippingoutput.Arguments; +import com.google.devtools.build.xcode.zippingoutput.Wrapper; +import com.google.devtools.build.xcode.zippingoutput.Wrappers; + +import java.io.IOException; + +/** + * A tool which wraps swift-stdlib-tool by running and zipping its output. See {@link Wrapper} for + * more information. + */ +public class SwiftStdlibToolZip implements Wrapper { + + @Override + public String name() { + return "SwiftStdlibToolZip"; + } + + @Override + public String subtoolName() { + return "swift-stdlib-tool"; + } + + @Override + public Iterable<String> subCommand(Arguments arguments, String outputDirectory) { + return new ImmutableList.Builder<String>() + .add(arguments.subtoolCmd()) + .addAll(arguments.subtoolExtraArgs()) + .add("--copy") + .add("--verbose") + .add("--destination").add(outputDirectory) + .build(); + } + + @Override + public boolean outputDirectoryMustExist() { + return true; + } + + public static void main(String[] args) throws IOException, InterruptedException { + Wrappers.executePipingOutput(args, new SwiftStdlibToolZip()); + } +} diff --git a/tools/objc/BUILD b/tools/objc/BUILD index 9ab7180907..8b27ebe1d1 100644 --- a/tools/objc/BUILD +++ b/tools/objc/BUILD @@ -39,6 +39,12 @@ java_binary( main_class = "com.google.devtools.build.xcode.xcodegen.XcodeGen", ) +java_binary( + name = "swiftstdlibtoolzip", + srcs = [":precomp_xcodegen_deploy.jar"], + main_class = "com.google.devtools.build.xcode.swiftstdlibtoolzip.SwiftStdlibToolZip", +) + filegroup( name = "srcs", srcs = glob(["**"]), |