// Copyright 2015 The Bazel Authors. 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.apple; 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; import com.google.devtools.build.lib.analysis.config.BuildConfiguration; import com.google.devtools.build.lib.analysis.config.BuildOptions; import com.google.devtools.build.lib.analysis.config.ConfigurationEnvironment; import com.google.devtools.build.lib.analysis.config.ConfigurationFragmentFactory; import com.google.devtools.build.lib.analysis.config.FragmentOptions; import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; import com.google.devtools.build.lib.rules.apple.AppleCommandLineOptions.AppleBitcodeMode; 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.lib.skylarkinterface.SkylarkModuleCategory; import com.google.devtools.build.lib.util.Preconditions; import java.util.ArrayList; import java.util.List; import java.util.Map; import javax.annotation.Nullable; /** A configuration containing flags required for Apple platforms and tools. */ @SkylarkModule( name = "apple", doc = "A configuration fragment for Apple platforms.", category = SkylarkModuleCategory.CONFIGURATION_FRAGMENT ) @Immutable public class AppleConfiguration extends BuildConfiguration.Fragment { /** * Environment variable name for the xcode version. The value of this environment variable should * be set to the version (for example, "7.2") of xcode to use when invoking part of the apple * toolkit in action execution. **/ public static final String XCODE_VERSION_ENV_NAME = "XCODE_VERSION_OVERRIDE"; /** * Environment variable name for the apple SDK version. If unset, uses the system default of the * host for the platform in the value of {@link #APPLE_SDK_PLATFORM_ENV_NAME}. **/ public static final String APPLE_SDK_VERSION_ENV_NAME = "APPLE_SDK_VERSION_OVERRIDE"; /** * Environment variable name for the apple SDK platform. This should be set for all actions that * require an apple SDK. The valid values consist of {@link Platform} names. **/ public static final String APPLE_SDK_PLATFORM_ENV_NAME = "APPLE_SDK_PLATFORM"; private static final DottedVersion MINIMUM_BITCODE_XCODE_VERSION = DottedVersion.fromString("7"); @Nullable private final DottedVersion xcodeVersion; private final DottedVersion iosSdkVersion; private final DottedVersion iosMinimumOs; private final DottedVersion watchosSdkVersion; private final DottedVersion watchosMinimumOs; private final DottedVersion tvosSdkVersion; private final DottedVersion tvosMinimumOs; private final DottedVersion macosSdkVersion; private final DottedVersion macosMinimumOs; private final String iosCpu; private final String appleSplitCpu; private final PlatformType applePlatformType; private final ConfigurationDistinguisher configurationDistinguisher; private final ImmutableList<String> iosMultiCpus; private final ImmutableList<String> watchosCpus; private final ImmutableList<String> tvosCpus; private final ImmutableList<String> macosCpus; private final AppleBitcodeMode bitcodeMode; private final Label xcodeConfigLabel; private final boolean enableAppleCrosstool; @Nullable private final String xcodeToolchain; @Nullable private final Label defaultProvisioningProfileLabel; AppleConfiguration(AppleCommandLineOptions appleOptions, @Nullable DottedVersion xcodeVersion, DottedVersion iosSdkVersion, DottedVersion watchosSdkVersion, DottedVersion watchosMinimumOs, DottedVersion tvosSdkVersion, DottedVersion tvosMinimumOs, DottedVersion macosSdkVersion, DottedVersion macosMinimumOs) { this.iosSdkVersion = Preconditions.checkNotNull(iosSdkVersion, "iosSdkVersion"); this.iosMinimumOs = Preconditions.checkNotNull(appleOptions.iosMinimumOs, "iosMinimumOs"); this.watchosSdkVersion = Preconditions.checkNotNull(watchosSdkVersion, "watchOsSdkVersion"); this.watchosMinimumOs = Preconditions.checkNotNull(watchosMinimumOs, "watchOsMinimumOs"); this.tvosSdkVersion = Preconditions.checkNotNull(tvosSdkVersion, "tvOsSdkVersion"); this.tvosMinimumOs = Preconditions.checkNotNull(tvosMinimumOs, "tvOsMinimumOs"); this.macosSdkVersion = Preconditions.checkNotNull(macosSdkVersion, "macOsSdkVersion"); this.macosMinimumOs = Preconditions.checkNotNull(macosMinimumOs, "macOsMinimumOs"); this.xcodeVersion = xcodeVersion; this.iosCpu = Preconditions.checkNotNull(appleOptions.iosCpu, "iosCpu"); this.appleSplitCpu = Preconditions.checkNotNull(appleOptions.appleSplitCpu, "appleSplitCpu"); this.applePlatformType = Preconditions.checkNotNull(appleOptions.applePlatformType, "applePlatformType"); this.configurationDistinguisher = appleOptions.configurationDistinguisher; this.iosMultiCpus = ImmutableList.copyOf( Preconditions.checkNotNull(appleOptions.iosMultiCpus, "iosMultiCpus")); this.watchosCpus = (appleOptions.watchosCpus == null || appleOptions.watchosCpus.isEmpty()) ? ImmutableList.of(AppleCommandLineOptions.DEFAULT_WATCHOS_CPU) : ImmutableList.copyOf(appleOptions.watchosCpus); this.tvosCpus = (appleOptions.tvosCpus == null || appleOptions.tvosCpus.isEmpty()) ? ImmutableList.of(AppleCommandLineOptions.DEFAULT_TVOS_CPU) : ImmutableList.copyOf(appleOptions.tvosCpus); this.macosCpus = (appleOptions.macosCpus == null || appleOptions.macosCpus.isEmpty()) ? ImmutableList.of(AppleCommandLineOptions.DEFAULT_MACOS_CPU) : ImmutableList.copyOf(appleOptions.macosCpus); this.bitcodeMode = appleOptions.appleBitcodeMode; this.xcodeConfigLabel = Preconditions.checkNotNull(appleOptions.xcodeVersionConfig, "xcodeConfigLabel"); this.enableAppleCrosstool = appleOptions.enableAppleCrosstoolTransition; this.defaultProvisioningProfileLabel = appleOptions.defaultProvisioningProfile; this.xcodeToolchain = appleOptions.xcodeToolchain; } /** * Returns the minimum iOS version supported by binaries and libraries. Any dependencies on newer * iOS version features or libraries will become weak dependencies which are only loaded if the * runtime OS supports them. */ @SkylarkCallable(name = "ios_minimum_os", structField = true, doc = "The minimum compatible iOS version for target simulators and devices.") public DottedVersion getMinimumOs() { // TODO(bazel-team): Deprecate in favor of getMinimumOsForPlatformType(IOS). return iosMinimumOs; } @SkylarkCallable( name = "minimum_os_for_platform_type", doc = "The minimum compatible OS version for target simulator and devices for a particular " + "platform type.") public DottedVersion getMinimumOsForPlatformType(PlatformType platformType) { // TODO(b/37240784): Look into using only a single minimum OS flag tied to the current // apple_platform_type. switch (platformType) { case IOS: return iosMinimumOs; case TVOS: return tvosMinimumOs; case WATCHOS: return watchosMinimumOs; case MACOS: return macosMinimumOs; default: throw new IllegalArgumentException("Unhandled platform: " + platformType); } } /** * Returns the SDK version for ios SDKs (whether they be for simulator or device). This is * directly derived from --ios_sdk_version. * * @deprecated - use {@link #getSdkVersionForPlatform()} */ @Deprecated public DottedVersion getIosSdkVersion() { return getSdkVersionForPlatform(Platform.IOS_DEVICE); } /** * Returns the SDK version for a platform (whether they be for simulator or device). This is * directly derived from command line args. */ @SkylarkCallable(name = "sdk_version_for_platform", doc = "The SDK version given a platform.") public DottedVersion getSdkVersionForPlatform(Platform platform) { switch (platform) { case IOS_DEVICE: case IOS_SIMULATOR: return iosSdkVersion; case TVOS_DEVICE: case TVOS_SIMULATOR: return tvosSdkVersion; case WATCHOS_DEVICE: case WATCHOS_SIMULATOR: return watchosSdkVersion; case MACOS: return macosSdkVersion; } throw new AssertionError(); } /** * Returns the value of the xcode version, if available. This is determined based on a combination * of the {@code --xcode_version} build flag and the {@code xcode_config} target defined in the * {@code --xcode_version_config} flag. Returns null if no xcode is available. */ @SkylarkCallable(name = "xcode_version") @Nullable public DottedVersion getXcodeVersion() { return xcodeVersion; } /** * Returns a map of environment variables (derived from configuration) that should be propagated * for actions pertaining to the given apple platform. Keys are variable names and values are * their corresponding values. */ @SkylarkCallable(name = "target_apple_env") public ImmutableMap<String, String> getTargetAppleEnvironment(Platform platform) { ImmutableMap.Builder<String, String> mapBuilder = ImmutableMap.builder(); mapBuilder.putAll(appleTargetPlatformEnv(platform)); return mapBuilder.build(); } /** * Returns a map of environment variables that should be propagated for actions that build on an * apple host system. These environment variables are needed by the apple toolchain. Keys are * variable names and values are their corresponding values. */ @SkylarkCallable( name = "apple_host_system_env", doc = "Returns a map of environment variables that should be propagated for actions that " + "build on an apple host system. These environment variables are needed by the apple " + "toolchain. Keys are variable names and values are their corresponding values." ) public ImmutableMap<String, String> getAppleHostSystemEnv() { DottedVersion xcodeVersion = getXcodeVersion(); if (xcodeVersion != null) { return getXcodeVersionEnv(xcodeVersion); } else { return ImmutableMap.of(); } } /** * Returns a map of environment variables that should be propagated for actions that require * a version of xcode to be explicitly declared. Keys are variable names and values are their * corresponding values. */ public ImmutableMap<String, String> getXcodeVersionEnv(DottedVersion xcodeVersion) { return ImmutableMap.of(AppleConfiguration.XCODE_VERSION_ENV_NAME, xcodeVersion.toString()); } /** * Returns a map of environment variables (derived from configuration) that should be propagated * for actions pertaining to building applications for apple platforms. These environment * variables are needed to use apple toolkits. Keys are variable names and values are their * corresponding values. */ public Map<String, String> appleTargetPlatformEnv(Platform platform) { ImmutableMap.Builder<String, String> builder = ImmutableMap.builder(); String sdkVersion = getSdkVersionForPlatform(platform).toStringWithMinimumComponents(2); builder .put(AppleConfiguration.APPLE_SDK_VERSION_ENV_NAME, sdkVersion) .put(AppleConfiguration.APPLE_SDK_PLATFORM_ENV_NAME, platform.getNameInPlist()); return builder.build(); } /** * Returns the value of {@code ios_cpu} for this configuration. This is not necessarily the * platform or cpu for all actions spawned in this configuration; it is appropriate for * identifying the target cpu of iOS compile and link actions within this configuration. */ @SkylarkCallable(name = "ios_cpu", doc = "The value of ios_cpu for this configuration.") public String getIosCpu() { return iosCpu; } /** * Gets the single "effective" architecture for this configuration's {@link PlatformType} (for * example, "i386" or "arm64"). Prefer this over {@link #getMultiArchitectures(PlatformType)} only * if in the context of rule logic which is only concerned with a single architecture (such as in * {@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>If the multi cpus flag (e.g. {@code --ios_multi_cpus}) is set and non-empty, then the first * such architecture is returned. * <li>In the case of iOS, use {@code --ios_cpu} for backwards compatibility. * <li>Use the default. * </ol> */ @SkylarkCallable( name = "single_arch_cpu", structField = true, doc = "The single \"effective\" architecture for this configuration (e.g. i386 or arm64) " + "in the context of rule logic which is only concerned with a single architecture " + "(such as in objc_library, which registers single-architecture compile actions). " ) public String getSingleArchitecture() { if (!Strings.isNullOrEmpty(appleSplitCpu)) { return appleSplitCpu; } switch (applePlatformType) { case IOS: if (!getIosMultiCpus().isEmpty()) { return getIosMultiCpus().get(0); } else { return getIosCpu(); } case WATCHOS: return watchosCpus.get(0); case TVOS: return tvosCpus.get(0); case MACOS: return macosCpus.get(0); default: throw new IllegalArgumentException("Unhandled platform type " + applePlatformType); } } /** * Gets the "effective" architecture(s) for the given {@link PlatformType}. For example, * "i386" or "arm64". At least one architecture is always returned. Prefer this over * {@link #getSingleArchitecture} in rule logic which may support multiple architectures, such * as bundling rules. * * <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> getMultiArchitectures(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(); } case WATCHOS: return watchosCpus; case TVOS: return tvosCpus; case MACOS: return macosCpus; default: throw new IllegalArgumentException("Unhandled platform type " + platformType); } } /** * Gets the single "effective" platform for this configuration's {@link PlatformType} and * architecture. Prefer this over {@link #getMultiArchPlatform(PlatformType)} only in cases if in * the context of rule logic which is only concerned with a single architecture (such as in {@code * objc_library}, which registers single-architecture compile actions). */ @SkylarkCallable( name = "single_arch_platform", doc = "The platform of the current configuration. This should only be invoked in a context where " + "only a single architecture may be supported; consider mutli_arch_platform for other " + "cases.", structField = true ) public Platform getSingleArchPlatform() { return Platform.forTarget(applePlatformType, getSingleArchitecture()); } /** * 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" * architectures of this configuration, as returned by {@link #getMultiArchitectures}; if any * of the supported architectures are of device type, this will return a device platform. * Otherwise, this will return a simulator platform. */ // TODO(bazel-team): This should support returning multiple platforms. @SkylarkCallable(name = "multi_arch_platform", doc = "The platform of the current configuration " + "for the given platform type. This should only be invoked in a context where multiple " + "architectures may be supported; consider single_arch_platform for other cases.") public Platform getMultiArchPlatform(PlatformType platformType) { List<String> architectures = getMultiArchitectures(platformType); switch (platformType) { case IOS: for (String arch : architectures) { if (Platform.forTarget(PlatformType.IOS, arch) == Platform.IOS_DEVICE) { return Platform.IOS_DEVICE; } } return Platform.IOS_SIMULATOR; case WATCHOS: for (String arch : architectures) { if (Platform.forTarget(PlatformType.WATCHOS, arch) == Platform.WATCHOS_DEVICE) { return Platform.WATCHOS_DEVICE; } } return Platform.WATCHOS_SIMULATOR; case TVOS: for (String arch : architectures) { if (Platform.forTarget(PlatformType.TVOS, arch) == Platform.TVOS_DEVICE) { return Platform.TVOS_DEVICE; } } return Platform.TVOS_SIMULATOR; case MACOS: return Platform.MACOS; default: throw new IllegalArgumentException("Unsupported platform type " + platformType); } } /** * 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 #getMultiArchPlatform(PlatformType)}. */ // 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 Platform.forTarget(PlatformType.IOS, iosCpu); } /** * Returns the architecture for which we keep dependencies that should be present only once (in a * single architecture). * * <p>When building with multiple architectures there are some dependencies we want to avoid * duplicating: they would show up more than once in the same location in the final application * bundle which is illegal. Instead we pick one architecture for which to keep all dependencies * and discard any others. */ public String getDependencySingleArchitecture() { if (!getIosMultiCpus().isEmpty()) { return getIosMultiCpus().get(0); } return getIosCpu(); } /** * List of all CPUs that this invocation is being built for. Different from {@link #getIosCpu()} * which is the specific CPU <b>this target</b> is being built for. */ public ImmutableList<String> getIosMultiCpus() { return iosMultiCpus; } /** * Returns the label of the default provisioning profile to use when bundling/signing an ios * application. Returns null if the target platform is not an iOS device (for example, if * iOS simulator is being targeted). */ @Nullable public Label getDefaultProvisioningProfileLabel() { return defaultProvisioningProfileLabel; } /** * Returns the bitcode mode to use for compilation steps. This should only be invoked in * single-architecture contexts. * * <p>Users can control bitcode mode using the {@code apple_bitcode} build flag, but bitcode * will be disabled for all simulator architectures regardless of this flag. * * @see AppleBitcodeMode */ @SkylarkCallable( name = "bitcode_mode", doc = "Returns the bitcode mode to use for compilation steps.", structField = true ) public AppleBitcodeMode getBitcodeMode() { if (getSingleArchPlatform().isDevice()) { return bitcodeMode; } else { return AppleBitcodeMode.NONE; } } /** * Returns the label of the xcode_config rule to use for resolving the host system xcode version. */ public Label getXcodeConfigLabel() { return xcodeConfigLabel; } /** * Returns the unique identifier distinguishing configurations that are otherwise the same. * * <p>Use this value for situations in which two configurations create two outputs that are the * same but are not collapsed due to their different configuration owners. */ public ConfigurationDistinguisher getConfigurationDistinguisher() { return configurationDistinguisher; } private boolean shouldDistinguishOutputDirectory() { if (configurationDistinguisher == ConfigurationDistinguisher.UNKNOWN) { return false; } else if (configurationDistinguisher == ConfigurationDistinguisher.APPLE_CROSSTOOL && isAppleCrosstoolEnabled()) { return false; } else { return true; } } @Nullable @Override public String getOutputDirectoryName() { List<String> components = new ArrayList<>(); if (!appleSplitCpu.isEmpty()) { components.add(applePlatformType.toString().toLowerCase()); components.add(appleSplitCpu); components.add("min" + getMinimumOsForPlatformType(applePlatformType)); } if (shouldDistinguishOutputDirectory()) { components.add(configurationDistinguisher.getFileSystemName()); } if (components.isEmpty()) { return null; } return Joiner.on('-').join(components); } /** Returns the identifier for an Xcode toolchain to use with tools. */ @SkylarkCallable( name = "xcode_toolchain", doc = "Identifier for the custom Xcode toolchain to use in build or None if not specified.", allowReturnNones = true, structField = true ) public String getXcodeToolchain() { return xcodeToolchain; } /** Returns true if {@link AppleCrosstoolTransition} should be applied to every apple rule. */ public boolean isAppleCrosstoolEnabled() { return enableAppleCrosstool; } @Override public Map<String, Object> lateBoundOptionDefaults() { // xcode_version and *_sdk_version defaults come from processing the // target with label given in --xcode_version_override. ImmutableMap.Builder<String, Object> mapBuilder = ImmutableMap.builder(); if (xcodeVersion != null) { mapBuilder.put("xcode_version", xcodeVersion); } return mapBuilder .put("ios_sdk_version", iosSdkVersion) .put("tvos_sdk_version", tvosSdkVersion) .put("watchos_sdk_version", watchosSdkVersion) .put("macos_sdk_version", macosSdkVersion) .build(); } /** * Loads {@link AppleConfiguration} from build options. */ public static class Loader implements ConfigurationFragmentFactory { @Override public AppleConfiguration create(ConfigurationEnvironment env, BuildOptions buildOptions) throws InvalidConfigurationException, InterruptedException { AppleCommandLineOptions appleOptions = buildOptions.get(AppleCommandLineOptions.class); XcodeVersionProperties xcodeVersionProperties = getXcodeVersionProperties(env, appleOptions); DottedVersion iosSdkVersion = (appleOptions.iosSdkVersion != null) ? appleOptions.iosSdkVersion : xcodeVersionProperties.getDefaultIosSdkVersion(); // TODO(cparsons): Look into ios_minimum_os matching the defaulting behavior of the other // platforms. DottedVersion watchosSdkVersion = (appleOptions.watchOsSdkVersion != null) ? appleOptions.watchOsSdkVersion : xcodeVersionProperties.getDefaultWatchosSdkVersion(); DottedVersion watchosMinimumOsVersion = (appleOptions.watchosMinimumOs != null) ? appleOptions.watchosMinimumOs : watchosSdkVersion; DottedVersion tvosSdkVersion = (appleOptions.tvOsSdkVersion != null) ? appleOptions.tvOsSdkVersion : xcodeVersionProperties.getDefaultTvosSdkVersion(); DottedVersion tvosMinimumOsVersion = (appleOptions.tvosMinimumOs != null) ? appleOptions.tvosMinimumOs : tvosSdkVersion; DottedVersion macosSdkVersion = (appleOptions.macOsSdkVersion != null) ? appleOptions.macOsSdkVersion : xcodeVersionProperties.getDefaultMacosSdkVersion(); DottedVersion macosMinimumOsVersion = (appleOptions.macosMinimumOs != null) ? appleOptions.macosMinimumOs : macosSdkVersion; AppleConfiguration configuration = new AppleConfiguration(appleOptions, xcodeVersionProperties.getXcodeVersion().orNull(), iosSdkVersion, watchosSdkVersion, watchosMinimumOsVersion, tvosSdkVersion, tvosMinimumOsVersion, macosSdkVersion, macosMinimumOsVersion); validate(configuration); return configuration; } private void validate(AppleConfiguration config) throws InvalidConfigurationException { DottedVersion xcodeVersion = config.getXcodeVersion(); if (config.getBitcodeMode() != AppleBitcodeMode.NONE && xcodeVersion != null && xcodeVersion.compareTo(MINIMUM_BITCODE_XCODE_VERSION) < 0) { throw new InvalidConfigurationException( String.format("apple_bitcode mode '%s' is unsupported for xcode version '%s'", config.getBitcodeMode(), xcodeVersion)); } } @Override public Class<? extends BuildConfiguration.Fragment> creates() { return AppleConfiguration.class; } @Override public ImmutableSet<Class<? extends FragmentOptions>> requiredOptions() { return ImmutableSet.<Class<? extends FragmentOptions>>of(AppleCommandLineOptions.class); } /** * Uses the {@link AppleCommandLineOptions#xcodeVersion} and {@link * AppleCommandLineOptions#xcodeVersionConfig} command line options to determine and return the * effective xcode version properties. Returns absent if no explicit xcode version is declared, * and host system defaults should be used. * * @param env the current configuration environment * @param appleOptions the command line options * @throws InvalidConfigurationException if the options given (or configuration targets) were * malformed and thus the xcode version could not be determined */ private static XcodeVersionProperties getXcodeVersionProperties( ConfigurationEnvironment env, AppleCommandLineOptions appleOptions) throws InvalidConfigurationException, InterruptedException { Optional<DottedVersion> xcodeVersionCommandLineFlag = Optional.fromNullable(appleOptions.xcodeVersion); Label xcodeVersionConfigLabel = appleOptions.xcodeVersionConfig; return XcodeConfig.resolveXcodeVersion(env, xcodeVersionConfigLabel, xcodeVersionCommandLineFlag, "xcode_version_config"); } } /** * 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. */ public enum ConfigurationDistinguisher { UNKNOWN("unknown"), /** Split transition distinguisher for {@code ios_extension} rule. */ IOS_EXTENSION("ios_extension"), /** Split transition distinguisher for {@code ios_application} rule. */ IOS_APPLICATION("ios_application"), /** Split transition distinguisher for {@code ios_framework} rule. */ FRAMEWORK("framework"), /** Split transition distinguisher for {@code apple_watch1_extension} rule. */ WATCH_OS1_EXTENSION("watch_os1_extension"), /** Distinguisher for non-extension {@code apple_binary} rule with "ios" platform_type. */ APPLEBIN_IOS("applebin_ios"), /** Distinguisher for non-extension {@code apple_binary} rule with "watchos" platform_type. */ APPLEBIN_WATCHOS("applebin_watchos"), /** Distinguisher for non-extension {@code apple_binary} rule with "tvos" platform_type. */ APPLEBIN_TVOS("applebin_tvos"), /** Distinguisher for non-extension {@code apple_binary} rule with "macos" platform_type. */ APPLEBIN_MACOS("applebin_macos"), /** Distinguisher for extension {@code apple_binary} rule with "ios" platform_type. */ APPLEBIN_IOS_EXT("applebin_ios_ext"), /** Distinguisher for extension {@code apple_binary} rule with "watchos" platform_type. */ APPLEBIN_WATCHOS_EXT("applebin_watchos_ext"), /** Distinguisher for extension {@code apple_binary} rule with "tvos" platform_type. */ APPLEBIN_TVOS_EXT("applebin_tvos_ext"), /** Distinguisher for extension {@code apple_binary} rule with "macos" platform_type. */ APPLEBIN_MACOS_EXT("applebin_macos_ext"), /** * Distinguisher for the apple crosstool configuration. We use "apl" for output directory * names instead of "apple_crosstool" to avoid oversized path names, which can be problematic * on OSX. */ APPLE_CROSSTOOL("apl"); private final String fileSystemName; private ConfigurationDistinguisher(String fileSystemName) { this.fileSystemName = fileSystemName; } /** * Returns the distinct string that should be used in creating output directories for a * configuration with this distinguisher. */ public String getFileSystemName() { return fileSystemName; } } }