From f07fd407c2b618b4ed0dc05edaa8d38a6918f5c4 Mon Sep 17 00:00:00 2001 From: Googler Date: Tue, 5 Apr 2016 00:33:31 +0000 Subject: Add 'apple_watch1_extension' and 'apple_watch_extension_binary' to support building watch OS 1 apps. RELNOTES: Support apple_watch1_extension and apple_watch_extension_binary rules for creating watch OS 1 extensions. -- MOS_MIGRATED_REVID=119000703 --- .../devtools/build/lib/rules/objc/WatchUtils.java | 172 +++++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 src/main/java/com/google/devtools/build/lib/rules/objc/WatchUtils.java (limited to 'src/main/java/com/google/devtools/build/lib/rules/objc/WatchUtils.java') diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/WatchUtils.java b/src/main/java/com/google/devtools/build/lib/rules/objc/WatchUtils.java new file mode 100644 index 0000000000..cc3492f997 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/WatchUtils.java @@ -0,0 +1,172 @@ +// 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.ROOT_MERGE_ZIP; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Ordering; +import com.google.devtools.build.lib.actions.Artifact; +import com.google.devtools.build.lib.analysis.RuleContext; +import com.google.devtools.build.lib.rules.apple.AppleConfiguration; +import com.google.devtools.build.lib.rules.apple.DottedVersion; +import com.google.devtools.build.xcode.xcodegen.proto.XcodeGenProtos.XcodeprojBuildSetting; + +/** + * Contains support methods for common processing and generating of watch extension and + * application bundles. + */ +final class WatchUtils { + + /** + * Supported Apple watch OS versions. + */ + enum WatchOSVersion { + OS1(XcodeProductType.WATCH_OS1_APPLICATION, XcodeProductType.WATCH_OS1_EXTENSION, + "WatchKitSupport"); + + private final XcodeProductType applicationXcodeProductType; + private final XcodeProductType extensionXcodeProductType; + private final String watchKitSupportDirName; + + WatchOSVersion(XcodeProductType applicationXcodeProductType, + XcodeProductType extensionXcodeProductType, + String watchKitSupportDirName) { + this.applicationXcodeProductType = applicationXcodeProductType; + this.extensionXcodeProductType = extensionXcodeProductType; + this.watchKitSupportDirName = watchKitSupportDirName; + } + + /** + * Returns the {@link XcodeProductType} to be used for the watch application's Xcode target. + */ + XcodeProductType getApplicationXcodeProductType() { + return applicationXcodeProductType; + } + + /** + * Returns the {@link XcodeProductType} to be used for the watch extension's Xcode target. + */ + XcodeProductType getExtensionXcodeProductType() { + return extensionXcodeProductType; + } + + /** + * Returns the name of the directory in the final iOS bundle which should contain the WatchKit + * support stub. + */ + String getWatchKitSupportDirName() { + return watchKitSupportDirName; + } + } + + @VisibleForTesting + static final String WATCH_KIT_STUB_PATH = + "${SDKROOT}/Library/Application\\ Support/WatchKit/WK"; + + // Apple only accepts watch extension and application starting at 8.2. + @VisibleForTesting + static final DottedVersion MINIMUM_OS_VERSION = DottedVersion.fromString("8.2"); + + /** + * Adds common xcode build settings required for watch bundles to the given xcode provider + * builder. + */ + static void addXcodeSettings(RuleContext ruleContext, + XcodeProvider.Builder xcodeProviderBuilder) { + xcodeProviderBuilder.addMainTargetXcodeprojBuildSettings(xcodeSettings(ruleContext)); + } + + /** + * Watch Extension are not accepted by Apple below version 8.2. While applications built with a + * minimum iOS version of less than 8.2 may contain watch extension in their bundle, the + * extension itself needs to be built with 8.2 or higher. This logic overrides (if necessary) + * any flag-set minimum iOS version for extensions only so that this requirement is not + * violated. + */ + static DottedVersion determineMinimumOsVersion(DottedVersion fromFlag) { + return Ordering.natural().max(fromFlag, MINIMUM_OS_VERSION); + } + + static boolean isBuildingForWatchOS1Version(WatchOSVersion watchOSVersion) { + return watchOSVersion == WatchOSVersion.OS1; + } + + /** + * Adds WatchKitSupport stub to the final ipa and exposes it to given @{link ObjcProvider.Builder} + * based on watch OS version. + * + * For example, for watch OS 1, the resulting ipa will have: + * Payload/TestApp.app + * WatchKitSupport + * WatchKitSupport/WK + */ + static void registerActionsToAddWatchSupport( + RuleContext ruleContext, ObjcProvider.Builder objcProviderBuilder, + WatchOSVersion watchOSVersion) { + Artifact watchSupportZip = watchSupportZip(ruleContext); + String workingDirectory = watchSupportZip.getExecPathString() + .substring(0, watchSupportZip.getExecPathString().lastIndexOf('/')); + String watchKitSupportDirName = watchOSVersion.getWatchKitSupportDirName(); + String watchKitSupportPath = workingDirectory + "/" + watchKitSupportDirName; + + ImmutableList command = ImmutableList.of( + // 1. Copy WK stub binary to watchKitSupportPath. + "mkdir -p " + watchKitSupportPath, + "&&", + String.format("cp %s %s", WATCH_KIT_STUB_PATH, watchKitSupportPath), + // 2. cd to working directory. + "&&", + "cd " + workingDirectory, + // 3. Zip watchSupport. + "&&", + String.format( + "/usr/bin/zip -q -r -0 %s %s", + watchSupportZip.getFilename(), + watchKitSupportDirName)); + + ruleContext.registerAction(ObjcRuleClasses.spawnAppleEnvActionBuilder(ruleContext, + ruleContext.getFragment(AppleConfiguration.class).getBundlingPlatform()) + .setProgressMessage("Copying Watchkit support to app bundle") + .setShellCommand(ImmutableList.of("/bin/bash", "-c", Joiner.on(" ").join(command))) + .addOutput(watchSupportZip) + .build(ruleContext)); + + objcProviderBuilder.add(ROOT_MERGE_ZIP, watchSupportZip(ruleContext)); + } + + private static Artifact watchSupportZip(RuleContext ruleContext) { + return ruleContext.getRelatedArtifact( + ruleContext.getUniqueDirectory("_watch"), "/WatchKitSupport.zip"); + } + + private static Iterable xcodeSettings(RuleContext ruleContext) { + ImmutableList.Builder xcodeSettings = new ImmutableList.Builder<>(); + xcodeSettings.add( + XcodeprojBuildSetting.newBuilder() + .setName("RESOURCES_TARGETED_DEVICE_FAMILY") + .setValue(TargetDeviceFamily.WATCH.getNameInRule()) + .build()); + xcodeSettings.add( + XcodeprojBuildSetting.newBuilder() + .setName("IPHONEOS_DEPLOYMENT_TARGET") + .setValue(determineMinimumOsVersion( + ObjcRuleClasses.objcConfiguration(ruleContext).getMinimumOs()).toString()) + .build()); + return xcodeSettings.build(); + } +} -- cgit v1.2.3