diff options
author | 2015-02-20 15:48:41 +0000 | |
---|---|---|
committer | 2015-02-20 15:48:41 +0000 | |
commit | 344bcbc4a2b4ca6f76b0b43929c0d8f0a3cc2662 (patch) | |
tree | c94fdbc893d38b454f81cdb5273ac7133e437ace /src/main/java/com/google | |
parent | 254aee40df78e79ac1b19fe6d20ae20bb05129a8 (diff) |
Implement ios_extension rule. See IosExtensionRule.java for information on how app extensions are built and how they differ from application bundles.
RELNOTES: Support ios_extension and ios_extension_binary rules for creating iOS app extensions.
--
MOS_MIGRATED_REVID=86788086
Diffstat (limited to 'src/main/java/com/google')
19 files changed, 284 insertions, 105 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 e6ab3674f6..18232e3f8f 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 @@ -72,6 +72,7 @@ import com.google.devtools.build.lib.rules.java.JvmConfigurationLoader; import com.google.devtools.build.lib.rules.objc.IosApplicationRule; import com.google.devtools.build.lib.rules.objc.IosDeviceRule; import com.google.devtools.build.lib.rules.objc.IosExtensionBinaryRule; +import com.google.devtools.build.lib.rules.objc.IosExtensionRule; import com.google.devtools.build.lib.rules.objc.ObjcBinaryRule; import com.google.devtools.build.lib.rules.objc.ObjcBundleLibraryRule; import com.google.devtools.build.lib.rules.objc.ObjcBundleRule; @@ -260,6 +261,7 @@ public class BazelRuleClassProvider { builder.addRuleDefinition(ObjcRuleClasses.ResourceToolsRule.class); builder.addRuleDefinition(IosApplicationRule.class); builder.addRuleDefinition(IosExtensionBinaryRule.class); + builder.addRuleDefinition(IosExtensionRule.class); builder.addRuleDefinition(BazelExtraActionRule.class); builder.addRuleDefinition(BazelActionListenerRule.class); diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/objc/BazelIosTestRule.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/objc/BazelIosTestRule.java index d65de5c53c..d2454a363a 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/rules/objc/BazelIosTestRule.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/objc/BazelIosTestRule.java @@ -24,8 +24,8 @@ import com.google.devtools.build.lib.analysis.RuleDefinitionEnvironment; import com.google.devtools.build.lib.packages.ImplicitOutputsFunction; import com.google.devtools.build.lib.packages.RuleClass; import com.google.devtools.build.lib.packages.RuleClass.Builder.RuleClassType; -import com.google.devtools.build.lib.rules.objc.ApplicationSupport; import com.google.devtools.build.lib.rules.objc.ObjcRuleClasses; +import com.google.devtools.build.lib.rules.objc.ReleaseBundlingSupport; import com.google.devtools.build.lib.rules.objc.XcodeSupport; /** @@ -51,7 +51,7 @@ public final class BazelIosTestRule implements RuleDefinition { </ul> <!-- #END_BLAZE_RULE.IMPLICIT_OUTPUTS -->*/ .setImplicitOutputsFunction( - ImplicitOutputsFunction.fromFunctions(ApplicationSupport.IPA, XcodeSupport.PBXPROJ)) + ImplicitOutputsFunction.fromFunctions(ReleaseBundlingSupport.IPA, XcodeSupport.PBXPROJ)) .add(attr(BazelIosTest.IOS_TEST_ON_BAZEL_ATTR, LABEL) .value(env.getLabel("//tools/objc:ios_test_on_bazel")).exec()) .build(); diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/BinaryLinkingTargetFactory.java b/src/main/java/com/google/devtools/build/lib/rules/objc/BinaryLinkingTargetFactory.java index 0438091476..54ae2a1955 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/BinaryLinkingTargetFactory.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/BinaryLinkingTargetFactory.java @@ -27,11 +27,11 @@ import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode; import com.google.devtools.build.lib.analysis.RuleContext; import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; import com.google.devtools.build.lib.rules.RuleConfiguredTargetFactory; -import com.google.devtools.build.lib.rules.objc.ApplicationSupport.LinkedBinary; import com.google.devtools.build.lib.rules.objc.ObjcActionsBuilder.ExtraLinkArgs; import com.google.devtools.build.lib.rules.objc.ObjcActionsBuilder.ExtraLinkInputs; import com.google.devtools.build.lib.rules.objc.ObjcCommon.CompilationAttributes; import com.google.devtools.build.lib.rules.objc.ObjcCommon.ResourceAttributes; +import com.google.devtools.build.lib.rules.objc.ReleaseBundlingSupport.LinkedBinary; /** * Implementation for rules that link binaries. @@ -41,17 +41,17 @@ abstract class BinaryLinkingTargetFactory implements RuleConfiguredTargetFactory * Indicates whether this binary generates an application bundle. If so, it causes the * {@code infoplist} attribute to be read and a bundle to be added to the files-to-build. */ - enum HasApplicationSupport { + enum HasReleaseBundlingSupport { YES, NO; } - private final HasApplicationSupport hasApplicationSupport; + private final HasReleaseBundlingSupport hasReleaseBundlingSupport; private final ExtraLinkArgs extraLinkArgs; private final XcodeProductType productType; - protected BinaryLinkingTargetFactory(HasApplicationSupport hasApplicationSupport, + protected BinaryLinkingTargetFactory(HasReleaseBundlingSupport hasReleaseBundlingSupport, ExtraLinkArgs extraLinkArgs, XcodeProductType productType) { - this.hasApplicationSupport = hasApplicationSupport; + this.hasReleaseBundlingSupport = hasReleaseBundlingSupport; this.extraLinkArgs = extraLinkArgs; this.productType = productType; } @@ -76,25 +76,25 @@ abstract class BinaryLinkingTargetFactory implements RuleConfiguredTargetFactory .add(ObjcRuleClasses.intermediateArtifacts(ruleContext).singleArchitectureBinary()); new CompilationSupport(ruleContext) - .registerJ2ObjcCompileAndArchiveActions(optionsProvider, common.getObjcProvider()) + .registerJ2ObjcCompileAndArchiveActions(optionsProvider, objcProvider) .registerCompileAndArchiveActions(common, optionsProvider) .addXcodeSettings(xcodeProviderBuilder, common, optionsProvider) - .registerLinkActions(common.getObjcProvider(), extraLinkArgs, new ExtraLinkInputs()) + .registerLinkActions(objcProvider, extraLinkArgs, new ExtraLinkInputs()) .validateAttributes(); Optional<XcTestAppProvider> xcTestAppProvider; - switch (hasApplicationSupport) { + switch (hasReleaseBundlingSupport) { case YES: // TODO(bazel-team): Remove once all bundle users are migrated to ios_application. - ApplicationSupport applicationSupport = new ApplicationSupport( - ruleContext, common.getObjcProvider(), optionsProvider, - LinkedBinary.LOCAL_AND_DEPENDENCIES); - applicationSupport + ReleaseBundlingSupport releaseBundlingSupport = new ReleaseBundlingSupport( + ruleContext, objcProvider, optionsProvider, + LinkedBinary.LOCAL_AND_DEPENDENCIES, "Payload/%s.app"); + releaseBundlingSupport .registerActions() .addXcodeSettings(xcodeProviderBuilder) .addFilesToBuild(filesToBuild) .validateAttributes(); - xcTestAppProvider = Optional.of(applicationSupport.xcTestAppProvider()); + xcTestAppProvider = Optional.of(releaseBundlingSupport.xcTestAppProvider()); break; case NO: xcTestAppProvider = Optional.absent(); @@ -111,8 +111,10 @@ abstract class BinaryLinkingTargetFactory implements RuleConfiguredTargetFactory XcodeSupport xcodeSupport = new XcodeSupport(ruleContext) // TODO(bazel-team): Use LIBRARY_STATIC as parameter instead of APPLICATION once objc_binary // no longer creates an application bundle - .addXcodeSettings(xcodeProviderBuilder, common.getObjcProvider(), productType) - .addDependencies(xcodeProviderBuilder) + .addXcodeSettings(xcodeProviderBuilder, objcProvider, productType) + .addDependencies(xcodeProviderBuilder, "bundles") + .addDependencies(xcodeProviderBuilder, "deps") + .addDependencies(xcodeProviderBuilder, "non_propagated_deps") .addFilesToBuild(filesToBuild); XcodeProvider xcodeProvider = xcodeProviderBuilder.build(); xcodeSupport.registerActions(xcodeProvider); @@ -122,7 +124,7 @@ abstract class BinaryLinkingTargetFactory implements RuleConfiguredTargetFactory return common.configuredTarget( filesToBuild.build(), Optional.of(xcodeProvider), - Optional.<ObjcProvider>absent(), + Optional.of(objcProvider), xcTestAppProvider, Optional.<J2ObjcSrcsProvider>absent()); } @@ -132,7 +134,7 @@ abstract class BinaryLinkingTargetFactory implements RuleConfiguredTargetFactory .addCopts(ruleContext.getTokenizedStringListAttr("copts")) .addTransitive(Optional.fromNullable( ruleContext.getPrerequisite("options", Mode.TARGET, OptionsProvider.class))); - if (hasApplicationSupport == HasApplicationSupport.YES) { + if (hasReleaseBundlingSupport == HasReleaseBundlingSupport.YES) { provider .addInfoplists(ruleContext.getPrerequisiteArtifacts("infoplist", Mode.TARGET).list()); } diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/BundleMergeControlBytes.java b/src/main/java/com/google/devtools/build/lib/rules/objc/BundleMergeControlBytes.java index 695dffc4d6..224e9bf449 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/BundleMergeControlBytes.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/BundleMergeControlBytes.java @@ -54,14 +54,13 @@ final class BundleMergeControlBytes extends ByteSource { @Override public InputStream openStream() { - return control("Payload/", "Payload/", rootBundling) + return control("", rootBundling) .toByteString() .newInput(); } - private Control control(String mergeZipPrefix, String bundleDirPrefix, Bundling bundling) { + private Control control(String mergeZipPrefix, Bundling bundling) { ObjcProvider objcProvider = bundling.getObjcProvider(); - String bundleDir = bundleDirPrefix + bundling.getBundleDir(); mergeZipPrefix += bundling.getBundleDir() + "/"; BundleMergeProtos.Control.Builder control = BundleMergeProtos.Control.newBuilder() @@ -73,7 +72,7 @@ final class BundleMergeControlBytes extends ByteSource { .setMinimumOsVersion(objcConfiguration.getMinimumOs()) .setSdkVersion(objcConfiguration.getIosSdkVersion()) .setPlatform(objcConfiguration.getPlatform().name()) - .setBundleRoot(bundleDir); + .setBundleRoot(bundling.getBundleDir()); for (Artifact mergeZip : bundling.getMergeZips()) { control.addMergeZip(MergeZip.newBuilder() @@ -113,7 +112,7 @@ final class BundleMergeControlBytes extends ByteSource { } for (Bundling nestedBundling : bundling.getObjcProvider().get(NESTED_BUNDLE)) { - control.addNestedBundle(control(mergeZipPrefix, "", nestedBundling)); + control.addNestedBundle(control(mergeZipPrefix, nestedBundling)); } return control.build(); 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 484c553678..7ba2f6ea7e 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 @@ -31,7 +31,7 @@ import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.collect.nestedset.NestedSet; import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; -import com.google.devtools.build.xcode.util.Value; +import com.google.devtools.build.lib.vfs.PathFragment; import java.util.Map; @@ -39,10 +39,10 @@ import java.util.Map; * Contains information regarding the creation of an iOS bundle. */ @Immutable -final class Bundling extends Value<Bundling> { +final class Bundling { static final class Builder { private String name; - private String bundleDirSuffix; + private String bundleDirFormat; private ImmutableList<BundleableFile> extraBundleFiles = ImmutableList.of(); private ObjcProvider objcProvider; private InfoplistMerging infoplistMerging; @@ -53,8 +53,8 @@ final class Bundling extends Value<Bundling> { return this; } - public Builder setBundleDirSuffix(String bundleDirSuffix) { - this.bundleDirSuffix = bundleDirSuffix; + public Builder setBundleDirFormat(String bundleDirFormat) { + this.bundleDirFormat = bundleDirFormat; return this; } @@ -98,7 +98,7 @@ final class Bundling extends Value<Bundling> { if (!Iterables.isEmpty(objcProvider.get(LIBRARY)) || !Iterables.isEmpty(objcProvider.get(IMPORTED_LIBRARY))) { combinedArchitectureBinary = - Optional.of(intermediateArtifacts.combinedArchitectureBinary(bundleDirSuffix)); + Optional.of(intermediateArtifacts.combinedArchitectureBinary()); } NestedSet<Artifact> mergeZips = NestedSetBuilder.<Artifact>stableOrder() @@ -115,13 +115,13 @@ final class Bundling extends Value<Bundling> { .addAll(Xcdatamodel.outputZips(objcProvider.get(XCDATAMODEL))) .build(); - return new Bundling(name, bundleDirSuffix, combinedArchitectureBinary, extraBundleFiles, + return new Bundling(name, bundleDirFormat, combinedArchitectureBinary, extraBundleFiles, objcProvider, infoplistMerging, actoolzipOutput, bundleContentArtifacts, mergeZips); } } private final String name; - private final String bundleDirSuffix; + private final String bundleDirFormat; private final Optional<Artifact> combinedArchitectureBinary; private final ImmutableList<BundleableFile> extraBundleFiles; private final ObjcProvider objcProvider; @@ -132,7 +132,7 @@ final class Bundling extends Value<Bundling> { private Bundling( String name, - String bundleDirSuffix, + String bundleDirFormat, Optional<Artifact> combinedArchitectureBinary, ImmutableList<BundleableFile> extraBundleFiles, ObjcProvider objcProvider, @@ -140,36 +140,24 @@ final class Bundling extends Value<Bundling> { Optional<Artifact> actoolzipOutput, NestedSet<Artifact> bundleContentArtifacts, NestedSet<Artifact> mergeZips) { - super(new ImmutableMap.Builder<String, Object>() - .put("name", name) - .put("bundleDirSuffix", bundleDirSuffix) - .put("combinedArchitectureBinary", combinedArchitectureBinary) - .put("extraBundleFiles", extraBundleFiles) - .put("objcProvider", objcProvider) - .put("infoplistMerging", infoplistMerging) - .put("actoolzipOutput", actoolzipOutput) - .put("bundleContentArtifacts", bundleContentArtifacts) - .put("mergeZips", mergeZips) - .build()); - this.name = name; - this.bundleDirSuffix = bundleDirSuffix; - this.combinedArchitectureBinary = combinedArchitectureBinary; - this.extraBundleFiles = extraBundleFiles; - this.objcProvider = objcProvider; - this.infoplistMerging = infoplistMerging; - this.actoolzipOutput = actoolzipOutput; - this.bundleContentArtifacts = bundleContentArtifacts; - this.mergeZips = mergeZips; + this.name = Preconditions.checkNotNull(name); + this.bundleDirFormat = Preconditions.checkNotNull(bundleDirFormat); + this.combinedArchitectureBinary = Preconditions.checkNotNull(combinedArchitectureBinary); + this.extraBundleFiles = Preconditions.checkNotNull(extraBundleFiles); + this.objcProvider = Preconditions.checkNotNull(objcProvider); + this.infoplistMerging = Preconditions.checkNotNull(infoplistMerging); + this.actoolzipOutput = Preconditions.checkNotNull(actoolzipOutput); + this.bundleContentArtifacts = Preconditions.checkNotNull(bundleContentArtifacts); + this.mergeZips = Preconditions.checkNotNull(mergeZips); } /** - * The bundle directory. For apps, {@code "Payload/" + bundleDir} is the directory in the bundle - * zip archive in which every file is found including the linked binary, nested bundles, and - * everything returned by {@link #getExtraBundleFiles()}. In an application bundle, for instance, - * this function returns {@code "(name).app"}. + * The bundle directory. For apps, this would be {@code "Payload/TARGET_NAME.app"}, which is where + * in the bundle zip archive every file is found, including the linked binary, nested bundles, and + * everything returned by {@link #getExtraBundleFiles()}. */ public String getBundleDir() { - return name + bundleDirSuffix; + return String.format(bundleDirFormat, name); } /** @@ -241,7 +229,7 @@ final class Bundling extends Value<Bundling> { public Map<String, String> variableSubstitutions() { return ImmutableMap.of( "EXECUTABLE_NAME", name, - "BUNDLE_NAME", name + bundleDirSuffix, + "BUNDLE_NAME", new PathFragment(getBundleDir()).getBaseName(), "PRODUCT_NAME", name); } 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 d87b4fb52e..2961d2ae6a 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 @@ -111,12 +111,9 @@ final class IntermediateArtifacts { /** * Lipo binary generated by combining one or more linked binaries. This binary is the one included * in generated bundles and invoked as entry point to the application. - * - * @param bundleDirSuffix suffix of the bundle containing this binary */ - public Artifact combinedArchitectureBinary(String bundleDirSuffix) { - String baseName = ownerLabel.toPathFragment().getBaseName(); - return appendExtension(bundleDirSuffix + "/" + baseName); + public Artifact combinedArchitectureBinary() { + return appendExtension("_lipobin"); } /** diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/IosExtension.java b/src/main/java/com/google/devtools/build/lib/rules/objc/IosExtension.java new file mode 100644 index 0000000000..4b6079acd8 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/IosExtension.java @@ -0,0 +1,81 @@ +// Copyright 2014 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.devtools.build.lib.rules.objc; + +import static com.google.devtools.build.lib.rules.objc.ObjcProvider.MERGE_ZIP; + +import com.google.common.base.Optional; +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.RuleContext; +import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; +import com.google.devtools.build.lib.rules.RuleConfiguredTargetFactory; +import com.google.devtools.build.lib.rules.objc.ReleaseBundlingSupport.LinkedBinary; + +/** + * Implementation for {@code ios_extension}. + */ +public class IosExtension implements RuleConfiguredTargetFactory { + + @Override + public ConfiguredTarget create(RuleContext ruleContext) throws InterruptedException { + ObjcCommon common = common(ruleContext); + + XcodeProvider.Builder xcodeProviderBuilder = new XcodeProvider.Builder(); + NestedSetBuilder<Artifact> filesToBuild = NestedSetBuilder.stableOrder(); + + ReleaseBundlingSupport releaseBundlingSupport = new ReleaseBundlingSupport( + ruleContext, common.getObjcProvider(), optionsProvider(ruleContext), + LinkedBinary.DEPENDENCIES_ONLY, "PlugIns/%s.appex"); + releaseBundlingSupport + .registerActions() + .addXcodeSettings(xcodeProviderBuilder) + .addFilesToBuild(filesToBuild) + .validateAttributes(); + + new XcodeSupport(ruleContext) + .addFilesToBuild(filesToBuild) + .addXcodeSettings( + xcodeProviderBuilder, common.getObjcProvider(), XcodeProductType.EXTENSION) + .addDependencies(xcodeProviderBuilder, "binary") + .registerActions(xcodeProviderBuilder.build()); + + ObjcProvider nestedBundleProvider = new ObjcProvider.Builder() + .add(MERGE_ZIP, ruleContext.getImplicitOutputArtifact(ReleaseBundlingSupport.IPA)) + .build(); + + return common.configuredTarget( + filesToBuild.build(), + Optional.of(xcodeProviderBuilder.build()), + Optional.of(nestedBundleProvider), + Optional.<XcTestAppProvider>absent(), + Optional.<J2ObjcSrcsProvider>absent()); + } + + private OptionsProvider optionsProvider(RuleContext ruleContext) { + return new OptionsProvider.Builder() + .addInfoplists(ruleContext.getPrerequisiteArtifacts("infoplist", Mode.TARGET).list()) + .build(); + } + + private ObjcCommon common(RuleContext ruleContext) { + return new ObjcCommon.Builder(ruleContext) + .setIntermediateArtifacts(ObjcRuleClasses.intermediateArtifacts(ruleContext)) + .addDepObjcProviders( + ruleContext.getPrerequisites("binary", Mode.TARGET, ObjcProvider.class)) + .build(); + } +} diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/IosExtensionBinary.java b/src/main/java/com/google/devtools/build/lib/rules/objc/IosExtensionBinary.java index c930a84005..cecc3d9f2c 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/IosExtensionBinary.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/IosExtensionBinary.java @@ -21,7 +21,7 @@ import com.google.devtools.build.lib.rules.objc.ObjcActionsBuilder.ExtraLinkArgs */ public class IosExtensionBinary extends BinaryLinkingTargetFactory { public IosExtensionBinary() { - super(HasApplicationSupport.NO, + super(HasReleaseBundlingSupport.NO, new ExtraLinkArgs("-e", "_NSExtensionMain", "-fapplication-extension"), XcodeProductType.LIBRARY_STATIC); } diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/IosExtensionRule.java b/src/main/java/com/google/devtools/build/lib/rules/objc/IosExtensionRule.java new file mode 100644 index 0000000000..a3491e0b9d --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/IosExtensionRule.java @@ -0,0 +1,102 @@ +// Copyright 2015 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.devtools.build.lib.rules.objc; + +import static com.google.devtools.build.lib.packages.Attribute.attr; +import static com.google.devtools.build.lib.packages.Type.LABEL; + +import com.google.devtools.build.lib.analysis.BaseRuleClasses; +import com.google.devtools.build.lib.analysis.BlazeRule; +import com.google.devtools.build.lib.analysis.RuleDefinition; +import com.google.devtools.build.lib.analysis.RuleDefinitionEnvironment; +import com.google.devtools.build.lib.packages.ImplicitOutputsFunction; +import com.google.devtools.build.lib.packages.RuleClass; +import com.google.devtools.build.lib.packages.RuleClass.Builder; + +/** + * Rule definition for ios_extension. + */ +@BlazeRule(name = "ios_extension", + factoryClass = IosExtension.class, + ancestors = { + BaseRuleClasses.BaseRule.class, + ObjcRuleClasses.ReleaseBundlingRule.class, + ObjcRuleClasses.XcodegenRule.class }) +public class IosExtensionRule implements RuleDefinition { + @Override + public RuleClass build(Builder builder, RuleDefinitionEnvironment env) { + return builder + /*<!-- #BLAZE_RULE(ios_extension).IMPLICIT_OUTPUTS --> + <ul> + <li><code><var>name</var>.ipa</code>: the extension bundle as an <code>.ipa</code> + file</li> + <li><code><var>name</var>.xcodeproj/project.pbxproj</code>: An Xcode project file which + can be used to develop or build on a Mac.</li> + </ul> + <!-- #END_BLAZE_RULE.IMPLICIT_OUTPUTS -->*/ + .setImplicitOutputsFunction( + ImplicitOutputsFunction.fromFunctions(ReleaseBundlingSupport.IPA, XcodeSupport.PBXPROJ)) + /* <!-- #BLAZE_RULE(ios_extension).ATTRIBUTE(binary) --> + The binary target containing the logic for the extension. + ${SYNOPSIS} + <!-- #END_BLAZE_RULE.ATTRIBUTE -->*/ + .add(attr("binary", LABEL) + .allowedRuleClasses("ios_extension_binary") + .allowedFileTypes() + .mandatory() + .direct_compile_time_input()) + .build(); + } +} + +/*<!-- #BLAZE_RULE (NAME = ios_extension, TYPE = BINARY, FAMILY = Objective-C) --> + +${ATTRIBUTE_SIGNATURE} + +<p>This rule produces a bundled binary for an iOS app extension from a compiled binary and bundle +metadata.</p> + +<p>An iOS app extension is a nested bundle that is located inside the application bundle and is +released with it. An iOS app extension cannot be released alone, although this rule allows you to +build an <code>.ipa</code> with only the extension. + +<p>Bundles generated by this rule use a bundle directory called +<code>PlugIns/<var>target-name</var>.appex</code>, while an application bundle uses +<code>Payload/<var>target-name</var>.app</code>. For instance, if an application call Foo has an app +extension called Bar, the Bar extension bundle files will be stored in +<code>Payload/Foo.app/PlugIns/Bar.appex</code> in the final application <code>.ipa</code>. + +<p>There are many similarities between app extensions and applications with little to no difference +between how each thing is processed: +<ul> + <li>both have entitlements and Info.plist files + <li>both are code-signed. Signing and merging happens in this order: the extension is code-signed, + bundles are merged, application is code-signed + <li>both can have an app icon and launch image, and of course asset catalogs and all kinds of + resources + <li>both have linked binaries. The app extension binary is different in that it is linked with + these additional flags: + <ul> + <li><code>-e _NSExtensionMain</code> - sets the entry point to a standard function in the + iOS runtime rather than <code>main()</code> + <li><code>-fapplicationextension</code> + </ul> +</ul> + +${IMPLICIT_OUTPUTS} + +${ATTRIBUTE_DEFINITION} + +<!-- #END_BLAZE_RULE -->*/ diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/IosTest.java b/src/main/java/com/google/devtools/build/lib/rules/objc/IosTest.java index d814c84bc7..b1645ba7f8 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/IosTest.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/IosTest.java @@ -26,9 +26,9 @@ import com.google.devtools.build.lib.collect.nestedset.NestedSet; import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; import com.google.devtools.build.lib.packages.Type; import com.google.devtools.build.lib.rules.RuleConfiguredTargetFactory; -import com.google.devtools.build.lib.rules.objc.ApplicationSupport.LinkedBinary; import com.google.devtools.build.lib.rules.objc.ObjcActionsBuilder.ExtraLinkArgs; import com.google.devtools.build.lib.rules.objc.ObjcActionsBuilder.ExtraLinkInputs; +import com.google.devtools.build.lib.rules.objc.ReleaseBundlingSupport.LinkedBinary; import java.util.ArrayList; import java.util.List; @@ -115,8 +115,10 @@ public abstract class IosTest implements RuleConfiguredTargetFactory { .addXcodeSettings(xcodeProviderBuilder, common, optionsProvider) .validateAttributes(); - new ApplicationSupport( - ruleContext, common.getObjcProvider(), optionsProvider, LinkedBinary.LOCAL_AND_DEPENDENCIES) + ReleaseBundlingSupport releaseBundlingSupport = new ReleaseBundlingSupport( + ruleContext, common.getObjcProvider(), optionsProvider, LinkedBinary.LOCAL_AND_DEPENDENCIES, + "Payload/%s.app"); + releaseBundlingSupport .registerActions() .addXcodeSettings(xcodeProviderBuilder) .addFilesToBuild(filesToBuild) @@ -129,7 +131,9 @@ public abstract class IosTest implements RuleConfiguredTargetFactory { new XcodeSupport(ruleContext) .addXcodeSettings(xcodeProviderBuilder, common.getObjcProvider(), productType) - .addDependencies(xcodeProviderBuilder) + .addDependencies(xcodeProviderBuilder, "bundles") + .addDependencies(xcodeProviderBuilder, "deps") + .addDependencies(xcodeProviderBuilder, "non_propagated_deps") .addFilesToBuild(filesToBuild) .registerActions(xcodeProviderBuilder.build()); diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcBinary.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcBinary.java index 23bd8ea515..37b772b99e 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcBinary.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcBinary.java @@ -24,7 +24,7 @@ public class ObjcBinary extends BinaryLinkingTargetFactory { super( // TODO(bazel-team): Remove the enum and delete all code depending on YES case once all // bundle users are migrated to ios_application. - HasApplicationSupport.YES, + HasReleaseBundlingSupport.YES, new ExtraLinkArgs(), // TODO(bazel-team): Use LIBRARY_STATIC as parameter instead of APPLICATION once objc_binary diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcBinaryRule.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcBinaryRule.java index 97c7ee41ea..7e16a213e5 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcBinaryRule.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcBinaryRule.java @@ -47,7 +47,7 @@ public class ObjcBinaryRule implements RuleDefinition { </ul> <!-- #END_BLAZE_RULE.IMPLICIT_OUTPUTS -->*/ .setImplicitOutputsFunction( - ImplicitOutputsFunction.fromFunctions(ApplicationSupport.IPA, XcodeSupport.PBXPROJ)) + ImplicitOutputsFunction.fromFunctions(ReleaseBundlingSupport.IPA, XcodeSupport.PBXPROJ)) .build(); } } diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcBundleLibrary.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcBundleLibrary.java index 0e7f6b0962..253fe32814 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcBundleLibrary.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcBundleLibrary.java @@ -58,6 +58,7 @@ public class ObjcBundleLibrary implements RuleConfiguredTargetFactory { new XcodeSupport(ruleContext) .addFilesToBuild(filesToBuild) .addXcodeSettings(xcodeProviderBuilder, common.getObjcProvider(), BUNDLE) + .addDependencies(xcodeProviderBuilder, "bundles") .registerActions(xcodeProviderBuilder.build()); ObjcProvider nestedBundleProvider = new ObjcProvider.Builder() @@ -84,7 +85,7 @@ public class ObjcBundleLibrary implements RuleConfiguredTargetFactory { ObjcRuleClasses.intermediateArtifacts(ruleContext); return new Bundling.Builder() .setName(ruleContext.getLabel().getName()) - .setBundleDirSuffix(".bundle") + .setBundleDirFormat("%s.bundle") .setObjcProvider(common.getObjcProvider()) .setInfoplistMerging( BundleSupport.infoPlistMerging(ruleContext, common.getObjcProvider(), optionsProvider)) diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcLibrary.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcLibrary.java index 702f9c1809..c13b510882 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcLibrary.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcLibrary.java @@ -96,7 +96,9 @@ public class ObjcLibrary implements RuleConfiguredTargetFactory { new XcodeSupport(ruleContext) .addFilesToBuild(filesToBuild) .addXcodeSettings(xcodeProviderBuilder, common.getObjcProvider(), LIBRARY_STATIC) - .addDependencies(xcodeProviderBuilder) + .addDependencies(xcodeProviderBuilder, "bundles") + .addDependencies(xcodeProviderBuilder, "deps") + .addDependencies(xcodeProviderBuilder, "non_propagated_deps") .registerActions(xcodeProviderBuilder.build()); return common.configuredTarget( diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcRuleClasses.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcRuleClasses.java index 9041d7dc14..62cef87f72 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcRuleClasses.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcRuleClasses.java @@ -350,7 +350,7 @@ public class ObjcRuleClasses { <!-- #END_BLAZE_RULE.ATTRIBUTE -->*/ .add(attr("bundles", LABEL_LIST) .direct_compile_time_input() - .allowedRuleClasses("objc_bundle", "objc_bundle_library") + .allowedRuleClasses("objc_bundle", "objc_bundle_library", "ios_extension") .allowedFileTypes()) .add(attr("$momczip_deploy", LABEL).cfg(HOST) .value(env.getLabel("//tools/objc:momczip_deploy.jar"))) diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcXcodeprojRule.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcXcodeprojRule.java index 0063c6da68..09be934efd 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcXcodeprojRule.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcXcodeprojRule.java @@ -53,6 +53,7 @@ public class ObjcXcodeprojRule implements RuleDefinition { .allowedRuleClasses( "objc_binary", "ios_extension_binary", + "ios_extension", "ios_test", "objc_bundle_library", "objc_import", diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ApplicationSupport.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ReleaseBundlingSupport.java index 67c6fcbf6d..e0dfb9c1af 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/ApplicationSupport.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ReleaseBundlingSupport.java @@ -49,15 +49,15 @@ import java.util.Set; import javax.annotation.Nullable; /** - * Support for application-generating ObjC rules. An application is generally composed of a - * top-level {@link BundleSupport bundle}, potentially signed, as well as some debug information, if - * {@link ObjcConfiguration#generateDebugSymbols() requested}. + * Support for released bundles, such as an application or extension. Such a bundle is generally + * composed of a top-level {@link BundleSupport bundle}, potentially signed, as well as some debug + * information, if {@link ObjcConfiguration#generateDebugSymbols() requested}. * * <p>Contains actions, validation logic and provider value generation. * * <p>Methods on this class can be called in any order without impacting the result. */ -public final class ApplicationSupport { +public final class ReleaseBundlingSupport { /** * Template for the containing application folder. @@ -113,17 +113,19 @@ public final class ApplicationSupport { * dependencies * @param linkedBinary whether to look for a linked binary from this rule and dependencies or just * the latter + * @param bundleDirFormat format string representing the bundle's directory with a single + * placeholder for the target name (e.g. {@code "Payload/%s.app"}) */ - ApplicationSupport( + ReleaseBundlingSupport( RuleContext ruleContext, ObjcProvider objcProvider, OptionsProvider optionsProvider, - LinkedBinary linkedBinary) { + LinkedBinary linkedBinary, String bundleDirFormat) { this.linkedBinary = linkedBinary; this.attributes = new Attributes(ruleContext); this.ruleContext = ruleContext; this.objcProvider = objcProvider; this.families = ImmutableSet.copyOf(attributes.families()); this.intermediateArtifacts = ObjcRuleClasses.intermediateArtifacts(ruleContext); - bundling = bundling(ruleContext, objcProvider, optionsProvider); + bundling = bundling(ruleContext, objcProvider, optionsProvider, bundleDirFormat); bundleSupport = new BundleSupport(ruleContext, families, bundling, extraActoolArgs()); } @@ -133,7 +135,7 @@ public final class ApplicationSupport { * * @return this application support */ - ApplicationSupport validateAttributes() { + ReleaseBundlingSupport validateAttributes() { bundleSupport.validateAttributes(); // No asset catalogs. That means you cannot specify app_icon or @@ -166,7 +168,7 @@ public final class ApplicationSupport { * * @return this application support */ - ApplicationSupport registerActions() { + ReleaseBundlingSupport registerActions() { bundleSupport.registerActions(objcProvider); registerCombineArchitecturesAction(); @@ -218,7 +220,7 @@ public final class ApplicationSupport { * * @return this application support */ - ApplicationSupport addXcodeSettings(XcodeProvider.Builder xcodeProviderBuilder) { + ReleaseBundlingSupport addXcodeSettings(XcodeProvider.Builder xcodeProviderBuilder) { bundleSupport.addXcodeSettings(xcodeProviderBuilder); xcodeProviderBuilder.addXcodeprojBuildSettings(buildSettings()); @@ -231,7 +233,7 @@ public final class ApplicationSupport { * * @return this application support */ - ApplicationSupport addFilesToBuild(NestedSetBuilder<Artifact> filesToBuild) { + ReleaseBundlingSupport addFilesToBuild(NestedSetBuilder<Artifact> filesToBuild) { NestedSetBuilder<Artifact> debugSymbolBuilder = NestedSetBuilder.<Artifact>stableOrder() .addTransitive(objcProvider.get(ObjcProvider.DEBUG_SYMBOLS)); @@ -244,7 +246,7 @@ public final class ApplicationSupport { .add(intermediateArtifacts.breakpadSym()); } - filesToBuild.add(ruleContext.getImplicitOutputArtifact(ApplicationSupport.IPA)) + filesToBuild.add(ruleContext.getImplicitOutputArtifact(ReleaseBundlingSupport.IPA)) // TODO(bazel-team): Fat binaries may require some merging of these file rather than just // making them available. .addTransitive(debugSymbolBuilder.build()); @@ -285,13 +287,14 @@ public final class ApplicationSupport { return new ExtraActoolArgs(extraArgs.build()); } - private Bundling bundling( - RuleContext ruleContext, ObjcProvider objcProvider, OptionsProvider optionsProvider) { + private static Bundling bundling( + RuleContext ruleContext, ObjcProvider objcProvider, OptionsProvider optionsProvider, + String bundleDirFormat) { ImmutableList<BundleableFile> extraBundleFiles; ObjcConfiguration objcConfiguration = ObjcRuleClasses.objcConfiguration(ruleContext); if (objcConfiguration.getPlatform() == Platform.DEVICE) { extraBundleFiles = ImmutableList.of(new BundleableFile( - attributes.provisioningProfile(), + new Attributes(ruleContext).provisioningProfile(), PROVISIONING_PROFILE_BUNDLE_FILE)); } else { extraBundleFiles = ImmutableList.of(); @@ -299,17 +302,17 @@ public final class ApplicationSupport { return new Bundling.Builder() .setName(ruleContext.getLabel().getName()) - .setBundleDirSuffix(".app") + .setBundleDirFormat(bundleDirFormat) .setExtraBundleFiles(extraBundleFiles) .setObjcProvider(objcProvider) .setInfoplistMerging( BundleSupport.infoPlistMerging(ruleContext, objcProvider, optionsProvider)) - .setIntermediateArtifacts(intermediateArtifacts) + .setIntermediateArtifacts(ObjcRuleClasses.intermediateArtifacts(ruleContext)) .build(); } private void registerCombineArchitecturesAction() { - Artifact resultingLinkedBinary = intermediateArtifacts.combinedArchitectureBinary(".app"); + Artifact resultingLinkedBinary = intermediateArtifacts.combinedArchitectureBinary(); NestedSet<Artifact> linkedBinaries = linkedBinaries(); ruleContext.registerAction(ObjcActionsBuilder.spawnOnDarwinActionBuilder() @@ -368,7 +371,7 @@ public final class ApplicationSupport { return buildSettings.build(); } - private ApplicationSupport registerSignBundleAction( + private ReleaseBundlingSupport registerSignBundleAction( Artifact entitlements, Artifact ipaOutput, Artifact ipaUnsigned) { // TODO(bazel-team): Support variable substitution ruleContext.registerAction(ObjcActionsBuilder.spawnOnDarwinActionBuilder() @@ -385,9 +388,9 @@ public final class ApplicationSupport { + codesignCommand( attributes.provisioningProfile(), entitlements, - String.format("${t}/Payload/%s.app", ruleContext.getLabel().getName())) + " && " + "${t}/" + bundling.getBundleDir()) // Using zip since we need to preserve permissions - + "cd \"${t}\" && /usr/bin/zip -q -r \"${signed_ipa}\" .") + + " && cd \"${t}\" && /usr/bin/zip -q -r \"${signed_ipa}\" .") .addInput(ipaUnsigned) .addInput(attributes.provisioningProfile()) .addInput(entitlements) @@ -440,7 +443,7 @@ public final class ApplicationSupport { .build(ruleContext)); } - private ApplicationSupport registerExtractEntitlementsAction(Artifact entitlements) { + private ReleaseBundlingSupport registerExtractEntitlementsAction(Artifact entitlements) { // See Apple Glossary (http://goo.gl/EkhXOb) // An Application Identifier is constructed as: TeamID.BundleID // TeamID is extracted from the provisioning profile. 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 b244cd9839..e492f95a71 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 @@ -345,7 +345,8 @@ public final class XcodeProvider implements TransitiveInfoProvider { } private static final EnumSet<XcodeProductType> CAN_LINK_PRODUCT_TYPES = EnumSet.of( - XcodeProductType.APPLICATION, XcodeProductType.BUNDLE, XcodeProductType.UNIT_TEST); + XcodeProductType.APPLICATION, XcodeProductType.BUNDLE, XcodeProductType.UNIT_TEST, + XcodeProductType.EXTENSION); private TargetControl targetControl() { String buildFilePath = label.getPackageFragment().getSafePathString() + "/BUILD"; diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/XcodeSupport.java b/src/main/java/com/google/devtools/build/lib/rules/objc/XcodeSupport.java index 111fef27aa..7eec1b2a01 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/XcodeSupport.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/XcodeSupport.java @@ -16,7 +16,6 @@ package com.google.devtools.build.lib.rules.objc; import static com.google.devtools.build.lib.packages.ImplicitOutputsFunction.fromTemplates; -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; @@ -89,16 +88,13 @@ public final class XcodeSupport { } /** - * Adds dependencies to the given provider builder from the {@code deps} and {@code bundles} - * attributes. + * Adds dependencies to the given provider builder from the given attribute. * * @return this xcode support */ - XcodeSupport addDependencies(XcodeProvider.Builder xcodeProviderBuilder) { - for (String attribute : ImmutableSet.of("deps", "non_propagated_deps", "bundles")) { - xcodeProviderBuilder.addDependencies( - ruleContext.getPrerequisites(attribute, Mode.TARGET, XcodeProvider.class)); - } + XcodeSupport addDependencies(XcodeProvider.Builder xcodeProviderBuilder, String attribute) { + xcodeProviderBuilder + .addDependencies(ruleContext.getPrerequisites(attribute, Mode.TARGET, XcodeProvider.class)); return this; } } |