aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Chris Parsons <cparsons@google.com>2016-08-02 18:48:43 +0000
committerGravatar Yun Peng <pcloudy@google.com>2016-08-03 07:57:44 +0000
commit36ff24cca3e3e1bd09422bef646d98621b1270e4 (patch)
treedc1f9e5cb2354041b6027a1f5d602940957018e8
parentc55fe15fc36ad01b93f4efe85ff85911d041d5d7 (diff)
Introduce apple_watch2_extension rule for bundling watchOS2 extensions.
This can be used in conjunction with apple_binary to add watchOS2 extensions to an ios application bundle. This is the majority of the native work for this bundling logic. Before announcing general availability of this feature, we will want to: 1. Iterate with teams who have existing watchOS2 apps to catch any corner-case bugs 2. Introduce a skylark macro to wrap apple_watch2_extension and apple_binary into a single rule; this will both serve as a convenience and ensure users set the appropriate linkopts and platformtype on their apple_binary target. -- MOS_MIGRATED_REVID=129122855
-rw-r--r--src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java3
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/objc/AppleBinary.java10
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/objc/AppleWatch1Extension.java15
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/objc/AppleWatch2Extension.java176
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/objc/AppleWatch2ExtensionRule.java76
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/objc/Bundling.java11
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/objc/IntermediateArtifacts.java9
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/objc/IosApplication.java3
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/objc/IosApplicationRule.java35
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProvider.java14
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/objc/Watch2ExtensionSupport.java236
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/objc/WatchApplicationSupport.java155
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/objc/WatchExtensionSupport.java57
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/objc/WatchUtils.java49
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/objc/XcodeProductType.java4
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/objc/XcodeProvider.java19
16 files changed, 757 insertions, 115 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java
index 8fe4241985..403acc7f8e 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java
@@ -108,6 +108,7 @@ import com.google.devtools.build.lib.rules.java.ProguardLibraryRule;
import com.google.devtools.build.lib.rules.objc.AppleBinaryRule;
import com.google.devtools.build.lib.rules.objc.AppleSkylarkCommon;
import com.google.devtools.build.lib.rules.objc.AppleWatch1ExtensionRule;
+import com.google.devtools.build.lib.rules.objc.AppleWatch2ExtensionRule;
import com.google.devtools.build.lib.rules.objc.AppleWatchExtensionBinaryRule;
import com.google.devtools.build.lib.rules.objc.BazelJ2ObjcProtoAspect;
import com.google.devtools.build.lib.rules.objc.ExperimentalObjcLibraryRule;
@@ -145,7 +146,6 @@ import com.google.devtools.build.lib.rules.repository.LocalRepositoryRule;
import com.google.devtools.build.lib.rules.repository.NewLocalRepositoryRule;
import com.google.devtools.build.lib.rules.repository.WorkspaceBaseRule;
import com.google.devtools.build.lib.util.ResourceFileLoader;
-
import java.io.IOException;
/**
@@ -456,6 +456,7 @@ public class BazelRuleClassProvider {
builder.addRuleDefinition(new ObjcRuleClasses.WatchApplicationBundleRule());
builder.addRuleDefinition(new ObjcRuleClasses.CrosstoolRule());
builder.addRuleDefinition(new AppleWatch1ExtensionRule());
+ builder.addRuleDefinition(new AppleWatch2ExtensionRule());
builder.addRuleDefinition(new AppleWatchExtensionBinaryRule());
builder.addRuleDefinition(new IosApplicationRule());
builder.addRuleDefinition(new IosExtensionBinaryRule());
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/AppleBinary.java b/src/main/java/com/google/devtools/build/lib/rules/objc/AppleBinary.java
index 77b659ff01..4c878e648d 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/objc/AppleBinary.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/objc/AppleBinary.java
@@ -14,6 +14,7 @@
package com.google.devtools.build.lib.rules.objc;
+import static com.google.devtools.build.lib.rules.objc.ObjcProvider.MULTI_ARCH_LINKED_BINARIES;
import static com.google.devtools.build.lib.syntax.Type.STRING;
import com.google.common.annotations.VisibleForTesting;
@@ -44,7 +45,6 @@ import com.google.devtools.build.lib.rules.cpp.CcToolchainProvider;
import com.google.devtools.build.lib.rules.objc.CompilationSupport.ExtraLinkArgs;
import com.google.devtools.build.lib.rules.objc.ObjcCommon.ResourceAttributes;
import com.google.devtools.build.lib.rules.objc.ProtoSupport.TargetType;
-
import java.util.List;
import java.util.Set;
@@ -92,6 +92,8 @@ public class AppleBinary implements RuleConfiguredTargetFactory {
NestedSetBuilder.<Artifact>stableOrder()
.add(ruleIntermediateArtifacts.combinedArchitectureBinary());
+ ObjcProvider.Builder objcProviderBuilder = new ObjcProvider.Builder();
+
for (BuildConfiguration childConfig : childConfigurations) {
IntermediateArtifacts intermediateArtifacts =
ObjcRuleClasses.intermediateArtifacts(ruleContext, childConfig);
@@ -141,6 +143,8 @@ public class AppleBinary implements RuleConfiguredTargetFactory {
DsymOutputType.APP)
.validateAttributes();
ruleContext.assertNoErrors();
+
+ objcProviderBuilder.addTransitiveAndPropagate(common.getObjcProvider());
}
AppleConfiguration appleConfiguration = ruleContext.getFragment(AppleConfiguration.class);
@@ -154,6 +158,10 @@ public class AppleBinary implements RuleConfiguredTargetFactory {
RuleConfiguredTargetBuilder targetBuilder =
ObjcRuleClasses.ruleConfiguredTarget(ruleContext, filesToBuild.build());
+ objcProviderBuilder.add(
+ MULTI_ARCH_LINKED_BINARIES, ruleIntermediateArtifacts.combinedArchitectureBinary());
+
+ targetBuilder.addProvider(ObjcProvider.class, objcProviderBuilder.build());
return targetBuilder.build();
}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/AppleWatch1Extension.java b/src/main/java/com/google/devtools/build/lib/rules/objc/AppleWatch1Extension.java
index 6100bf2d74..e43ca738d2 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/objc/AppleWatch1Extension.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/objc/AppleWatch1Extension.java
@@ -31,7 +31,6 @@ import com.google.devtools.build.lib.analysis.RuleContext;
import com.google.devtools.build.lib.analysis.config.BuildOptions;
import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
import com.google.devtools.build.lib.packages.Attribute.SplitTransition;
-import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException;
import com.google.devtools.build.lib.rules.RuleConfiguredTargetFactory;
import com.google.devtools.build.lib.rules.apple.AppleConfiguration.ConfigurationDistinguisher;
import com.google.devtools.build.lib.rules.objc.IosExtension.ExtensionSplitArchTransition;
@@ -56,7 +55,6 @@ public class AppleWatch1Extension implements RuleConfiguredTargetFactory {
@Override
public ConfiguredTarget create(RuleContext ruleContext)
throws InterruptedException, RuleErrorException {
- ObjcProvider.Builder applicationObjcProviderBuilder = new ObjcProvider.Builder();
ObjcProvider.Builder extensionObjcProviderBuilder = new ObjcProvider.Builder();
ObjcProvider.Builder exposedObjcProviderBuilder = new ObjcProvider.Builder();
XcodeProvider.Builder applicationXcodeProviderBuilder = new XcodeProvider.Builder();
@@ -68,7 +66,6 @@ public class AppleWatch1Extension implements RuleConfiguredTargetFactory {
createWatchApplicationBundle(
ruleContext,
applicationXcodeProviderBuilder,
- applicationObjcProviderBuilder,
applicationFilesToBuild,
exposedObjcProviderBuilder);
@@ -143,14 +140,12 @@ public class AppleWatch1Extension implements RuleConfiguredTargetFactory {
* Creates a watch application bundle.
* @param ruleContext rule context in which to create the bundle
* @param xcodeProviderBuilder {@link XcodeProvider.Builder} for the application
- * @param objcProviderBuilder {@link ObjcProvider.Builder} for the application
* @param filesToBuild the list to contain the files to be built for this bundle
* @param exposedObjcProviderBuilder {@link ObjcProvider.Builder} exposed to the parent target
*/
private void createWatchApplicationBundle(
RuleContext ruleContext,
XcodeProvider.Builder xcodeProviderBuilder,
- ObjcProvider.Builder objcProviderBuilder,
NestedSetBuilder<Artifact> filesToBuild,
ObjcProvider.Builder exposedObjcProviderBuilder)
throws InterruptedException {
@@ -161,10 +156,12 @@ public class AppleWatch1Extension implements RuleConfiguredTargetFactory {
new IntermediateArtifacts(ruleContext, "", watchApplicationBundleName(ruleContext)),
watchApplicationBundleName(ruleContext),
watchApplicationIpaArtifact(ruleContext),
- watchApplicationBundleName(ruleContext),
- ConfigurationDistinguisher.WATCH_OS1_EXTENSION)
- .createBundle(
- xcodeProviderBuilder, objcProviderBuilder, filesToBuild, exposedObjcProviderBuilder);
+ watchApplicationBundleName(ruleContext))
+ .createBundleAndXcodeproj(
+ xcodeProviderBuilder,
+ ImmutableList.<Artifact>of(),
+ filesToBuild,
+ exposedObjcProviderBuilder);
}
/**
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/AppleWatch2Extension.java b/src/main/java/com/google/devtools/build/lib/rules/objc/AppleWatch2Extension.java
new file mode 100644
index 0000000000..d208062813
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/objc/AppleWatch2Extension.java
@@ -0,0 +1,176 @@
+// 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.packages.ImplicitOutputsFunction.fromTemplates;
+import static com.google.devtools.build.lib.rules.objc.ObjcProvider.FLAG;
+import static com.google.devtools.build.lib.rules.objc.ObjcProvider.Flag.HAS_WATCH2_EXTENSION;
+import static com.google.devtools.build.lib.rules.objc.ObjcProvider.MERGE_ZIP;
+import static com.google.devtools.build.lib.rules.objc.ObjcRuleClasses.WatchApplicationBundleRule.WATCH_APP_NAME_ATTR;
+
+import com.google.common.collect.HashMultiset;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Multiset;
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.analysis.ConfiguredTarget;
+import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode;
+import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder;
+import com.google.devtools.build.lib.analysis.RuleContext;
+import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
+import com.google.devtools.build.lib.packages.ImplicitOutputsFunction.SafeImplicitOutputsFunction;
+import com.google.devtools.build.lib.rules.RuleConfiguredTargetFactory;
+import com.google.devtools.build.lib.rules.objc.WatchUtils.WatchOSVersion;
+import com.google.devtools.build.lib.rules.test.InstrumentedFilesCollector;
+import com.google.devtools.build.lib.rules.test.InstrumentedFilesProvider;
+import com.google.devtools.build.lib.syntax.Type;
+
+/** Implementation for {@code apple_watch2_extension}. */
+public class AppleWatch2Extension implements RuleConfiguredTargetFactory {
+
+ /** Template for the containing application folder. */
+ public static final SafeImplicitOutputsFunction APP_NAME_IPA = fromTemplates("%{app_name}.ipa");
+
+ @Override
+ public ConfiguredTarget create(RuleContext ruleContext)
+ throws InterruptedException, RuleErrorException {
+ validateAttributes(ruleContext);
+
+ ObjcProvider.Builder exposedObjcProviderBuilder = new ObjcProvider.Builder();
+ NestedSetBuilder<Artifact> applicationFilesToBuild = NestedSetBuilder.stableOrder();
+
+ // 1. Build watch extension bundle.
+ createWatchExtensionBundle(ruleContext);
+
+ // 2. Build watch application bundle, which will contain the extension bundle.
+ createWatchApplicationBundle(
+ ruleContext,
+ watchExtensionIpaArtifact(ruleContext),
+ applicationFilesToBuild,
+ exposedObjcProviderBuilder);
+
+ RuleConfiguredTargetBuilder targetBuilder =
+ ObjcRuleClasses.ruleConfiguredTarget(ruleContext, applicationFilesToBuild.build())
+ .addProvider(
+ InstrumentedFilesProvider.class,
+ InstrumentedFilesCollector.forward(ruleContext, "binary"));
+
+ // 3. Add final watch application artifacts to the ObjcProvider, for bundling the watch
+ // application bundle into the final iOS application IPA depending on this rule.
+ exposedObjcProviderBuilder.add(MERGE_ZIP, ruleContext.getImplicitOutputArtifact(APP_NAME_IPA));
+ WatchUtils.registerActionsToAddWatchSupport(
+ ruleContext, exposedObjcProviderBuilder, WatchOSVersion.OS2);
+ exposedObjcProviderBuilder.add(FLAG, HAS_WATCH2_EXTENSION);
+ targetBuilder.addProvider(ObjcProvider.class, exposedObjcProviderBuilder.build());
+
+ return targetBuilder.build();
+ }
+
+ /**
+ * Registers actions to create the watch extension bundle.
+ *
+ * @param ruleContext rule context in which to create the bundle
+ */
+ private void createWatchExtensionBundle(RuleContext ruleContext) throws InterruptedException {
+ new Watch2ExtensionSupport(
+ ruleContext,
+ ObjcRuleClasses.intermediateArtifacts(ruleContext),
+ watchExtensionBundleName(ruleContext))
+ .createBundle(watchExtensionIpaArtifact(ruleContext));
+ }
+
+ /**
+ * Registers actions to create the watch application bundle. This will contain the watch extension
+ * bundle. The output artifacts are {@link #APP_NAME_IPA} (which is an implicit output of this
+ * rule), and artifacts which are added to {@code exposedObjcProviderBuilder} for consumption by
+ * depending targets.
+ *
+ * @param ruleContext rule context in which to create the bundle
+ * @param filesToBuild the list to contain the files to be built for this bundle
+ * @param exposedObjcProviderBuilder builder of {@link ObjcProvider} exposed to the parent target;
+ * bundling information will be added to this builder
+ */
+ private void createWatchApplicationBundle(
+ RuleContext ruleContext,
+ Artifact extensionIpa,
+ NestedSetBuilder<Artifact> filesToBuild,
+ ObjcProvider.Builder exposedObjcProviderBuilder)
+ throws InterruptedException {
+ new WatchApplicationSupport(
+ ruleContext,
+ WatchOSVersion.OS2,
+ // TODO(cparsons): Remove dependency attributes from WatchApplicationSupport,
+ // as this is redundant with other attributes.
+ ImmutableSet.<Attribute>of(),
+ new IntermediateArtifacts(ruleContext, "", watchApplicationBundleName(ruleContext)),
+ watchApplicationBundleName(ruleContext),
+ watchApplicationIpaArtifact(ruleContext),
+ watchApplicationBundleName(ruleContext))
+ .createBundle(ImmutableList.of(extensionIpa), filesToBuild, exposedObjcProviderBuilder);
+ }
+
+ /** Returns the {@Artifact} containing final watch application bundle. */
+ private Artifact watchApplicationIpaArtifact(RuleContext ruleContext)
+ throws InterruptedException {
+ return ruleContext.getImplicitOutputArtifact(APP_NAME_IPA);
+ }
+
+ /** Returns the {@Artifact} containing final watch extension bundle. */
+ private Artifact watchExtensionIpaArtifact(RuleContext ruleContext) throws InterruptedException {
+ return ruleContext.getImplicitOutputArtifact(ReleaseBundlingSupport.IPA);
+ }
+
+ private String watchApplicationBundleName(RuleContext ruleContext) {
+ return ruleContext.attributes().get(WATCH_APP_NAME_ATTR, Type.STRING);
+ }
+
+ private String watchExtensionBundleName(RuleContext ruleContext) {
+ return ruleContext.getLabel().getName();
+ }
+
+ private void validateAttributes(RuleContext ruleContext) throws RuleErrorException {
+ boolean hasError = false;
+
+ Multiset<Artifact> appResources = HashMultiset.create();
+ appResources.addAll(ruleContext.getPrerequisiteArtifacts("app_resources", Mode.TARGET).list());
+ appResources.addAll(ruleContext.getPrerequisiteArtifacts("app_strings", Mode.TARGET).list());
+
+ for (Multiset.Entry<Artifact> entry : appResources.entrySet()) {
+ if (entry.getCount() > 1) {
+ ruleContext.ruleError(
+ "The same file was included multiple times in this rule: "
+ + entry.getElement().getRootRelativePathString());
+ hasError = true;
+ }
+ }
+
+ Multiset<Artifact> extResources = HashMultiset.create();
+ extResources.addAll(ruleContext.getPrerequisiteArtifacts("ext_resources", Mode.TARGET).list());
+ extResources.addAll(ruleContext.getPrerequisiteArtifacts("ext_strings", Mode.TARGET).list());
+
+ for (Multiset.Entry<Artifact> entry : extResources.entrySet()) {
+ if (entry.getCount() > 1) {
+ ruleContext.ruleError(
+ "The same file was included multiple times in this rule: "
+ + entry.getElement().getRootRelativePathString());
+ hasError = true;
+ }
+ }
+
+ if (hasError) {
+ throw new RuleErrorException();
+ }
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/AppleWatch2ExtensionRule.java b/src/main/java/com/google/devtools/build/lib/rules/objc/AppleWatch2ExtensionRule.java
new file mode 100644
index 0000000000..2149b4c09e
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/objc/AppleWatch2ExtensionRule.java
@@ -0,0 +1,76 @@
+// 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.packages.Attribute.attr;
+import static com.google.devtools.build.lib.packages.BuildType.LABEL;
+
+import com.google.devtools.build.lib.analysis.BaseRuleClasses;
+import com.google.devtools.build.lib.analysis.RuleDefinition;
+import com.google.devtools.build.lib.analysis.RuleDefinitionEnvironment;
+import com.google.devtools.build.lib.packages.RuleClass;
+import com.google.devtools.build.lib.packages.RuleClass.Builder;
+import com.google.devtools.build.lib.rules.apple.AppleConfiguration;
+
+/** Rule definition for apple_watch2_extension. */
+public class AppleWatch2ExtensionRule implements RuleDefinition {
+
+ @Override
+ public RuleClass build(Builder builder, RuleDefinitionEnvironment env) {
+ return builder
+ .requiresConfigurationFragments(ObjcConfiguration.class, AppleConfiguration.class)
+ /* <!-- #BLAZE_RULE(apple_watch2_extension).ATTRIBUTE(binary) -->
+ The binary target containing the logic for the watch extension.
+ ${SYNOPSIS}
+ <!-- #END_BLAZE_RULE.ATTRIBUTE -->*/
+ .add(
+ attr("binary", LABEL)
+ .allowedRuleClasses("apple_binary")
+ .allowedFileTypes()
+ .mandatory()
+ .direct_compile_time_input())
+ .build();
+ }
+
+ @Override
+ public Metadata getMetadata() {
+ return RuleDefinition.Metadata.builder()
+ .name("apple_watch2_extension")
+ .factoryClass(AppleWatch2Extension.class)
+ .ancestors(
+ BaseRuleClasses.BaseRule.class,
+ ObjcRuleClasses.XcodegenRule.class,
+ ObjcRuleClasses.WatchApplicationBundleRule.class,
+ ObjcRuleClasses.WatchExtensionBundleRule.class)
+ .build();
+ }
+}
+
+/*<!-- #BLAZE_RULE (NAME = apple_watch2_extension, TYPE = BINARY, FAMILY = Objective-C) -->
+
+<p>This rule produces an extension bundle for apple watch OS 2.</p>
+
+<p>It requires attributes set for both the watchOS2 application and watchOS2 extension that will be
+ present in any final ios application bundle. Application attributes are prefixed with app_, and
+ extension attributes prefixed with ext_.</p>
+
+<p>The required 'binary' attribute should contain the apple_binary extension binary (built for
+ the watch platform type.</p>
+
+${IMPLICIT_OUTPUTS}
+
+${ATTRIBUTE_DEFINITION}
+
+<!-- #END_BLAZE_RULE -->*/
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 e00d64bf8d..3bbb1eb901 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
@@ -21,6 +21,7 @@ import static com.google.devtools.build.lib.rules.objc.ObjcProvider.Flag.USES_SW
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;
+import static com.google.devtools.build.lib.rules.objc.ObjcProvider.MULTI_ARCH_LINKED_BINARIES;
import static com.google.devtools.build.lib.rules.objc.ObjcProvider.NESTED_BUNDLE;
import static com.google.devtools.build.lib.rules.objc.ObjcProvider.ROOT_MERGE_ZIP;
import static com.google.devtools.build.lib.rules.objc.ObjcProvider.STORYBOARD;
@@ -249,13 +250,13 @@ final class Bundling {
}
private Optional<Artifact> combinedArchitectureBinary() {
- Optional<Artifact> combinedArchitectureBinary = Optional.absent();
- if (!Iterables.isEmpty(objcProvider.get(LIBRARY))
+ if (!Iterables.isEmpty(objcProvider.get(MULTI_ARCH_LINKED_BINARIES))) {
+ return Optional.of(Iterables.getOnlyElement(objcProvider.get(MULTI_ARCH_LINKED_BINARIES)));
+ } else if (!Iterables.isEmpty(objcProvider.get(LIBRARY))
|| !Iterables.isEmpty(objcProvider.get(IMPORTED_LIBRARY))) {
- combinedArchitectureBinary =
- Optional.of(intermediateArtifacts.combinedArchitectureBinary());
+ return Optional.of(intermediateArtifacts.combinedArchitectureBinary());
}
- return combinedArchitectureBinary;
+ return Optional.absent();
}
private Optional<Artifact> actoolzipOutput() {
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 0ccc9d2752..455526a393 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
@@ -86,6 +86,15 @@ public final class IntermediateArtifacts {
}
/**
+ * Returns the location of this target's extension plist which contains entries required by all
+ * watch extensions (for final merging into the bundle plist).
+ */
+ public Artifact watchExtensionAutomaticPlist() {
+ return ruleContext.getRelatedArtifact(
+ ruleContext.getUniqueDirectory("plists"), "-automatic-watchExtensionInfo.plist");
+ }
+
+ /**
* Returns a derived artifact in the bin directory obtained by appending some extension to the end
* of the given {@link PathFragment}.
*/
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/IosApplication.java b/src/main/java/com/google/devtools/build/lib/rules/objc/IosApplication.java
index 617fd3ece9..506c5088d4 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/objc/IosApplication.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/objc/IosApplication.java
@@ -61,7 +61,8 @@ public class IosApplication extends ReleaseBundlingTargetFactory {
protected void validateAttributes(RuleContext ruleContext) {
Iterable<ObjcProvider> extensionProviders = ruleContext.getPrerequisites(
"extensions", Mode.TARGET, ObjcProvider.class);
- if (hasMoreThanOneWatchExtension(extensionProviders, Flag.HAS_WATCH1_EXTENSION)) {
+ if (hasMoreThanOneWatchExtension(extensionProviders, Flag.HAS_WATCH1_EXTENSION)
+ || hasMoreThanOneWatchExtension(extensionProviders, Flag.HAS_WATCH2_EXTENSION)) {
ruleContext.attributeError("extensions", "An iOS application can contain exactly one "
+ "watch extension for each watch OS version");
}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/IosApplicationRule.java b/src/main/java/com/google/devtools/build/lib/rules/objc/IosApplicationRule.java
index c7a0a2bb26..e299f3aacf 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/objc/IosApplicationRule.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/objc/IosApplicationRule.java
@@ -51,23 +51,30 @@ public class IosApplicationRule implements RuleDefinition {
/* <!-- #BLAZE_RULE(ios_application).ATTRIBUTE(binary) -->
The binary target included in the final bundle.
<!-- #END_BLAZE_RULE.ATTRIBUTE -->*/
- .add(attr("binary", LABEL)
- .allowedRuleClasses("objc_binary")
- .allowedFileTypes()
- .mandatory()
- .direct_compile_time_input()
- .cfg(IosApplication.SPLIT_ARCH_TRANSITION))
+ .add(
+ attr("binary", LABEL)
+ .allowedRuleClasses("objc_binary")
+ .allowedFileTypes()
+ .mandatory()
+ .direct_compile_time_input()
+ .cfg(IosApplication.SPLIT_ARCH_TRANSITION))
/* <!-- #BLAZE_RULE(ios_application).ATTRIBUTE(extensions) -->
Any extensions to include in the final application.
<!-- #END_BLAZE_RULE.ATTRIBUTE -->*/
- .add(attr("extensions", LABEL_LIST)
- .allowedRuleClasses("ios_extension", "apple_watch1_extension")
- .allowedFileTypes()
- .direct_compile_time_input())
- .add(attr("$runner_script_template", LABEL).cfg(HOST)
- .value(env.getToolsLabel("//tools/objc:ios_runner.sh.mac_template")))
- .add(attr("$is_executable", BOOLEAN).value(true)
- .nonconfigurable("Called from RunCommand.isExecutable, which takes a Target"))
+ .add(
+ attr("extensions", LABEL_LIST)
+ .allowedRuleClasses(
+ "ios_extension", "apple_watch1_extension", "apple_watch2_extension")
+ .allowedFileTypes()
+ .direct_compile_time_input())
+ .add(
+ attr("$runner_script_template", LABEL)
+ .cfg(HOST)
+ .value(env.getToolsLabel("//tools/objc:ios_runner.sh.mac_template")))
+ .add(
+ attr("$is_executable", BOOLEAN)
+ .value(true)
+ .nonconfigurable("Called from RunCommand.isExecutable, which takes a Target"))
.build();
}
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 067d2acb8f..63ad021223 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
@@ -35,7 +35,6 @@ import com.google.devtools.build.lib.syntax.EvalUtils;
import com.google.devtools.build.lib.util.Preconditions;
import com.google.devtools.build.lib.vfs.PathFragment;
import com.google.devtools.build.xcode.xcodegen.proto.XcodeGenProtos.TargetControl;
-
import java.util.HashMap;
import java.util.Map;
@@ -106,6 +105,10 @@ public final class ObjcProvider extends SkylarkClassObject implements Transitive
public static final Key<Artifact> LINKED_BINARY =
new Key<>(STABLE_ORDER, "linked_binary", Artifact.class);
+ /** Combined-architecture binaries to include in the final bundle. */
+ public static final Key<Artifact> MULTI_ARCH_LINKED_BINARIES =
+ new Key<>(STABLE_ORDER, "combined_arch_linked_binary", Artifact.class);
+
/**
* Indicates which libraries to load with {@code -force_load}. This is a subset of the union of
* the {@link #LIBRARY} and {@link #IMPORTED_LIBRARY} sets.
@@ -343,9 +346,16 @@ public final class ObjcProvider extends SkylarkClassObject implements Transitive
USES_SWIFT,
/**
- * Indicates that watch os 1 extension is present in the bundle.
+ * Indicates that a watchOS 1 extension is present in the bundle. (There can only be one
+ * extension for any given watchOS version in a given bundle).
*/
HAS_WATCH1_EXTENSION,
+
+ /**
+ * Indicates that a watchOS 2 extension is present in the bundle. (There can only be one
+ * extension for any given watchOS version in a given bundle).
+ */
+ HAS_WATCH2_EXTENSION,
}
private final ImmutableMap<Key<?>, NestedSet<?>> items;
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/Watch2ExtensionSupport.java b/src/main/java/com/google/devtools/build/lib/rules/objc/Watch2ExtensionSupport.java
new file mode 100644
index 0000000000..4a0b24ae21
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/objc/Watch2ExtensionSupport.java
@@ -0,0 +1,236 @@
+// 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.BUNDLE_FILE;
+import static com.google.devtools.build.lib.rules.objc.ObjcProvider.GENERAL_RESOURCE_DIR;
+import static com.google.devtools.build.lib.rules.objc.ObjcProvider.GENERAL_RESOURCE_FILE;
+import static com.google.devtools.build.lib.rules.objc.ObjcProvider.STRINGS;
+import static com.google.devtools.build.lib.rules.objc.ObjcRuleClasses.BundlingRule.FAMILIES_ATTR;
+import static com.google.devtools.build.lib.rules.objc.ObjcRuleClasses.WatchExtensionBundleRule.WATCH_EXT_BUNDLE_ID_ATTR;
+import static com.google.devtools.build.lib.rules.objc.ObjcRuleClasses.WatchExtensionBundleRule.WATCH_EXT_DEFAULT_PROVISIONING_PROFILE_ATTR;
+import static com.google.devtools.build.lib.rules.objc.ObjcRuleClasses.WatchExtensionBundleRule.WATCH_EXT_ENTITLEMENTS_ATTR;
+import static com.google.devtools.build.lib.rules.objc.ObjcRuleClasses.WatchExtensionBundleRule.WATCH_EXT_FAMILIES_ATTR;
+import static com.google.devtools.build.lib.rules.objc.ObjcRuleClasses.WatchExtensionBundleRule.WATCH_EXT_INFOPLISTS_ATTR;
+import static com.google.devtools.build.lib.rules.objc.ObjcRuleClasses.WatchExtensionBundleRule.WATCH_EXT_PROVISIONING_PROFILE_ATTR;
+import static com.google.devtools.build.lib.rules.objc.ObjcRuleClasses.WatchExtensionBundleRule.WATCH_EXT_RESOURCES_ATTR;
+import static com.google.devtools.build.lib.rules.objc.ObjcRuleClasses.WatchExtensionBundleRule.WATCH_EXT_STRINGS_ATTR;
+import static com.google.devtools.build.lib.rules.objc.ObjcRuleClasses.WatchExtensionBundleRule.WATCH_EXT_STRUCTURED_RESOURCES_ATTR;
+
+import com.dd.plist.NSDictionary;
+import com.dd.plist.NSObject;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+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;
+import com.google.devtools.build.lib.analysis.actions.FileWriteAction;
+import com.google.devtools.build.lib.rules.objc.ReleaseBundlingSupport.LinkedBinary;
+import com.google.devtools.build.lib.rules.objc.TargetDeviceFamily.InvalidFamilyNameException;
+import com.google.devtools.build.lib.rules.objc.TargetDeviceFamily.RepeatedFamilyNameException;
+import com.google.devtools.build.lib.syntax.Type;
+import java.util.List;
+import javax.annotation.Nullable;
+
+/**
+ * Contains support methods to build watch extension bundle - does normal bundle processing -
+ * compiling and linking the binary, resources, plists and creates a final (signed if necessary)
+ * bundle.
+ */
+public class Watch2ExtensionSupport {
+
+ private final RuleContext ruleContext;
+ private final IntermediateArtifacts intermediateArtifacts;
+ private final String bundleName;
+ private final Attributes attributes;
+
+ /**
+ * @param ruleContext the current rule context
+ * @param intermediateArtifacts the utility object to obtain namespacing for intermediate bundling
+ * artifacts
+ * @param bundleName the name of the bundle
+ */
+ Watch2ExtensionSupport(
+ RuleContext ruleContext, IntermediateArtifacts intermediateArtifacts, String bundleName) {
+ this.ruleContext = ruleContext;
+ this.intermediateArtifacts = intermediateArtifacts;
+ this.bundleName = bundleName;
+ this.attributes = new Attributes(ruleContext);
+ }
+
+ /**
+ * Registers actions to create a watchOS2 extension bundle and zip it into an {@code .ipa}.
+ *
+ * @param ipaArtifact an .ipa artifact containing to extension bundle; this is the output artifact
+ * of the bundling
+ */
+ void createBundle(Artifact ipaArtifact) throws InterruptedException {
+ ObjcProvider.Builder releaseBundlingObjcProviderBuilder = new ObjcProvider.Builder();
+ releaseBundlingObjcProviderBuilder.addTransitiveAndPropagate(attributes.binaryDependencies());
+ releaseBundlingObjcProviderBuilder
+ .addAll(GENERAL_RESOURCE_FILE, attributes.resources())
+ .addAll(GENERAL_RESOURCE_FILE, attributes.strings())
+ .addAll(
+ GENERAL_RESOURCE_DIR,
+ ObjcCommon.xcodeStructuredResourceDirs(attributes.structuredResources()))
+ .addAll(BUNDLE_FILE, BundleableFile.flattenedRawResourceFiles(attributes.resources()))
+ .addAll(
+ BUNDLE_FILE,
+ BundleableFile.structuredRawResourceFiles(attributes.structuredResources()))
+ .addAll(STRINGS, attributes.strings());
+ ObjcProvider releaseBundlingObjcProvider = releaseBundlingObjcProviderBuilder.build();
+
+ registerWatchExtensionAutomaticPlistAction();
+
+ ImmutableSet<TargetDeviceFamily> families = attributes.families();
+
+ if (families.isEmpty()) {
+ ruleContext.attributeError(FAMILIES_ATTR, ReleaseBundling.INVALID_FAMILIES_ERROR);
+ }
+
+ ReleaseBundling.Builder releaseBundling =
+ new ReleaseBundling.Builder()
+ .setIpaArtifact(ipaArtifact)
+ .setBundleId(attributes.bundleId())
+ .setProvisioningProfile(attributes.provisioningProfile())
+ .setProvisioningProfileAttributeName(WATCH_EXT_PROVISIONING_PROFILE_ATTR)
+ .setTargetDeviceFamilies(families)
+ .setIntermediateArtifacts(intermediateArtifacts)
+ .setInfoPlistsFromRule(attributes.infoPlists())
+ .addInfoplistInput(intermediateArtifacts.watchExtensionAutomaticPlist())
+ .setEntitlements(attributes.entitlements());
+
+ if (attributes.isBundleIdExplicitySpecified()) {
+ releaseBundling.setPrimaryBundleId(attributes.bundleId());
+ } else {
+ releaseBundling.setFallbackBundleId(attributes.bundleId());
+ }
+
+ ReleaseBundlingSupport releaseBundlingSupport =
+ new ReleaseBundlingSupport(
+ ruleContext,
+ releaseBundlingObjcProvider,
+ LinkedBinary.DEPENDENCIES_ONLY,
+ ReleaseBundlingSupport.EXTENSION_BUNDLE_DIR_FORMAT,
+ bundleName,
+ WatchUtils.determineMinimumOsVersion(
+ ObjcRuleClasses.objcConfiguration(ruleContext).getMinimumOs()),
+ releaseBundling.build());
+
+ releaseBundlingSupport
+ .registerActions(DsymOutputType.APP)
+ .validateResources()
+ .validateAttributes();
+ }
+
+ /**
+ * Registers an action to generate a plist containing entries required for watch extension that
+ * should be added to the merged plist.
+ */
+ private void registerWatchExtensionAutomaticPlistAction() {
+ NSDictionary watchExtensionAutomaticEntries = new NSDictionary();
+ watchExtensionAutomaticEntries.put(
+ "UIRequiredDeviceCapabilities", NSObject.wrap(new String[] {"watch-companion"}));
+
+ ruleContext.registerAction(
+ new FileWriteAction(
+ ruleContext.getActionOwner(),
+ intermediateArtifacts.watchExtensionAutomaticPlist(),
+ watchExtensionAutomaticEntries.toGnuStepASCIIPropertyList(),
+ /*makeExecutable=*/ false));
+ }
+
+ /** Rule attributes used for creating watch application bundle. */
+ private static class Attributes {
+ private final RuleContext ruleContext;
+
+ private Attributes(RuleContext ruleContext) {
+ this.ruleContext = ruleContext;
+ }
+
+ /**
+ * Returns the value of the {@code families} attribute in a form that is more useful than a list
+ * of strings. Returns an empty set for any invalid {@code families} attribute value, including
+ * an empty list.
+ */
+ ImmutableSet<TargetDeviceFamily> families() {
+ List<String> rawFamilies =
+ ruleContext.attributes().get(WATCH_EXT_FAMILIES_ATTR, Type.STRING_LIST);
+ try {
+ return ImmutableSet.copyOf(TargetDeviceFamily.fromNamesInRule(rawFamilies));
+ } catch (InvalidFamilyNameException | RepeatedFamilyNameException e) {
+ return ImmutableSet.of();
+ }
+ }
+
+ @Nullable
+ Artifact provisioningProfile() {
+ Artifact explicitProvisioningProfile =
+ getPrerequisiteArtifact(WATCH_EXT_PROVISIONING_PROFILE_ATTR);
+ if (explicitProvisioningProfile != null) {
+ return explicitProvisioningProfile;
+ }
+ return getPrerequisiteArtifact(WATCH_EXT_DEFAULT_PROVISIONING_PROFILE_ATTR);
+ }
+
+ String bundleId() {
+ Preconditions.checkState(
+ !Strings.isNullOrEmpty(
+ ruleContext.attributes().get(WATCH_EXT_BUNDLE_ID_ATTR, Type.STRING)),
+ "requires a bundle_id value");
+ return ruleContext.attributes().get(WATCH_EXT_BUNDLE_ID_ATTR, Type.STRING);
+ }
+
+ ImmutableList<Artifact> infoPlists() {
+ return getPrerequisiteArtifacts(WATCH_EXT_INFOPLISTS_ATTR);
+ }
+
+ ImmutableList<Artifact> strings() {
+ return getPrerequisiteArtifacts(WATCH_EXT_STRINGS_ATTR);
+ }
+
+ ImmutableList<Artifact> resources() {
+ return getPrerequisiteArtifacts(WATCH_EXT_RESOURCES_ATTR);
+ }
+
+ ImmutableList<Artifact> structuredResources() {
+ return getPrerequisiteArtifacts(WATCH_EXT_STRUCTURED_RESOURCES_ATTR);
+ }
+
+ Iterable<ObjcProvider> binaryDependencies() {
+ return ruleContext.getPrerequisites("binary", Mode.TARGET, ObjcProvider.class);
+ }
+
+ @Nullable
+ Artifact entitlements() {
+ return getPrerequisiteArtifact(WATCH_EXT_ENTITLEMENTS_ATTR);
+ }
+
+ private boolean isBundleIdExplicitySpecified() {
+ return ruleContext.attributes().isAttributeValueExplicitlySpecified(WATCH_EXT_BUNDLE_ID_ATTR);
+ }
+
+ private ImmutableList<Artifact> getPrerequisiteArtifacts(String attribute) {
+ return ruleContext.getPrerequisiteArtifacts(attribute, Mode.TARGET).list();
+ }
+
+ @Nullable
+ private Artifact getPrerequisiteArtifact(String attribute) {
+ return ruleContext.getPrerequisiteArtifact(attribute, Mode.TARGET);
+ }
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/WatchApplicationSupport.java b/src/main/java/com/google/devtools/build/lib/rules/objc/WatchApplicationSupport.java
index ad0d1912c8..2a5bd462dc 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/objc/WatchApplicationSupport.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/objc/WatchApplicationSupport.java
@@ -18,6 +18,7 @@ import static com.google.devtools.build.lib.rules.objc.ObjcProvider.ASSET_CATALO
import static com.google.devtools.build.lib.rules.objc.ObjcProvider.BUNDLE_FILE;
import static com.google.devtools.build.lib.rules.objc.ObjcProvider.GENERAL_RESOURCE_DIR;
import static com.google.devtools.build.lib.rules.objc.ObjcProvider.GENERAL_RESOURCE_FILE;
+import static com.google.devtools.build.lib.rules.objc.ObjcProvider.MERGE_ZIP;
import static com.google.devtools.build.lib.rules.objc.ObjcProvider.STORYBOARD;
import static com.google.devtools.build.lib.rules.objc.ObjcProvider.STRINGS;
import static com.google.devtools.build.lib.rules.objc.ObjcProvider.XCASSETS_DIR;
@@ -34,6 +35,7 @@ import static com.google.devtools.build.lib.rules.objc.ObjcRuleClasses.WatchAppl
import static com.google.devtools.build.lib.rules.objc.ObjcRuleClasses.WatchApplicationBundleRule.WATCH_APP_STRUCTURED_RESOURCES_ATTR;
import com.google.common.base.Joiner;
+import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
@@ -52,7 +54,6 @@ import com.google.devtools.build.lib.rules.objc.ReleaseBundlingSupport.LinkedBin
import com.google.devtools.build.lib.rules.objc.WatchUtils.WatchOSVersion;
import com.google.devtools.build.lib.syntax.Type;
import com.google.devtools.build.xcode.xcodegen.proto.XcodeGenProtos.XcodeprojBuildSetting;
-
import javax.annotation.Nullable;
/**
@@ -69,12 +70,28 @@ final class WatchApplicationSupport {
private final Attributes attributes;
private final Artifact ipaArtifact;
private final String artifactPrefix;
- private final ConfigurationDistinguisher configurationDistinguisher;
- WatchApplicationSupport(RuleContext ruleContext, WatchOSVersion watchOSVersion,
- ImmutableSet<Attribute> dependencyAttributes, IntermediateArtifacts intermediateArtifacts,
- String bundleName, Artifact ipaArtifact, String artifactPrefix,
- ConfigurationDistinguisher configurationDistinguisher) {
+ /**
+ * @param ruleContext the current rule context
+ * @param watchOSVersion the version of watchOS for which to create an application bundle
+ * @param dependencyAttributes attributes on the current rule context to obtain transitive
+ * resources from
+ * @param intermediateArtifacts the utility object to obtain namespacing for intermediate bundling
+ * artifacts
+ * @param bundleName the name of the bundle
+ * @param ipaArtifact the output ipa created by this application bundling
+ * @param artifactPrefix the string prefix to prepend to bundling artifacts for the application --
+ * this prevents intermediate artifacts under this same rule context (such as watch extension
+ * bundling) from conflicting
+ */
+ WatchApplicationSupport(
+ RuleContext ruleContext,
+ WatchOSVersion watchOSVersion,
+ ImmutableSet<Attribute> dependencyAttributes,
+ IntermediateArtifacts intermediateArtifacts,
+ String bundleName,
+ Artifact ipaArtifact,
+ String artifactPrefix) {
this.ruleContext = ruleContext;
this.watchOSVersion = watchOSVersion;
this.dependencyAttributes = dependencyAttributes;
@@ -83,24 +100,82 @@ final class WatchApplicationSupport {
this.ipaArtifact = ipaArtifact;
this.artifactPrefix = artifactPrefix;
this.attributes = new Attributes(ruleContext);
- this.configurationDistinguisher = configurationDistinguisher;
}
+ /**
+ * Registers actions to create a watch application bundle.
+ *
+ * @param innerBundleZips any zip files to be unzipped and merged into the application bundle
+ * @param filesToBuild files to build for the rule; the watchOS application .ipa is added to this
+ * set
+ * @param exposedObjcProviderBuilder provider builder which watch application bundle outputs are
+ * added to (for later consumption by depending rules)
+ */
void createBundle(
+ Iterable<Artifact> innerBundleZips,
+ NestedSetBuilder<Artifact> filesToBuild,
+ ObjcProvider.Builder exposedObjcProviderBuilder)
+ throws InterruptedException {
+
+ ObjcProvider objcProvider = objcProvider(innerBundleZips);
+
+ createBundle(
+ Optional.<XcodeProvider.Builder>absent(),
+ objcProvider,
+ filesToBuild,
+ exposedObjcProviderBuilder);
+ }
+
+ /**
+ * Registers actions to create a watch application bundle and xcode project.
+ *
+ * @param xcodeProviderBuilder provider builder which xcode project generation information is
+ * added to (for later consumption by depending rules)
+ * @param innerBundleZips any zip files to be unzipped and merged into the application bundle
+ * @param filesToBuild files to build for the rule; the watchOS application .ipa is added to this
+ * set
+ * @param exposedObjcProviderBuilder provider builder which watch application bundle outputs are
+ * added to (for later consumption by depending rules)
+ */
+ void createBundleAndXcodeproj(
XcodeProvider.Builder xcodeProviderBuilder,
- ObjcProvider.Builder objcProviderBuilder,
+ Iterable<Artifact> innerBundleZips,
NestedSetBuilder<Artifact> filesToBuild,
ObjcProvider.Builder exposedObjcProviderBuilder)
throws InterruptedException {
+ ObjcProvider objcProvider = objcProvider(innerBundleZips);
+
+ createBundle(
+ Optional.of(xcodeProviderBuilder), objcProvider, filesToBuild, exposedObjcProviderBuilder);
+
// Add common watch settings.
WatchUtils.addXcodeSettings(ruleContext, xcodeProviderBuilder);
// Add watch application specific xcode settings.
addXcodeSettings(xcodeProviderBuilder);
+ XcodeSupport xcodeSupport =
+ new XcodeSupport(ruleContext, intermediateArtifacts, labelForWatchApplication())
+ .addXcodeSettings(
+ xcodeProviderBuilder,
+ objcProvider,
+ watchOSVersion.getApplicationXcodeProductType(),
+ ruleContext.getFragment(AppleConfiguration.class).getIosCpu(),
+ ConfigurationDistinguisher.WATCH_OS1_EXTENSION);
+
+ for (Attribute attribute : dependencyAttributes) {
+ xcodeSupport.addDependencies(xcodeProviderBuilder, attribute);
+ }
+ }
+
+ private void createBundle(
+ Optional<XcodeProvider.Builder> xcodeProviderBuilder,
+ ObjcProvider depsObjcProvider,
+ NestedSetBuilder<Artifact> filesToBuild,
+ ObjcProvider.Builder exposedObjcProviderBuilder)
+ throws InterruptedException {
registerActions();
- ObjcProvider objcProvider = objcProvider(objcProviderBuilder);
ReleaseBundling.Builder releaseBundling = new ReleaseBundling.Builder()
.setIpaArtifact(ipaArtifact)
.setBundleId(attributes.bundleId())
@@ -119,32 +194,27 @@ final class WatchApplicationSupport {
releaseBundling.setFallbackBundleId(attributes.bundleId());
}
- new ReleaseBundlingSupport(
- ruleContext,
- objcProvider,
- LinkedBinary.DEPENDENCIES_ONLY,
- ReleaseBundlingSupport.APP_BUNDLE_DIR_FORMAT,
- bundleName,
- WatchUtils.determineMinimumOsVersion(
- ObjcRuleClasses.objcConfiguration(ruleContext).getMinimumOs()),
- releaseBundling.build())
- .registerActions(DsymOutputType.APP)
- .addXcodeSettings(xcodeProviderBuilder)
+ ReleaseBundlingSupport releaseBundlingSupport =
+ new ReleaseBundlingSupport(
+ ruleContext,
+ depsObjcProvider,
+ LinkedBinary.DEPENDENCIES_ONLY,
+ watchOSVersion.getApplicationBundleDirFormat(),
+ bundleName,
+ WatchUtils.determineMinimumOsVersion(
+ ObjcRuleClasses.objcConfiguration(ruleContext).getMinimumOs()),
+ releaseBundling.build())
+ .registerActions(DsymOutputType.APP);
+
+ if (xcodeProviderBuilder.isPresent()) {
+ releaseBundlingSupport.addXcodeSettings(xcodeProviderBuilder.get());
+ }
+
+ releaseBundlingSupport
.addFilesToBuild(filesToBuild, DsymOutputType.APP)
.validateResources()
.validateAttributes()
.addExportedDebugArtifacts(exposedObjcProviderBuilder, DsymOutputType.APP);
-
- XcodeSupport xcodeSupport = new XcodeSupport(ruleContext, intermediateArtifacts,
- labelForWatchApplication())
- .addXcodeSettings(xcodeProviderBuilder, objcProvider,
- watchOSVersion.getApplicationXcodeProductType(),
- ruleContext.getFragment(AppleConfiguration.class).getIosCpu(),
- configurationDistinguisher);
-
- for (Attribute attribute : dependencyAttributes) {
- xcodeSupport.addDependencies(xcodeProviderBuilder, attribute);
- }
}
/**
@@ -208,16 +278,23 @@ final class WatchApplicationSupport {
watchKitStubZip.getFilename(),
Joiner.on(" ").join(ImmutableList.of("_WatchKitStub", bundleName))));
- ruleContext.registerAction(ObjcRuleClasses.spawnAppleEnvActionBuilder(ruleContext,
- ruleContext.getFragment(AppleConfiguration.class).getMultiArchPlatform(PlatformType.IOS))
- .setProgressMessage(
- "Copying WatchKit binary and stub resource: " + ruleContext.getLabel())
- .setShellCommand(ImmutableList.of("/bin/bash", "-c", Joiner.on(" ").join(command)))
- .addOutput(watchKitStubZip)
- .build(ruleContext));
+ ruleContext.registerAction(
+ ObjcRuleClasses.spawnAppleEnvActionBuilder(
+ ruleContext,
+ ruleContext
+ .getFragment(AppleConfiguration.class)
+ .getMultiArchPlatform(PlatformType.WATCHOS))
+ .setProgressMessage(
+ "Copying WatchKit binary and stub resource: " + ruleContext.getLabel())
+ .setShellCommand(ImmutableList.of("/bin/bash", "-c", Joiner.on(" ").join(command)))
+ .addOutput(watchKitStubZip)
+ .build(ruleContext));
}
- private ObjcProvider objcProvider(ObjcProvider.Builder objcProviderBuilder) {
+ private ObjcProvider objcProvider(Iterable<Artifact> innerBundleZips) {
+ ObjcProvider.Builder objcProviderBuilder = new ObjcProvider.Builder();
+ objcProviderBuilder.addAll(MERGE_ZIP, innerBundleZips);
+
// Add all resource files applicable to watch application from dependency providers.
for (Attribute attribute : dependencyAttributes) {
Iterable<ObjcProvider> dependencyObjcProviders = ruleContext.getPrerequisites(
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/WatchExtensionSupport.java b/src/main/java/com/google/devtools/build/lib/rules/objc/WatchExtensionSupport.java
index bed207d76f..ce070131d7 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/objc/WatchExtensionSupport.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/objc/WatchExtensionSupport.java
@@ -31,6 +31,8 @@ import static com.google.devtools.build.lib.rules.objc.ObjcRuleClasses.WatchExte
import static com.google.devtools.build.lib.rules.objc.ObjcRuleClasses.WatchExtensionBundleRule.WATCH_EXT_STRINGS_ATTR;
import static com.google.devtools.build.lib.rules.objc.ObjcRuleClasses.WatchExtensionBundleRule.WATCH_EXT_STRUCTURED_RESOURCES_ATTR;
+import com.dd.plist.NSDictionary;
+import com.dd.plist.NSObject;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
@@ -48,12 +50,7 @@ import com.google.devtools.build.lib.rules.objc.TargetDeviceFamily.InvalidFamily
import com.google.devtools.build.lib.rules.objc.TargetDeviceFamily.RepeatedFamilyNameException;
import com.google.devtools.build.lib.rules.objc.WatchUtils.WatchOSVersion;
import com.google.devtools.build.lib.syntax.Type;
-
-import com.dd.plist.NSDictionary;
-import com.dd.plist.NSObject;
-
import java.util.List;
-
import javax.annotation.Nullable;
/**
@@ -74,14 +71,15 @@ public class WatchExtensionSupport {
private final XcodeProvider watchApplicationXcodeProvider;
private final ConfigurationDistinguisher configurationDistinguisher;
- WatchExtensionSupport(RuleContext ruleContext,
+ WatchExtensionSupport(
+ RuleContext ruleContext,
WatchOSVersion watchOSVersion,
ImmutableSet<Attribute> dependencyAttributes,
IntermediateArtifacts intermediateArtifacts,
String bundleName,
Artifact ipaArtifact,
- Artifact watchApplicationBundle,
- XcodeProvider watchApplicationXcodeProvider,
+ @Nullable Artifact watchApplicationBundle,
+ @Nullable XcodeProvider watchApplicationXcodeProvider,
ConfigurationDistinguisher configurationDistinguisher) {
this.ruleContext = ruleContext;
this.watchOSVersion = watchOSVersion;
@@ -102,7 +100,10 @@ public class WatchExtensionSupport {
void createBundle(NestedSetBuilder<Artifact> filesToBuild,
ObjcProvider.Builder objcProviderBuilder, XcodeProvider.Builder xcodeProviderBuilder)
throws InterruptedException {
- WatchUtils.addXcodeSettings(ruleContext, xcodeProviderBuilder);
+
+ if (WatchUtils.isBuildingForWatchOS1Version(watchOSVersion)) {
+ WatchUtils.addXcodeSettings(ruleContext, xcodeProviderBuilder);
+ }
registerWatchExtensionAutomaticPlistAction();
@@ -142,26 +143,35 @@ public class WatchExtensionSupport {
ObjcRuleClasses.objcConfiguration(ruleContext).getMinimumOs()),
releaseBundling.build());
+ releaseBundlingSupport.registerActions(DsymOutputType.APP);
+
+ if (WatchUtils.isBuildingForWatchOS1Version(watchOSVersion)) {
+ releaseBundlingSupport.addXcodeSettings(xcodeProviderBuilder);
+ }
+
releaseBundlingSupport
- .registerActions(DsymOutputType.APP)
- .addXcodeSettings(xcodeProviderBuilder)
.addFilesToBuild(filesToBuild, DsymOutputType.APP)
.validateResources()
.validateAttributes();
- XcodeSupport xcodeSupport = new XcodeSupport(ruleContext)
- .addFilesToBuild(filesToBuild)
- .addXcodeSettings(xcodeProviderBuilder, objcProvider,
- watchOSVersion.getExtensionXcodeProductType(),
- ruleContext.getFragment(AppleConfiguration.class).getDependencySingleArchitecture(),
- configurationDistinguisher)
- .addDummySource(xcodeProviderBuilder);
-
- for (Attribute attribute : dependencyAttributes) {
- xcodeSupport.addDependencies(xcodeProviderBuilder, attribute);
- }
-
if (WatchUtils.isBuildingForWatchOS1Version(watchOSVersion)) {
+ XcodeSupport xcodeSupport =
+ new XcodeSupport(ruleContext)
+ .addFilesToBuild(filesToBuild)
+ .addXcodeSettings(
+ xcodeProviderBuilder,
+ objcProvider,
+ watchOSVersion.getExtensionXcodeProductType(),
+ ruleContext
+ .getFragment(AppleConfiguration.class)
+ .getDependencySingleArchitecture(),
+ configurationDistinguisher)
+ .addDummySource(xcodeProviderBuilder);
+
+ for (Attribute attribute : dependencyAttributes) {
+ xcodeSupport.addDependencies(xcodeProviderBuilder, attribute);
+ }
+
// Generate xcodeproj for watch OS 1 extension as the main target with watch application
// target as the dependency.
xcodeProviderBuilder.addPropagatedDependencies(
@@ -297,5 +307,4 @@ public class WatchExtensionSupport {
return ruleContext.getPrerequisiteArtifact(attribute, Mode.TARGET);
}
}
-
}
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
index 4909c2553c..70093373ad 100644
--- 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
@@ -28,27 +28,45 @@ import com.google.devtools.build.lib.rules.apple.Platform.PlatformType;
import com.google.devtools.build.xcode.xcodegen.proto.XcodeGenProtos.XcodeprojBuildSetting;
/**
- * Contains support methods for common processing and generating of watch extension and
- * application bundles.
+ * Contains support methods for common processing and generating of watch extension and application
+ * bundles.
*/
+// TODO(b/30503590): Refactor this into a support class -- such classes are better than this static
+// utility.
final class WatchUtils {
+ @VisibleForTesting
+ /** Bundle directory format for watch applications for watch OS 2. */
+ static final String WATCH2_APP_BUNDLE_DIR_FORMAT = "Watch/%s.app";
+
/**
* Supported Apple watch OS versions.
*/
enum WatchOSVersion {
- OS1(XcodeProductType.WATCH_OS1_APPLICATION, XcodeProductType.WATCH_OS1_EXTENSION,
- "WatchKitSupport");
+ OS1(
+ XcodeProductType.WATCH_OS1_APPLICATION,
+ XcodeProductType.WATCH_OS1_EXTENSION,
+ ReleaseBundlingSupport.APP_BUNDLE_DIR_FORMAT,
+ "WatchKitSupport"),
+ OS2(
+ XcodeProductType.WATCH_OS2_APPLICATION,
+ XcodeProductType.WATCH_OS2_EXTENSION,
+ WATCH2_APP_BUNDLE_DIR_FORMAT,
+ "WatchKitSupport2");
private final XcodeProductType applicationXcodeProductType;
private final XcodeProductType extensionXcodeProductType;
+ private final String applicationBundleDirFormat;
private final String watchKitSupportDirName;
- WatchOSVersion(XcodeProductType applicationXcodeProductType,
+ WatchOSVersion(
+ XcodeProductType applicationXcodeProductType,
XcodeProductType extensionXcodeProductType,
+ String applicationBundleDirFormat,
String watchKitSupportDirName) {
this.applicationXcodeProductType = applicationXcodeProductType;
this.extensionXcodeProductType = extensionXcodeProductType;
+ this.applicationBundleDirFormat = applicationBundleDirFormat;
this.watchKitSupportDirName = watchKitSupportDirName;
}
@@ -66,6 +84,11 @@ final class WatchUtils {
return extensionXcodeProductType;
}
+ /** Returns the bundle directory format of the watch application relative to its container. */
+ String getApplicationBundleDirFormat() {
+ return applicationBundleDirFormat;
+ }
+
/**
* Returns the name of the directory in the final iOS bundle which should contain the WatchKit
* support stub.
@@ -140,12 +163,16 @@ final class WatchUtils {
watchSupportZip.getFilename(),
watchKitSupportDirName));
- ruleContext.registerAction(ObjcRuleClasses.spawnAppleEnvActionBuilder(ruleContext,
- ruleContext.getFragment(AppleConfiguration.class).getMultiArchPlatform(PlatformType.IOS))
- .setProgressMessage("Copying Watchkit support to app bundle")
- .setShellCommand(ImmutableList.of("/bin/bash", "-c", Joiner.on(" ").join(command)))
- .addOutput(watchSupportZip)
- .build(ruleContext));
+ ruleContext.registerAction(
+ ObjcRuleClasses.spawnAppleEnvActionBuilder(
+ ruleContext,
+ ruleContext
+ .getFragment(AppleConfiguration.class)
+ .getMultiArchPlatform(PlatformType.WATCHOS))
+ .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));
}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/XcodeProductType.java b/src/main/java/com/google/devtools/build/lib/rules/objc/XcodeProductType.java
index df912c3df3..bc4bb96722 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/objc/XcodeProductType.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/objc/XcodeProductType.java
@@ -26,7 +26,9 @@ enum XcodeProductType {
EXTENSION("com.apple.product-type.app-extension"),
FRAMEWORK("com.apple.product-type.framework"),
WATCH_OS1_APPLICATION("com.apple.product-type.application.watchapp"),
- WATCH_OS1_EXTENSION("com.apple.product-type.watchkit-extension");
+ WATCH_OS2_APPLICATION("com.apple.product-type.application.watchapp2"),
+ WATCH_OS1_EXTENSION("com.apple.product-type.watchkit-extension"),
+ WATCH_OS2_EXTENSION("com.apple.product-type.watchkit2-extension");
private final String identifier;
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/XcodeProvider.java b/src/main/java/com/google/devtools/build/lib/rules/objc/XcodeProvider.java
index 38e39348a6..36cd017fd1 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/objc/XcodeProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/objc/XcodeProvider.java
@@ -53,7 +53,6 @@ import com.google.devtools.build.lib.vfs.PathFragment;
import com.google.devtools.build.xcode.xcodegen.proto.XcodeGenProtos.DependencyControl;
import com.google.devtools.build.xcode.xcodegen.proto.XcodeGenProtos.TargetControl;
import com.google.devtools.build.xcode.xcodegen.proto.XcodeGenProtos.XcodeprojBuildSetting;
-
import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashMap;
@@ -508,10 +507,15 @@ public final class XcodeProvider implements TransitiveInfoProvider {
}
@VisibleForTesting
- static final EnumSet<XcodeProductType> CAN_LINK_PRODUCT_TYPES = EnumSet.of(
- XcodeProductType.APPLICATION, XcodeProductType.BUNDLE, XcodeProductType.UNIT_TEST,
- XcodeProductType.EXTENSION, XcodeProductType.FRAMEWORK, XcodeProductType.WATCH_OS1_EXTENSION,
- XcodeProductType.WATCH_OS1_APPLICATION);
+ static final EnumSet<XcodeProductType> CAN_LINK_PRODUCT_TYPES =
+ EnumSet.of(
+ XcodeProductType.APPLICATION,
+ XcodeProductType.BUNDLE,
+ XcodeProductType.UNIT_TEST,
+ XcodeProductType.EXTENSION,
+ XcodeProductType.FRAMEWORK,
+ XcodeProductType.WATCH_OS1_EXTENSION,
+ XcodeProductType.WATCH_OS1_APPLICATION);
/**
* Returns the name of the Xcode target that corresponds to a build target with the given name.
@@ -637,8 +641,9 @@ public final class XcodeProvider implements TransitiveInfoProvider {
// but do have a dummy source file to make Xcode happy.
boolean hasSources = dependency.compilationArtifacts.isPresent()
&& dependency.compilationArtifacts.get().getArchive().isPresent();
- if (hasSources || (dependency.productType == XcodeProductType.BUNDLE
- || (dependency.productType == XcodeProductType.WATCH_OS1_APPLICATION))) {
+ if (hasSources
+ || (dependency.productType == XcodeProductType.BUNDLE
+ || (dependency.productType == XcodeProductType.WATCH_OS1_APPLICATION))) {
String dependencyXcodeTargetName = dependency.dependencyXcodeTargetName();
Set<DependencyControl> set = jreTargetNames.contains(dependencyXcodeTargetName)
? jreDependencySet : dependencySet;