Skip to content

Commit

Permalink
Refactoring of Apple Platform detection and usage, to pave the way fo…
Browse files Browse the repository at this point in the history
…r non-IOS Platforms.

There are still various places which infer IOS platform type which need to be fixed, but this ensures switching to other platform types is as easy as changing an argument.

--
MOS_MIGRATED_REVID=123444548
  • Loading branch information
c-parsons authored and dslomov committed May 30, 2016
1 parent 0f4927f commit adccc11
Show file tree
Hide file tree
Showing 10 changed files with 166 additions and 74 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -171,11 +171,11 @@ public DefaultProvisioningProfileConverter() {

private Platform getPlatform() {
for (String architecture : iosMultiCpus) {
if (Platform.forIosArch(architecture) == Platform.IOS_DEVICE) {
if (Platform.forTarget(PlatformType.IOS, architecture) == Platform.IOS_DEVICE) {
return Platform.IOS_DEVICE;
}
}
return Platform.forIosArch(iosCpu);
return Platform.forTarget(PlatformType.IOS, iosCpu);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import com.google.common.base.Joiner;
import com.google.common.base.Optional;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
Expand Down Expand Up @@ -220,38 +221,135 @@ public String getIosCpu() {
}

/**
* Returns the {@link Platform} represented by {@code ios_cpu} (see {@link #getIosCpu}.
* (For example, {@code i386} maps to {@link Platform#IOS_SIMULATOR}.) Note that this is not
* necessarily the effective platform for all ios actions in the current context: This is
* typically the correct platform for implicityly-ios compile and link actions in the current
* context. For effective platform for bundling actions, see {@link #getBundlingPlatform}.
* Gets the single "effective" architecture for the given {@link PlatformType}. Prefer this over
* {@link #getArchitectures(PlatformType)} only in cases if in the context of a rule which
* is only concerned with a single architecture (such as {@code objc_library}, which registers
* single-architecture compile actions.
*
* <p>Single effective architecture is determined using the following rules:
* <ol>
* <li>If {@code --apple_split_cpu} is set (done via prior configuration transition), then
* that is the effective architecture.</li>
* <li>In the case of iOS, use {@code --ios_cpu}.</li>
* <li>Use the default.</li></ol>
*
* @throws IllegalArgumentException if {@code --apple_platform_type} is set (via prior
* configuration transition) yet does not match {@code platformType}
*/
@SkylarkCallable(name = "ios_cpu_platform", doc = "The platform given by the ios_cpu flag.")
public Platform getIosCpuPlatform() {
return Platform.forIosArch(getIosCpu());
// TODO(cparsons): Support platform types other than iOS.
// TODO(b/28958783): Consider changing this behavior to be more consistent between single and
// multi-arch cases.
public String getSingleArchitecture(PlatformType platformType) {
if (!Strings.isNullOrEmpty(appleSplitCpu)) {
if (applePlatformType != platformType) {
throw new IllegalArgumentException(
String.format("Expected post-split-transition platform type %s to match input %s ",
applePlatformType, platformType));
}
return appleSplitCpu;
}
switch (platformType) {
case IOS:
return getIosCpu();
// TODO(cparsons): Support other platform types.
default:
throw new IllegalArgumentException("Unhandled platform type " + platformType);
}
}

/**
* Gets the "effective" architecture(s) for the given {@link PlatformType}. For example,
* "i386" or "arm64". At least one architecture is always returned.
*
* <p>Effective architecture(s) is determined using the following rules:
* <ol>
* <li>If {@code --apple_split_cpu} is set (done via prior configuration transition), then
* that is the effective architecture.</li>
* <li>If the multi-cpu flag (for example, {@code --ios_multi_cpus}) is non-empty, then, return
* all architectures from that flag.</li>
* <li>In the case of iOS, use {@code --ios_cpu} for backwards compatibility.</li>
* <li>Use the default.</li></ol>
*
* @throws IllegalArgumentException if {@code --apple_platform_type} is set (via prior
* configuration transition) yet does not match {@code platformType}
*/
public List<String> getArchitectures(PlatformType platformType) {
if (!Strings.isNullOrEmpty(appleSplitCpu)) {
if (applePlatformType != platformType) {
throw new IllegalArgumentException(
String.format("Expected post-split-transition platform type %s to match input %s ",
applePlatformType, platformType));
}
return ImmutableList.of(appleSplitCpu);
}
switch (platformType) {
case IOS:
if (getIosMultiCpus().isEmpty()) {
return ImmutableList.of(getIosCpu());
} else {
return getIosMultiCpus();
}
// TODO(cparsons): Support other platform types.
default:
throw new IllegalArgumentException("Unhandled platform type " + platformType);
}
}

/**
* Gets the current configuration {@link Platform} for the given {@link PlatformType}. Platform
* is determined via a combination between the given platform type and the "effective
* architecture" of this configuration, as returned by {@link #getArchitectures}. If there
* are multiple effective architectures, the first in the list will be used. (This handles
* cases where multiple architectures may be specified, for example via multi-cpu flag, though
* only one can be consumed for the current rule.) Consider {@link #getBundlingPlatform} as an
* alternative, when more than one architecture may be expected.
*/
public Platform getPlatform(PlatformType platformType) {
return Platform.forTarget(platformType, getSingleArchitecture(platformType));
}

/**
* Returns the platform of the configuration for the current bundle, based on configured
* architectures (for example, {@code i386} maps to {@link Platform#IOS_SIMULATOR}).
* Returns the platform of the configuration for the current bundle for {@link PlatformType#IOS}
* platform type, based on configured "effective" architectures (for example, {@code i386} maps
* to {@link Platform#IOS_SIMULATOR}).
*
* <p>Effective architecture(s) are determined via {@link #getArchitectures}. If there are
* multiple effective architectures, then returns {@link Platform#IOS_DEVICE} if any of the
* architectures matches it, otherwise returns {@link Platform#IOS_SIMULATOR}.
*
* <p>If {@link #getIosMultiCpus()} is set, returns {@link Platform#IOS_DEVICE} if any of the
* architectures matches it, otherwise returns the mapping for {@link #getIosCpu()}.
* <p>Note that this method is similar to, {@link #getPlatform} but different in how it handles
* multiple architecture scenarios. This method should be used for obtaining {@link Platform} in
* contexts where multiple architectures are expected, such as bundling rules.
*
* <p>Note that this method should not be used to determine the platform for code compilation.
* Derive the platform from {@link #getIosCpu()} instead.
* @throws IllegalArgumentException if the current build options specify architecture(s) with
* no known apple platform
*/
// TODO(bazel-team): This method should be enabled to return multiple values once all call sites
// (in particular actool, bundlemerge, momc) have been upgraded to support multiple values.
// TODO(cparsons): Take platform type as input, supporting platforms other than IOS.
public Platform getBundlingPlatform() {
for (String architecture : getIosMultiCpus()) {
if (Platform.forIosArch(architecture) == Platform.IOS_DEVICE) {
List<String> architectures = getArchitectures(PlatformType.IOS);
for (String arch : architectures) {
if (Platform.forTarget(PlatformType.IOS, arch) == Platform.IOS_DEVICE) {
return Platform.IOS_DEVICE;
}
}
return Platform.forIosArch(getIosCpu());
return Platform.IOS_SIMULATOR;
}

/**
* Returns the {@link Platform} represented by {@code ios_cpu} (see {@link #getIosCpu}.
* (For example, {@code i386} maps to {@link Platform#IOS_SIMULATOR}.) Note that this is not
* necessarily the effective platform for all ios actions in the current context: This is
* typically the correct platform for implicityly-ios compile and link actions in the current
* context. For effective platform for bundling actions, see {@link #getBundlingPlatform}.
*/
// TODO(b/28754442): Deprecate for more general skylark-exposed platform retrieval.
@SkylarkCallable(name = "ios_cpu_platform", doc = "The platform given by the ios_cpu flag.")
public Platform getIosCpuPlatform() {
return getPlatform(PlatformType.IOS);
}

/**
* Returns the architecture for which we keep dependencies that should be present only once (in a
* single architecture).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import com.google.devtools.build.lib.packages.RuleClass;
import com.google.devtools.build.lib.packages.RuleClass.Builder;
import com.google.devtools.build.lib.packages.RuleClass.Builder.RuleClassType;
import com.google.devtools.build.lib.rules.apple.Platform.PlatformType;
import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable;
import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
import com.google.devtools.build.xcode.xcodegen.proto.XcodeGenProtos.XcodeprojBuildSetting;
Expand Down Expand Up @@ -86,7 +87,7 @@ public class AppleToolchain {
*/
// TODO(bazel-team): Support non-ios platforms.
public static String getPlatformPlistName(AppleConfiguration configuration) {
return Platform.forIosArch(configuration.getIosCpu()).getNameInPlist();
return configuration.getPlatform(PlatformType.IOS).getNameInPlist();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,10 @@ public enum Platform {
WATCHOS_DEVICE("WatchOS"),
WATCHOS_SIMULATOR("WatchSimulator");

private static final Set<String> IOS_SIMULATOR_ARCHS = ImmutableSet.of("i386", "x86_64");
private static final Set<String> IOS_DEVICE_ARCHS =
ImmutableSet.of("armv6", "armv7", "armv7s", "arm64");

private static final Set<String> IOS_SIMULATOR_TARGET_CPUS =
ImmutableSet.of("ios_x86_64", "ios_i386");
private static final Set<String> IOS_DEVICE_TARGET_CPUS =
ImmutableSet.of("ios_armv7", "ios_arm64");
ImmutableSet.of("ios_armv6", "ios_arm64", "ios_armv7", "ios_armv7s");
private static final Set<String> MACOSX_TARGET_CPUS =
ImmutableSet.of("darwin_x86_64");

Expand All @@ -68,26 +64,6 @@ public String getLowerCaseNameInPlist() {
return nameInPlist.toLowerCase(Locale.US);
}

/**
* Returns the iOS platform for the given iOS architecture.
*
* <p>If this method is used in non-iOS contexts, results are undefined. If the input happens
* to share an architecture with some iOS platform, this will return that platform even if it is
* incorrect (for example, IOS_SIMULATOR for the x86_64 of darwin_x86_64).
*
* @throws IllegalArgumentException if there is no valid ios platform for the given architecture
*/
public static Platform forIosArch(String arch) {
if (IOS_SIMULATOR_ARCHS.contains(arch)) {
return IOS_SIMULATOR;
} else if (IOS_DEVICE_ARCHS.contains(arch)) {
return IOS_DEVICE;
} else {
throw new IllegalArgumentException(
"No supported ios platform registered for architecture " + arch);
}
}

@Nullable
private static Platform forTargetCpuNullable(String targetCpu) {
if (IOS_SIMULATOR_TARGET_CPUS.contains(targetCpu)) {
Expand All @@ -102,10 +78,22 @@ private static Platform forTargetCpuNullable(String targetCpu) {
}

/**
* Returns the platform for the given target cpu.
* Returns the platform for the given target cpu and platform type.
*
* @param platformType platform type that the given cpu value is implied for
* @param arch architecture representation, such as 'arm64'
* @throws IllegalArgumentException if there is no valid apple platform for the given target cpu
*/
public static Platform forTarget(PlatformType platformType, String arch) {
return forTargetCpu(String.format("%s_%s", platformType.toString(), arch));
}

/**
* Returns the platform for the given target cpu.
*
* @param targetCpu cpu value with platform type prefix, such as 'ios_arm64'
* @throws IllegalArgumentException if there is no valid apple platform for the given target cpu
*/
public static Platform forTargetCpu(String targetCpu) {
Platform platform = forTargetCpuNullable(targetCpu);
if (platform != null) {
Expand All @@ -132,6 +120,11 @@ public enum PlatformType {
IOS,
WATCHOS,
TVOS,
MACOSX
MACOSX;

@Override
public String toString() {
return name().toLowerCase();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -142,11 +142,11 @@ public final ConfiguredTarget create(RuleContext ruleContext)
.registerCombineArchitecturesAction(
binariesToLipo.build(),
ruleIntermediateArtifacts.combinedArchitectureBinary(),
appleConfiguration.getIosCpuPlatform())
appleConfiguration.getPlatform(PlatformType.IOS))
.registerCombineArchitecturesAction(
archivesToLipo.build(),
ruleContext.getImplicitOutputArtifact(AppleBinaryRule.LIPO_ARCHIVE),
appleConfiguration.getIosCpuPlatform());
appleConfiguration.getPlatform(PlatformType.IOS));

RuleConfiguredTargetBuilder targetBuilder =
ObjcRuleClasses.ruleConfiguredTarget(ruleContext, filesToBuild.build());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import com.google.devtools.build.lib.rules.apple.AppleConfiguration;
import com.google.devtools.build.lib.rules.apple.AppleToolchain;
import com.google.devtools.build.lib.rules.apple.Platform;
import com.google.devtools.build.lib.rules.apple.Platform.PlatformType;
import com.google.devtools.build.lib.rules.objc.XcodeProvider.Builder;
import com.google.devtools.build.lib.vfs.FileSystemUtils;
import com.google.devtools.build.lib.vfs.PathFragment;
Expand Down Expand Up @@ -137,8 +138,8 @@ private void validatePlatform() {
Platform platform = null;
for (String architecture : appleConfiguration.getIosMultiCpus()) {
if (platform == null) {
platform = Platform.forIosArch(architecture);
} else if (platform != Platform.forIosArch(architecture)) {
platform = Platform.forTarget(PlatformType.IOS, architecture);
} else if (platform != Platform.forTarget(PlatformType.IOS, architecture)) {
ruleContext.ruleError(
String.format("In builds which require bundling, --ios_multi_cpus does not currently "
+ "allow values for both simulator and device builds. Flag was %s",
Expand Down
Loading

0 comments on commit adccc11

Please sign in to comment.