diff options
Diffstat (limited to 'src/main/java/com/google/devtools')
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; |