Skip to content

Commit

Permalink
Implement ios_extension rule. See IosExtensionRule.java for informati…
Browse files Browse the repository at this point in the history
…on 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
  • Loading branch information
matvore authored and hanwen committed Feb 20, 2015
1 parent 254aee4 commit 344bcbc
Show file tree
Hide file tree
Showing 19 changed files with 284 additions and 105 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
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;
Expand Down Expand Up @@ -260,6 +261,7 @@ public static void setup(ConfiguredRuleClassProvider.Builder builder) {
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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
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;

/**
Expand All @@ -51,7 +51,7 @@ public RuleClass build(RuleClass.Builder builder, final RuleDefinitionEnvironmen
</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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@
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.
Expand All @@ -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;
}
Expand All @@ -76,25 +76,25 @@ public final ConfiguredTarget create(RuleContext ruleContext) throws Interrupted
.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();
Expand All @@ -111,8 +111,10 @@ public final ConfiguredTarget create(RuleContext ruleContext) throws Interrupted
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);
Expand All @@ -122,7 +124,7 @@ public final ConfiguredTarget create(RuleContext ruleContext) throws Interrupted
return common.configuredTarget(
filesToBuild.build(),
Optional.of(xcodeProvider),
Optional.<ObjcProvider>absent(),
Optional.of(objcProvider),
xcTestAppProvider,
Optional.<J2ObjcSrcsProvider>absent());
}
Expand All @@ -132,7 +134,7 @@ private OptionsProvider optionsProvider(RuleContext ruleContext) {
.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());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,13 @@ public BundleMergeControlBytes(

@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()
Expand All @@ -73,7 +72,7 @@ private Control control(String mergeZipPrefix, String bundleDirPrefix, Bundling
.setMinimumOsVersion(objcConfiguration.getMinimumOs())
.setSdkVersion(objcConfiguration.getIosSdkVersion())
.setPlatform(objcConfiguration.getPlatform().name())
.setBundleRoot(bundleDir);
.setBundleRoot(bundling.getBundleDir());

for (Artifact mergeZip : bundling.getMergeZips()) {
control.addMergeZip(MergeZip.newBuilder()
Expand Down Expand Up @@ -113,7 +112,7 @@ private Control control(String mergeZipPrefix, String bundleDirPrefix, Bundling
}

for (Bundling nestedBundling : bundling.getObjcProvider().get(NESTED_BUNDLE)) {
control.addNestedBundle(control(mergeZipPrefix, "", nestedBundling));
control.addNestedBundle(control(mergeZipPrefix, nestedBundling));
}

return control.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,18 @@
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;

/**
* 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;
Expand All @@ -53,8 +53,8 @@ public Builder setName(String name) {
return this;
}

public Builder setBundleDirSuffix(String bundleDirSuffix) {
this.bundleDirSuffix = bundleDirSuffix;
public Builder setBundleDirFormat(String bundleDirFormat) {
this.bundleDirFormat = bundleDirFormat;
return this;
}

Expand Down Expand Up @@ -98,7 +98,7 @@ public Bundling build() {
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()
Expand All @@ -115,13 +115,13 @@ public Bundling build() {
.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;
Expand All @@ -132,44 +132,32 @@ public Bundling build() {

private Bundling(
String name,
String bundleDirSuffix,
String bundleDirFormat,
Optional<Artifact> combinedArchitectureBinary,
ImmutableList<BundleableFile> extraBundleFiles,
ObjcProvider objcProvider,
InfoplistMerging infoplistMerging,
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);
}

/**
Expand Down Expand Up @@ -241,7 +229,7 @@ public NestedSet<Artifact> getMergeZips() {
public Map<String, String> variableSubstitutions() {
return ImmutableMap.of(
"EXECUTABLE_NAME", name,
"BUNDLE_NAME", name + bundleDirSuffix,
"BUNDLE_NAME", new PathFragment(getBundleDir()).getBaseName(),
"PRODUCT_NAME", name);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,12 +111,9 @@ public Artifact singleArchitectureBinary() {
/**
* 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");
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -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();
}
}
Loading

0 comments on commit 344bcbc

Please sign in to comment.