diff options
Diffstat (limited to 'src/main/java/com')
20 files changed, 312 insertions, 149 deletions
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 0566525aac..7475baf6f2 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 @@ -90,10 +90,12 @@ abstract class BinaryLinkingTargetFactory implements RuleConfiguredTargetFactory 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) @@ -101,7 +103,6 @@ abstract class BinaryLinkingTargetFactory implements RuleConfiguredTargetFactory .validateResources() .validateAttributes(); - ObjcConfiguration objcConfiguration = ObjcRuleClasses.objcConfiguration(ruleContext); xcTestAppProvider = Optional.of(releaseBundlingSupport.xcTestAppProvider()); if (objcConfiguration.getBundlingPlatform() == Platform.SIMULATOR) { Artifact runnerScript = intermediateArtifacts.runnerScript(); 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 d0be20c12e..a90e23b8e6 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 @@ -67,7 +67,7 @@ final class BundleMergeControlBytes extends ByteSource { .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()); diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/BundleSupport.java b/src/main/java/com/google/devtools/build/lib/rules/objc/BundleSupport.java index 8d51f5752d..4b586c1e6a 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/BundleSupport.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/BundleSupport.java @@ -20,7 +20,6 @@ import static com.google.devtools.build.lib.rules.objc.ObjcProvider.XCASSETS_DIR 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; @@ -192,8 +191,6 @@ final class BundleSupport { 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); @@ -208,7 +205,7 @@ final class BundleSupport { .add(archiveRoot) .addPath(ObjcActionsBuilder.IBTOOL) - .add("--minimum-deployment-target").add(minimumOs) + .add("--minimum-deployment-target").add(bundling.getMinimumOsVersion()) .addPath(storyboardInput.getExecPath()) .build()) .addOutput(zipOutput) @@ -234,25 +231,21 @@ final class BundleSupport { .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( @@ -267,7 +260,7 @@ final class BundleSupport { .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) @@ -294,15 +287,6 @@ final class BundleSupport { } } - /** - * 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()); @@ -348,7 +332,7 @@ final class BundleSupport { .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)); 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 8b5edfdabb..1ddc16a749 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 @@ -28,7 +28,6 @@ import static com.google.devtools.build.lib.rules.objc.ObjcProvider.XIB; 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; @@ -57,6 +56,7 @@ final class Bundling { private String primaryBundleId; private String fallbackBundleId; private String architecture; + private String minimumOsVersion; public Builder setName(String name) { this.name = name; @@ -107,6 +107,15 @@ final class Bundling { 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) { @@ -175,7 +184,7 @@ final class Bundling { return new Bundling(name, bundleDirFormat, combinedArchitectureBinary, extraBundleFiles, objcProvider, infoplistMerging, actoolzipOutput, bundleContentArtifactsBuilder.build(), - mergeZips, primaryBundleId, fallbackBundleId, architecture); + mergeZips, primaryBundleId, fallbackBundleId, architecture, minimumOsVersion); } } @@ -191,6 +200,7 @@ final class Bundling { private final NestedSet<Artifact> mergeZips; private final String primaryBundleId; private final String fallbackBundleId; + private final String minimumOsVersion; private Bundling( String name, @@ -204,7 +214,8 @@ final class 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); @@ -217,6 +228,7 @@ final class Bundling { this.fallbackBundleId = fallbackBundleId; this.primaryBundleId = primaryBundleId; this.architecture = Preconditions.checkNotNull(architecture); + this.minimumOsVersion = Preconditions.checkNotNull(minimumOsVersion); } /** @@ -328,4 +340,12 @@ final class Bundling { 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; + } } 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 d852bb49cd..ee1a3edf88 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 @@ -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; @@ -231,9 +231,8 @@ final class IntermediateArtifacts { * file. */ public Artifact compiledXibFileZip(Artifact originalFile) { - return analysisEnvironment.getDerivedArtifact( - FileSystemUtils.replaceExtension(originalFile.getExecPath(), ".nib.zip"), - binDirectory); + return appendExtension( + "/" + FileSystemUtils.replaceExtension(originalFile.getExecPath(), ".nib.zip")); } /** 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 4b8c7603aa..d76d1c0af5 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 @@ -20,12 +20,23 @@ import com.google.devtools.build.lib.actions.Artifact; 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; /** * 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), 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 898150baa9..37a08c4530 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 @@ -54,7 +54,7 @@ public class IosApplicationRule implements RuleDefinition { .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} 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 index fa2e8fbef6..c1ceab997c 100644 --- 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 @@ -14,18 +14,41 @@ 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 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)) + ); } protected OptionsProvider optionsProvider(RuleContext ruleContext) { @@ -33,4 +56,58 @@ public class IosExtension extends ReleaseBundlingTargetFactory { .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; + } + } } 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 index eaf0f0a8fd..76d6290561 100644 --- 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 @@ -50,7 +50,7 @@ public class IosExtensionRule implements RuleDefinition { .allowedFileTypes() .mandatory() .direct_compile_time_input() - .cfg(ReleaseBundlingSupport.SPLIT_ARCH_TRANSITION)) + .cfg(IosExtension.MINIMUM_OS_AND_SPLIT_ARCH_TRANSITION)) .build(); } 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 17e8395789..a76af46b61 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 @@ -122,9 +122,10 @@ public abstract class IosTest implements RuleConfiguredTargetFactory { .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) 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 7e4c48c15a..005eb67a36 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 @@ -82,15 +82,17 @@ public class ObjcBundleLibrary implements RuleConfiguredTargetFactory { RuleContext ruleContext, ObjcCommon common, OptionsProvider optionsProvider) { IntermediateArtifacts intermediateArtifacts = ObjcRuleClasses.intermediateArtifacts(ruleContext); + ObjcConfiguration objcConfiguration = ObjcRuleClasses.objcConfiguration(ruleContext); return new Bundling.Builder() .setName(ruleContext.getLabel().getName()) - .setArchitecture(ObjcRuleClasses.objcConfiguration(ruleContext).getIosCpu()) + .setArchitecture(objcConfiguration.getIosCpu()) .setBundleDirFormat("%s.bundle") .setObjcProvider(common.getObjcProvider()) .setInfoplistMerging( BundleSupport.infoPlistMerging(ruleContext, common.getObjcProvider(), optionsProvider, new BundleSupport.ExtraMergePlists())) .setIntermediateArtifacts(intermediateArtifacts) + .setMinimumOsVersion(objcConfiguration.getMinimumOs()) .build(); } diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcCommandLineOptions.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcCommandLineOptions.java index b835e7a416..9d9d1735bb 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcCommandLineOptions.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcCommandLineOptions.java @@ -21,8 +21,10 @@ import com.google.devtools.build.lib.analysis.config.BuildConfiguration.LabelCon import com.google.devtools.build.lib.analysis.config.BuildOptions; import com.google.devtools.build.lib.analysis.config.FragmentOptions; import com.google.devtools.build.lib.packages.Attribute.SplitTransition; +import com.google.devtools.build.lib.rules.objc.ReleaseBundlingSupport.SplitArchTransition.ConfigurationDistinguisher; import com.google.devtools.build.lib.syntax.Label; import com.google.devtools.common.options.Converters.CommaSeparatedOptionListConverter; +import com.google.devtools.common.options.EnumConverter; import com.google.devtools.common.options.Option; import java.util.List; @@ -127,6 +129,19 @@ public class ObjcCommandLineOptions extends FragmentOptions { help = "Whether to add include path entries for every individual proto file.") public boolean perProtoIncludes; + // This option exists because two configurations are not allowed to have the same cache key + // (partially derived from options). Since we have multiple transitions (see + // getPotentialSplitTransitions below) that may result in the same configuration values at runtime + // we need an artificial way to distinguish between them. This option must only be set by those + // transitions for this purpose. + // TODO(bazel-team): Remove this once we have dynamic configurations but make sure that different + // configurations (e.g. by min os version) always use different output paths. + @Option(name = "DO_NOT_USE_configuration_distinguisher", + defaultValue = "UNKNOWN", + converter = ConfigurationDistinguisherConverter.class, + category = "undocumented") + public ConfigurationDistinguisher configurationDistinguisher; + @VisibleForTesting static final String DEFAULT_MINIMUM_IOS = "7.0"; @VisibleForTesting static final String DEFAULT_IOS_CPU = "i386"; @@ -152,6 +167,14 @@ public class ObjcCommandLineOptions extends FragmentOptions { @Override public List<SplitTransition<BuildOptions>> getPotentialSplitTransitions() { - return ImmutableList.of(ReleaseBundlingSupport.SPLIT_ARCH_TRANSITION); + return ImmutableList.of( + IosApplication.SPLIT_ARCH_TRANSITION, IosExtension.MINIMUM_OS_AND_SPLIT_ARCH_TRANSITION); + } + + public static final class ConfigurationDistinguisherConverter + extends EnumConverter<ConfigurationDistinguisher> { + public ConfigurationDistinguisherConverter() { + super(ConfigurationDistinguisher.class, "configuration distinguisher"); + } } } diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcConfiguration.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcConfiguration.java index 6d4310d5cd..870292cb8c 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcConfiguration.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcConfiguration.java @@ -15,6 +15,7 @@ package com.google.devtools.build.lib.rules.objc; import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Joiner; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.devtools.build.lib.analysis.config.BuildConfiguration; @@ -22,9 +23,12 @@ import com.google.devtools.build.lib.analysis.config.BuildOptions; import com.google.devtools.build.lib.analysis.config.CompilationMode; import com.google.devtools.build.lib.events.Event; import com.google.devtools.build.lib.events.EventHandler; +import com.google.devtools.build.lib.rules.objc.ReleaseBundlingSupport.SplitArchTransition.ConfigurationDistinguisher; import com.google.devtools.build.lib.syntax.Label; +import java.util.ArrayList; import java.util.List; +import java.util.Locale; import javax.annotation.Nullable; @@ -58,6 +62,7 @@ public class ObjcConfiguration extends BuildConfiguration.Fragment { private final List<String> iosMultiCpus; private final String iosSplitCpu; private final boolean perProtoIncludes; + private final ConfigurationDistinguisher configurationDistinguisher; // We only load these labels if the mode which uses them is enabled. That is know as part of the // BuildConfiguration. This label needs to be part of a configuration because only configurations @@ -88,6 +93,7 @@ public class ObjcConfiguration extends BuildConfiguration.Fragment { this.iosMultiCpus = Preconditions.checkNotNull(objcOptions.iosMultiCpus, "iosMultiCpus"); this.iosSplitCpu = Preconditions.checkNotNull(objcOptions.iosSplitCpu, "iosSplitCpu"); this.perProtoIncludes = objcOptions.perProtoIncludes; + this.configurationDistinguisher = objcOptions.configurationDistinguisher; } public String getIosSdkVersion() { @@ -243,7 +249,18 @@ public class ObjcConfiguration extends BuildConfiguration.Fragment { @Nullable @Override public String getOutputDirectoryName() { - return !iosSplitCpu.isEmpty() ? "ios-" + iosSplitCpu : null; + List<String> components = new ArrayList<>(); + if (!iosSplitCpu.isEmpty()) { + components.add("ios-" + iosSplitCpu); + } + if (configurationDistinguisher != ConfigurationDistinguisher.UNKNOWN) { + components.add(configurationDistinguisher.toString().toLowerCase(Locale.US)); + } + + if (components.isEmpty()) { + return null; + } + return Joiner.on('-').join(components); } @Override diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProtoLibrary.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProtoLibrary.java index 90483b4095..63587e5325 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProtoLibrary.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProtoLibrary.java @@ -180,7 +180,7 @@ public class ObjcProtoLibrary implements RuleConfiguredTargetFactory { .setLabel(ruleContext.getLabel()) .setArchitecture(configuration.getIosCpu()) .addUserHeaderSearchPaths(searchPathEntries) - .addPropagatedDependencies(protoDeps, configuration) + .addPropagatedDependencies(protoDeps) .addCopts(configuration.getCopts()) .setProductType(LIBRARY_STATIC) .addHeaders(protoGeneratedHeaders) diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ReleaseBundlingSupport.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ReleaseBundlingSupport.java index 56f65d3bed..762bd84d3c 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/ReleaseBundlingSupport.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ReleaseBundlingSupport.java @@ -70,36 +70,6 @@ public final class ReleaseBundlingSupport { */ public static final SafeImplicitOutputsFunction IPA = fromTemplates("%{name}.ipa"); - /** - * 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 SplitTransition<BuildOptions>() { - @Override - public List<BuildOptions> split(BuildOptions buildOptions) { - List<String> iosMultiCpus = buildOptions.get(ObjcCommandLineOptions.class).iosMultiCpus; - if (iosMultiCpus.isEmpty()) { - return ImmutableList.of(); - } - - ImmutableList.Builder<BuildOptions> splitBuildOptions = ImmutableList.builder(); - for (String iosCpu : iosMultiCpus) { - BuildOptions splitOptions = buildOptions.clone(); - splitOptions.get(ObjcCommandLineOptions.class).iosSplitCpu = iosCpu; - splitOptions.get(ObjcCommandLineOptions.class).iosCpu = iosCpu; - splitBuildOptions.add(splitOptions); - } - return splitBuildOptions.build(); - } - - @Override - public boolean defaultsToSelf() { - return true; - } - }; - @VisibleForTesting static final String NO_ASSET_CATALOG_ERROR_FORMAT = "a value was specified (%s), but this app does not have any asset catalogs"; @@ -154,18 +124,22 @@ public final class ReleaseBundlingSupport { * @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"}) + * placeholder for the target name (e.g. {@code "Payload/%s.app"}) + * @param bundleMinimumOsVersion the minimum OS version this bundle's plist should be generated + * for (<b>not</b> the minimum OS version its binary is compiled with, that needs to be set + * through the configuration) */ ReleaseBundlingSupport( RuleContext ruleContext, ObjcProvider objcProvider, OptionsProvider optionsProvider, - LinkedBinary linkedBinary, String bundleDirFormat) { + LinkedBinary linkedBinary, String bundleDirFormat, String bundleMinimumOsVersion) { 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, bundleDirFormat); + bundling = bundling( + ruleContext, objcProvider, optionsProvider, bundleDirFormat, bundleMinimumOsVersion); bundleSupport = new BundleSupport(ruleContext, families, bundling, extraActoolArgs()); } @@ -414,7 +388,7 @@ public final class ReleaseBundlingSupport { private Bundling bundling( RuleContext ruleContext, ObjcProvider objcProvider, OptionsProvider optionsProvider, - String bundleDirFormat) { + String bundleDirFormat, String minimumOsVersion) { ImmutableList<BundleableFile> extraBundleFiles; ObjcConfiguration objcConfiguration = ObjcRuleClasses.objcConfiguration(ruleContext); if (objcConfiguration.getBundlingPlatform() == Platform.DEVICE) { @@ -447,6 +421,7 @@ public final class ReleaseBundlingSupport { .setIntermediateArtifacts(ObjcRuleClasses.intermediateArtifacts(ruleContext)) .setPrimaryBundleId(primaryBundleId) .setFallbackBundleId(fallbackBundleId) + .setMinimumOsVersion(minimumOsVersion) .build(); } @@ -752,4 +727,74 @@ public final class ReleaseBundlingSupport { return value.isEmpty() ? null : value; } } + + /** + * Transition that results in one configured target per architecture set in {@code + * --ios_multi_cpus}. + */ + protected static class SplitArchTransition implements SplitTransition<BuildOptions> { + + @Override + public final List<BuildOptions> split(BuildOptions buildOptions) { + List<String> iosMultiCpus = buildOptions.get(ObjcCommandLineOptions.class).iosMultiCpus; + if (iosMultiCpus.isEmpty()) { + return defaultOptions(buildOptions); + } + + ImmutableList.Builder<BuildOptions> splitBuildOptions = ImmutableList.builder(); + for (String iosCpu : iosMultiCpus) { + BuildOptions splitOptions = buildOptions.clone(); + setArchitectureOptions(splitOptions, iosCpu); + setAdditionalOptions(splitOptions, buildOptions); + splitOptions.get(ObjcCommandLineOptions.class).configurationDistinguisher = + getConfigurationDistinguisher(); + splitBuildOptions.add(splitOptions); + } + return splitBuildOptions.build(); + } + + /** + * Returns the default options to use if no split architectures are specified. + * + * @param originalOptions original options before this transition + */ + protected ImmutableList<BuildOptions> defaultOptions(BuildOptions originalOptions) { + return ImmutableList.of(); + } + + /** + * Sets or overwrites flags on the given split options. + * + * <p>Invoked once for each configuration produced by this transition. + * + * @param splitOptions options to use after this transition + * @param originalOptions original options before this transition + */ + protected void setAdditionalOptions(BuildOptions splitOptions, BuildOptions originalOptions) {} + + private void setArchitectureOptions(BuildOptions splitOptions, String iosCpu) { + splitOptions.get(ObjcCommandLineOptions.class).iosSplitCpu = iosCpu; + splitOptions.get(ObjcCommandLineOptions.class).iosCpu = iosCpu; + } + + @Override + public boolean defaultsToSelf() { + return true; + } + + /** + * Returns the configuration distinguisher for this transition instance. + */ + protected ConfigurationDistinguisher getConfigurationDistinguisher() { + return ConfigurationDistinguisher.APPLICATION; + } + + /** + * Value used to avoid multiple configurations from conflicting. No two instances of this + * transition may exist with the same value in a single Bazel invocation. + */ + enum ConfigurationDistinguisher { + EXTENSION, APPLICATION, UNKNOWN + } + } } diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ReleaseBundlingTargetFactory.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ReleaseBundlingTargetFactory.java index af98af5dce..8157bd73ca 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/ReleaseBundlingTargetFactory.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ReleaseBundlingTargetFactory.java @@ -67,7 +67,7 @@ public abstract class ReleaseBundlingTargetFactory implements RuleConfiguredTarg ReleaseBundlingSupport releaseBundlingSupport = new ReleaseBundlingSupport( ruleContext, common.getObjcProvider(), optionsProvider(ruleContext), - LinkedBinary.DEPENDENCIES_ONLY, bundleDirFormat); + LinkedBinary.DEPENDENCIES_ONLY, bundleDirFormat, bundleMinimumOsVersion(ruleContext)); releaseBundlingSupport .registerActions() .addXcodeSettings(xcodeProviderBuilder) @@ -77,7 +77,8 @@ public abstract class ReleaseBundlingTargetFactory implements RuleConfiguredTarg XcodeSupport xcodeSupport = new XcodeSupport(ruleContext) .addFilesToBuild(filesToBuild) - .addXcodeSettings(xcodeProviderBuilder, common.getObjcProvider(), xcodeProductType) + .addXcodeSettings(xcodeProviderBuilder, common.getObjcProvider(), xcodeProductType, + ObjcRuleClasses.objcConfiguration(ruleContext).getDependencySingleArchitecture()) .addDummySource(xcodeProviderBuilder); for (Attribute attribute : dependencyAttributes) { @@ -106,6 +107,15 @@ public abstract class ReleaseBundlingTargetFactory implements RuleConfiguredTarg } /** + * Returns the minimum OS version this bundle's plist and resources should be generated for + * (<b>not</b> the minimum OS version its binary is compiled with, that needs to be set in the + * configuration). + */ + protected String bundleMinimumOsVersion(RuleContext ruleContext) { + return ObjcRuleClasses.objcConfiguration(ruleContext).getMinimumOs(); + } + + /** * Returns a provider based on this rule's options and those of its option-providing dependencies. */ protected abstract OptionsProvider optionsProvider(RuleContext ruleContext); diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/Xcdatamodel.java b/src/main/java/com/google/devtools/build/lib/rules/objc/Xcdatamodel.java index ff8601cb96..a7ac20cb59 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/Xcdatamodel.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/Xcdatamodel.java @@ -19,7 +19,6 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.devtools.build.lib.actions.Artifact; -import com.google.devtools.build.lib.util.FileType; import com.google.devtools.build.lib.vfs.PathFragment; /** @@ -118,16 +117,4 @@ class Xcdatamodel extends Value<Xcdatamodel> { } }); } - - /** - * Returns a sequence of all unique *.xcdatamodel directories that contain all the artifacts of - * the given models. Note that this does not return any *.xcdatamodeld directories. - */ - static Iterable<PathFragment> xcdatamodelDirs(Iterable<Xcdatamodel> models) { - ImmutableSet.Builder<PathFragment> result = new ImmutableSet.Builder<>(); - for (Xcdatamodel model : models) { - result.addAll(ObjcCommon.uniqueContainers(model.getInputs(), FileType.of(".xcdatamodel"))); - } - return result.build(); - } } 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 a7cbd61883..ab6b4f7384 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 @@ -49,8 +49,10 @@ import com.google.devtools.build.xcode.xcodegen.proto.XcodeGenProtos.XcodeprojBu import java.util.Arrays; import java.util.EnumSet; +import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; import java.util.Set; /** @@ -147,23 +149,19 @@ public final class XcodeProvider implements TransitiveInfoProvider { * Adds {@link XcodeProvider}s corresponding to direct dependencies of this target which should * be added in the {@code .xcodeproj} file and propagated up the dependency chain. */ - public Builder addPropagatedDependencies(Iterable<XcodeProvider> dependencies, - ObjcConfiguration configuration) { - return addDependencies(dependencies, configuration, /*doPropagate=*/true); + public Builder addPropagatedDependencies(Iterable<XcodeProvider> dependencies) { + return addDependencies(dependencies, /*doPropagate=*/true); } /** * Adds {@link XcodeProvider}s corresponding to direct dependencies of this target which should * be added in the {@code .xcodeproj} file and not propagated up the dependency chain. */ - public Builder addNonPropagatedDependencies(Iterable<XcodeProvider> dependencies, - ObjcConfiguration configuration) { - return addDependencies(dependencies, configuration, /*doPropagate=*/false); + public Builder addNonPropagatedDependencies(Iterable<XcodeProvider> dependencies) { + return addDependencies(dependencies, /*doPropagate=*/false); } - private Builder addDependencies(Iterable<XcodeProvider> dependencies, - ObjcConfiguration configuration, boolean doPropagate) { - String architecture = configuration.getDependencySingleArchitecture(); + private Builder addDependencies(Iterable<XcodeProvider> dependencies, boolean doPropagate) { for (XcodeProvider dependency : dependencies) { // TODO(bazel-team): This is messy. Maybe we should make XcodeProvider be able to specify // how to depend on it rather than require this method to choose based on the dependency's @@ -171,7 +169,7 @@ public final class XcodeProvider implements TransitiveInfoProvider { if (dependency.productType == XcodeProductType.EXTENSION) { this.extensions.add(dependency); this.inputsToXcodegen.addTransitive(dependency.inputsToXcodegen); - } else if (dependency.architecture.equals(architecture)) { + } else { if (doPropagate) { this.propagatedDependencies.add(dependency); this.propagatedDependencies.addTransitive(dependency.propagatedDependencies); @@ -354,7 +352,21 @@ public final class XcodeProvider implements TransitiveInfoProvider { } ImmutableList.Builder<TargetControl> controls = new ImmutableList.Builder<>(); + Map<Label, XcodeProvider> labelToProvider = new HashMap<>(); for (XcodeProvider provider : providerSet) { + XcodeProvider oldProvider = labelToProvider.put(provider.label, provider); + if (oldProvider != null) { + if (!oldProvider.architecture.equals(provider.architecture)) { + // Do not include duplicate dependencies whose architecture does not match this + // project's. This check avoids having multiple conflicting Xcode targets for the same + // BUILD target that are only distinguished by this field (which Xcode does not care + // about). + continue; + } + + throw new IllegalStateException("Depending on multiple versions of the same xcode target " + + "is not allowed but occurred for: " + provider.label); + } controls.addAll(provider.targetControls()); } return controls.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 e2d057d990..366cf99448 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 @@ -93,9 +93,27 @@ public final class XcodeSupport { */ XcodeSupport addXcodeSettings(XcodeProvider.Builder xcodeProviderBuilder, ObjcProvider objcProvider, XcodeProductType productType) { + return addXcodeSettings(xcodeProviderBuilder, objcProvider, productType, + ObjcRuleClasses.objcConfiguration(ruleContext).getIosCpu()); + } + + /** + * Adds common xcode settings to the given provider builder, explicitly specifying architecture + * to use. + * + * @param objcProvider provider containing all dependencies' information as well as some of this + * rule's + * @param productType type of this rule's Xcode target + * @param architecture architecture to filter all dependencies with (only matching ones will be + * included in the final targets generated) + * + * @return this xcode support + */ + XcodeSupport addXcodeSettings(Builder xcodeProviderBuilder, + ObjcProvider objcProvider, XcodeProductType productType, String architecture) { xcodeProviderBuilder .setLabel(ruleContext.getLabel()) - .setArchitecture(ObjcRuleClasses.objcConfiguration(ruleContext).getIosCpu()) + .setArchitecture(architecture) .setObjcProvider(objcProvider) .setProductType(productType); return this; @@ -109,8 +127,7 @@ public final class XcodeSupport { XcodeSupport addDependencies(Builder xcodeProviderBuilder, Attribute attribute) { xcodeProviderBuilder.addPropagatedDependencies( ruleContext.getPrerequisites( - attribute.getName(), attribute.getAccessMode(), XcodeProvider.class), - ObjcRuleClasses.objcConfiguration(ruleContext)); + attribute.getName(), attribute.getAccessMode(), XcodeProvider.class)); return this; } @@ -125,8 +142,7 @@ public final class XcodeSupport { XcodeSupport addNonPropagatedDependencies(Builder xcodeProviderBuilder, Attribute attribute) { xcodeProviderBuilder.addNonPropagatedDependencies( ruleContext.getPrerequisites( - attribute.getName(), attribute.getAccessMode(), XcodeProvider.class), - ObjcRuleClasses.objcConfiguration(ruleContext)); + attribute.getName(), attribute.getAccessMode(), XcodeProvider.class)); return this; } diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/XibFiles.java b/src/main/java/com/google/devtools/build/lib/rules/objc/XibFiles.java deleted file mode 100644 index 9be1d06a01..0000000000 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/XibFiles.java +++ /dev/null @@ -1,42 +0,0 @@ -// 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 com.google.common.collect.ImmutableList; -import com.google.devtools.build.lib.actions.Artifact; - -/** - * A sequence of xib source files. Each {@code .xib} file can be compiled to a {@code .nib} file or - * directory. Because it might be a directory, we always use zip files to store the output and use - * the {@code actooloribtoolzip} utility to run ibtool and zip the output. - */ -public final class XibFiles extends IterableWrapper<Artifact> { - public XibFiles(Iterable<Artifact> artifacts) { - super(artifacts); - } - - /** - * Returns a sequence where each element of this sequence is converted to the file which contains - * the compiled contents of the xib. - */ - public ImmutableList<Artifact> compiledZips(IntermediateArtifacts intermediateArtifacts) { - ImmutableList.Builder<Artifact> zips = new ImmutableList.Builder<>(); - for (Artifact xib : this) { - zips.add(intermediateArtifacts.compiledXibFileZip(xib)); - } - return zips.build(); - } -} |