Skip to content

Commit

Permalink
Ensure extension's bundle minimum OS version is at least 8.0.
Browse files Browse the repository at this point in the history
This is required because Apple does not accept extensions whose plist
has a minimum OS value lower than 8.0, or whose code is compiled at less
than 8.0, even if it is included in an application that is compiled for,
and marked compatible with, a lower OS version.

Note that I didn't make this an attribute for now as there are no use
cases for setting the value to anything but 8.0. If we ever need to
make this user-configurable, a value can easily be set.

Second submission attempt with small additional logic to distinguish xcode
projects across configurations.

--
MOS_MIGRATED_REVID=92267493
  • Loading branch information
aragos authored and hanwen committed Apr 28, 2015
1 parent d6e5480 commit 1f0f444
Show file tree
Hide file tree
Showing 20 changed files with 357 additions and 154 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -90,18 +90,19 @@ public final ConfiguredTarget create(RuleContext ruleContext) throws Interrupted
Optional<RunfilesSupport> maybeRunfilesSupport = Optional.absent();
switch (hasReleaseBundlingSupport) {
case YES:
ObjcConfiguration objcConfiguration = ObjcRuleClasses.objcConfiguration(ruleContext);
// TODO(bazel-team): Remove once all bundle users are migrated to ios_application.
ReleaseBundlingSupport releaseBundlingSupport = new ReleaseBundlingSupport(
ruleContext, objcProvider, optionsProvider,
LinkedBinary.LOCAL_AND_DEPENDENCIES, ReleaseBundlingSupport.APP_BUNDLE_DIR_FORMAT);
LinkedBinary.LOCAL_AND_DEPENDENCIES, ReleaseBundlingSupport.APP_BUNDLE_DIR_FORMAT,
objcConfiguration.getMinimumOs());
releaseBundlingSupport
.registerActions()
.addXcodeSettings(xcodeProviderBuilder)
.addFilesToBuild(filesToBuild)
.validateResources()
.validateAttributes();

ObjcConfiguration objcConfiguration = ObjcRuleClasses.objcConfiguration(ruleContext);
xcTestAppProvider = Optional.of(releaseBundlingSupport.xcTestAppProvider());
if (objcConfiguration.getBundlingPlatform() == Platform.SIMULATOR) {
Artifact runnerScript = intermediateArtifacts.runnerScript();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ private Control control(String mergeZipPrefix, Bundling bundling) {
.addAllSourcePlistFile(Artifact.toExecPaths(
bundling.getInfoplistMerging().getPlistWithEverything().asSet()))
// TODO(bazel-team): Add rule attribute for specifying targeted device family
.setMinimumOsVersion(objcConfiguration.getMinimumOs())
.setMinimumOsVersion(bundling.getMinimumOsVersion())
.setSdkVersion(objcConfiguration.getIosSdkVersion())
.setPlatform(objcConfiguration.getBundlingPlatform().name())
.setBundleRoot(bundling.getBundleDir());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@

import com.google.common.base.Optional;
import com.google.common.base.Verify;
import com.google.common.collect.ImmutableList;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.analysis.FilesToRunProvider;
import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode;
Expand Down Expand Up @@ -192,8 +191,6 @@ BundleSupport validateResources(ObjcProvider objcProvider) {
private void registerInterfaceBuilderActions(ObjcProvider objcProvider) {
IntermediateArtifacts intermediateArtifacts =
ObjcRuleClasses.intermediateArtifacts(ruleContext);
ObjcConfiguration objcConfiguration = ObjcRuleClasses.objcConfiguration(ruleContext);
String minimumOs = objcConfiguration.getMinimumOs();
for (Artifact storyboardInput : objcProvider.get(ObjcProvider.STORYBOARD)) {
String archiveRoot = BundleableFile.flatBundlePath(storyboardInput.getExecPath()) + "c";
Artifact zipOutput = intermediateArtifacts.compiledStoryboardZip(storyboardInput);
Expand All @@ -208,7 +205,7 @@ private void registerInterfaceBuilderActions(ObjcProvider objcProvider) {
.add(archiveRoot)
.addPath(ObjcActionsBuilder.IBTOOL)

.add("--minimum-deployment-target").add(minimumOs)
.add("--minimum-deployment-target").add(bundling.getMinimumOsVersion())
.addPath(storyboardInput.getExecPath())
.build())
.addOutput(zipOutput)
Expand All @@ -234,25 +231,21 @@ private void registerMomczipActions(ObjcProvider objcProvider) {
.addPath(outputZip.getExecPath())
.add(datamodel.archiveRootForMomczip())
.add(IosSdkCommands.MOMC_PATH)
.add(commonMomczipArguments(objcConfiguration))

.add("-XD_MOMC_SDKROOT=" + IosSdkCommands.sdkDir(objcConfiguration))
.add("-XD_MOMC_IOS_TARGET_VERSION=" + bundling.getMinimumOsVersion())
.add("-MOMC_PLATFORMS")
.add(objcConfiguration.getBundlingPlatform().getLowerCaseNameInPlist())
.add("-XD_MOMC_TARGET_VERSION=10.6")
.add(datamodel.getContainer().getSafePathString())
.build())
.build(ruleContext));
}
}

static Iterable<String> commonMomczipArguments(ObjcConfiguration configuration) {
return ImmutableList.of(
"-XD_MOMC_SDKROOT=" + IosSdkCommands.sdkDir(configuration),
"-XD_MOMC_IOS_TARGET_VERSION=" + configuration.getMinimumOs(),
"-MOMC_PLATFORMS", configuration.getBundlingPlatform().getLowerCaseNameInPlist(),
"-XD_MOMC_TARGET_VERSION=10.6");
}

private void registerConvertXibsActions(ObjcProvider objcProvider) {
IntermediateArtifacts intermediateArtifacts =
ObjcRuleClasses.intermediateArtifacts(ruleContext);
ObjcConfiguration objcConfiguration = ObjcRuleClasses.objcConfiguration(ruleContext);
for (Artifact original : objcProvider.get(ObjcProvider.XIB)) {
Artifact zipOutput = intermediateArtifacts.compiledXibFileZip(original);
String archiveRoot = BundleableFile.flatBundlePath(
Expand All @@ -267,7 +260,7 @@ private void registerConvertXibsActions(ObjcProvider objcProvider) {
.add(archiveRoot)
.addPath(ObjcActionsBuilder.IBTOOL)

.add("--minimum-deployment-target").add(objcConfiguration.getMinimumOs())
.add("--minimum-deployment-target").add(bundling.getMinimumOsVersion())
.addPath(original.getExecPath())
.build())
.addOutput(zipOutput)
Expand All @@ -294,15 +287,6 @@ private void registerConvertStringsActions(ObjcProvider objcProvider) {
}
}

/**
* Validates any rule attributes and dependencies related to this bundle.
*
* @return this bundle support
*/
BundleSupport validateAttributes() {
return this;
}

private void registerMergeInfoplistAction() {
// TODO(bazel-team): Move action implementation from InfoplistMerging to this class.
ruleContext.registerAction(bundling.getInfoplistMerging().getMergeAction());
Expand Down Expand Up @@ -348,7 +332,7 @@ private CommandLine actoolzipCommandLine(

.add("--platform").add(objcConfiguration.getBundlingPlatform().getLowerCaseNameInPlist())
.addExecPath("--output-partial-info-plist", partialInfoPlist)
.add("--minimum-deployment-target").add(objcConfiguration.getMinimumOs());
.add("--minimum-deployment-target").add(bundling.getMinimumOsVersion());

for (TargetDeviceFamily targetDeviceFamily : targetDeviceFamilies) {
commandLine.add("--target-device").add(targetDeviceFamily.name().toLowerCase(Locale.US));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.devtools.build.lib.actions.Artifact;
Expand Down Expand Up @@ -57,6 +56,7 @@ static final class Builder {
private String primaryBundleId;
private String fallbackBundleId;
private String architecture;
private String minimumOsVersion;

public Builder setName(String name) {
this.name = name;
Expand Down Expand Up @@ -107,6 +107,15 @@ public Builder setFallbackBundleId(String fallbackId) {
return this;
}

/**
* Sets the minimum OS version for this bundle which will be used when constructing the bundle's
* plist.
*/
public Builder setMinimumOsVersion(String minimumOsVersion) {
this.minimumOsVersion = minimumOsVersion;
return this;
}

private static NestedSet<Artifact> nestedBundleContentArtifacts(Iterable<Bundling> bundles) {
NestedSetBuilder<Artifact> artifacts = NestedSetBuilder.stableOrder();
for (Bundling bundle : bundles) {
Expand Down Expand Up @@ -175,7 +184,7 @@ public Bundling build() {

return new Bundling(name, bundleDirFormat, combinedArchitectureBinary, extraBundleFiles,
objcProvider, infoplistMerging, actoolzipOutput, bundleContentArtifactsBuilder.build(),
mergeZips, primaryBundleId, fallbackBundleId, architecture);
mergeZips, primaryBundleId, fallbackBundleId, architecture, minimumOsVersion);
}
}

Expand All @@ -191,6 +200,7 @@ public Bundling build() {
private final NestedSet<Artifact> mergeZips;
private final String primaryBundleId;
private final String fallbackBundleId;
private final String minimumOsVersion;

private Bundling(
String name,
Expand All @@ -204,7 +214,8 @@ private Bundling(
NestedSet<Artifact> mergeZips,
String primaryBundleId,
String fallbackBundleId,
String architecture) {
String architecture,
String minimumOsVersion) {
this.name = Preconditions.checkNotNull(name);
this.bundleDirFormat = Preconditions.checkNotNull(bundleDirFormat);
this.combinedArchitectureBinary = Preconditions.checkNotNull(combinedArchitectureBinary);
Expand All @@ -217,6 +228,7 @@ private Bundling(
this.fallbackBundleId = fallbackBundleId;
this.primaryBundleId = primaryBundleId;
this.architecture = Preconditions.checkNotNull(architecture);
this.minimumOsVersion = Preconditions.checkNotNull(minimumOsVersion);
}

/**
Expand Down Expand Up @@ -328,4 +340,12 @@ public String getFallbackBundleId() {
public String getArchitecture() {
return architecture;
}

/**
* Returns the minimum iOS version this bundle's plist and resources should be generated for
* (does <b>not</b> affect the minimum OS version its binary is compiled with).
*/
public String getMinimumOsVersion() {
return minimumOsVersion;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ final class IntermediateArtifacts {
private final String archiveFileNameSuffix;

/**
* Label to scope the output paths of generated artifacts, in addition to {@link ownerLabel}.
* Label to scope the output paths of generated artifacts, in addition to {@link #ownerLabel}.
*/
private final Optional<Label> scopingLabel;

Expand Down Expand Up @@ -231,9 +231,8 @@ public Artifact convertedStringsFile(Artifact originalFile) {
* file.
*/
public Artifact compiledXibFileZip(Artifact originalFile) {
return analysisEnvironment.getDerivedArtifact(
FileSystemUtils.replaceExtension(originalFile.getExecPath(), ".nib.zip"),
binDirectory);
return appendExtension(
"/" + FileSystemUtils.replaceExtension(originalFile.getExecPath(), ".nib.zip"));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,32 @@
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.analysis.config.BuildOptions;
import com.google.devtools.build.lib.packages.Attribute.SplitTransition;
import com.google.devtools.build.lib.rules.objc.ReleaseBundlingSupport.SplitArchTransition;
import com.google.devtools.build.lib.rules.objc.ReleaseBundlingSupport.SplitArchTransition.ConfigurationDistinguisher;

/**
* Implementation for {@code ios_application}.
*/
public class IosApplication extends ReleaseBundlingTargetFactory {

/**
* Transition that when applied to a target generates a configured target for each value in
* {@code --ios_multi_cpus}, such that {@code --ios_cpu} is set to a different one of those values
* in the configured targets.
*/
public static final SplitTransition<BuildOptions> SPLIT_ARCH_TRANSITION =
new SplitArchTransition();

private static final ImmutableSet<Attribute> DEPENDENCY_ATTRIBUTES =
ImmutableSet.of(
new Attribute("binary", Mode.SPLIT),
new Attribute("extensions", Mode.TARGET));

public IosApplication() {
super(ReleaseBundlingSupport.APP_BUNDLE_DIR_FORMAT, XcodeProductType.APPLICATION,
ExposeAsNestedBundle.NO, DEPENDENCY_ATTRIBUTES);
ExposeAsNestedBundle.NO, DEPENDENCY_ATTRIBUTES, ConfigurationDistinguisher.APPLICATION);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public RuleClass build(Builder builder, RuleDefinitionEnvironment env) {
.allowedFileTypes()
.mandatory()
.direct_compile_time_input()
.cfg(ReleaseBundlingSupport.SPLIT_ARCH_TRANSITION))
.cfg(IosApplication.SPLIT_ARCH_TRANSITION))
/* <!-- #BLAZE_RULE(ios_application).ATTRIBUTE(extensions) -->
Any extensions to include in the final application.
${SYNOPSIS}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,101 @@

package com.google.devtools.build.lib.rules.objc;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode;
import com.google.devtools.build.lib.analysis.RuleContext;
import com.google.devtools.build.lib.analysis.config.BuildOptions;
import com.google.devtools.build.lib.packages.Attribute.SplitTransition;
import com.google.devtools.build.lib.rules.objc.ReleaseBundlingSupport.SplitArchTransition;
import com.google.devtools.build.lib.rules.objc.ReleaseBundlingSupport.SplitArchTransition.ConfigurationDistinguisher;

import java.io.Serializable;

/**
* Implementation for {@code ios_extension}.
*/
public class IosExtension extends ReleaseBundlingTargetFactory {

/**
* Transition that when applied to a target generates a configured target for each value in
* {@code --ios_multi_cpus}, such that {@code --ios_cpu} is set to a different one of those values
* in the configured targets.
*
* <p>Also ensures that, no matter whether {@code --ios_multi_cpus} is set, {@code
* --ios_minimum_os} is at least {@code 8.0} as Apple requires this for extensions.
*/
static final SplitTransition<BuildOptions> MINIMUM_OS_AND_SPLIT_ARCH_TRANSITION =
new ExtensionSplitArchTransition();

// Apple only accepts extensions starting at 8.0.
@VisibleForTesting
static final String EXTENSION_MINIMUM_OS_VERSION = "8.0";

public IosExtension() {
super(ReleaseBundlingSupport.EXTENSION_BUNDLE_DIR_FORMAT, XcodeProductType.EXTENSION,
ExposeAsNestedBundle.YES, ImmutableSet.of(new Attribute("binary", Mode.SPLIT)));
ExposeAsNestedBundle.YES, ImmutableSet.of(new Attribute("binary", Mode.SPLIT)),
ConfigurationDistinguisher.EXTENSION);
}

protected OptionsProvider optionsProvider(RuleContext ruleContext) {
return new OptionsProvider.Builder()
.addInfoplists(ruleContext.getPrerequisiteArtifacts("infoplist", Mode.TARGET).list())
.build();
}

@Override
protected String bundleMinimumOsVersion(RuleContext ruleContext) {
return determineMinimumOsVersion(ObjcRuleClasses.objcConfiguration(ruleContext).getMinimumOs());
}

private static String determineMinimumOsVersion(String fromFlag) {
if (Double.parseDouble(fromFlag) < Double.parseDouble(EXTENSION_MINIMUM_OS_VERSION)) {
// Extensions are not accepted by Apple below version 8.0. While applications built with a
// minimum iOS version of less than 8.0 may contain extensions in their bundle, the extension
// itself needs to be built with 8.0 or higher. This logic overrides (if necessary) any
// flag-set minimum iOS version for extensions only so that this requirement is not violated.
return EXTENSION_MINIMUM_OS_VERSION;
}
return fromFlag;
}

/**
* Split transition that configures the minimum iOS version in addition to architecture splitting.
*/
private static class ExtensionSplitArchTransition extends SplitArchTransition
implements Serializable {

@Override
protected ImmutableList<BuildOptions> defaultOptions(BuildOptions originalOptions) {
ObjcCommandLineOptions objcOptions = originalOptions.get(ObjcCommandLineOptions.class);
String newMinimumVersion = determineMinimumOsVersion(objcOptions.iosMinimumOs);

if (newMinimumVersion.equals(objcOptions.iosMinimumOs)) {
return ImmutableList.of();
}

BuildOptions splitOptions = originalOptions.clone();
setMinimumOsVersion(splitOptions, newMinimumVersion);
splitOptions.get(ObjcCommandLineOptions.class).configurationDistinguisher =
getConfigurationDistinguisher();
return ImmutableList.of(splitOptions);
}

@Override
protected void setAdditionalOptions(BuildOptions splitOptions, BuildOptions originalOptions) {
String fromFlag = originalOptions.get(ObjcCommandLineOptions.class).iosMinimumOs;
setMinimumOsVersion(splitOptions, determineMinimumOsVersion(fromFlag));
}

@Override
protected ConfigurationDistinguisher getConfigurationDistinguisher() {
return ConfigurationDistinguisher.EXTENSION;
}

private void setMinimumOsVersion(BuildOptions splitOptions, String newMinimumVersion) {
splitOptions.get(ObjcCommandLineOptions.class).iosMinimumOs = newMinimumVersion;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public RuleClass build(Builder builder, RuleDefinitionEnvironment env) {
.allowedFileTypes()
.mandatory()
.direct_compile_time_input()
.cfg(ReleaseBundlingSupport.SPLIT_ARCH_TRANSITION))
.cfg(IosExtension.MINIMUM_OS_AND_SPLIT_ARCH_TRANSITION))
.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,10 @@ public final ConfiguredTarget create(RuleContext ruleContext) throws Interrupted
.addXcodeSettings(xcodeProviderBuilder, common, optionsProvider)
.validateAttributes();

ObjcConfiguration objcConfiguration = ObjcRuleClasses.objcConfiguration(ruleContext);
ReleaseBundlingSupport releaseBundlingSupport = new ReleaseBundlingSupport(
ruleContext, common.getObjcProvider(), optionsProvider, LinkedBinary.LOCAL_AND_DEPENDENCIES,
ReleaseBundlingSupport.APP_BUNDLE_DIR_FORMAT);
ReleaseBundlingSupport.APP_BUNDLE_DIR_FORMAT, objcConfiguration.getMinimumOs());
releaseBundlingSupport
.registerActions()
.addXcodeSettings(xcodeProviderBuilder)
Expand Down
Loading

0 comments on commit 1f0f444

Please sign in to comment.