| // Copyright 2016 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.objc; |
| |
| import static com.google.devtools.build.lib.packages.Type.STRING; |
| |
| import com.google.common.annotations.VisibleForTesting; |
| import com.google.common.base.Optional; |
| import com.google.common.base.Strings; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableSet; |
| import com.google.common.collect.Iterables; |
| import com.google.devtools.build.lib.analysis.PlatformOptions; |
| import com.google.devtools.build.lib.analysis.RuleContext; |
| import com.google.devtools.build.lib.analysis.config.BuildOptions; |
| import com.google.devtools.build.lib.analysis.config.BuildOptionsView; |
| import com.google.devtools.build.lib.analysis.config.CoreOptions; |
| import com.google.devtools.build.lib.analysis.config.FragmentOptions; |
| import com.google.devtools.build.lib.analysis.config.transitions.SplitTransition; |
| import com.google.devtools.build.lib.analysis.config.transitions.TransitionFactory; |
| import com.google.devtools.build.lib.cmdline.Label; |
| import com.google.devtools.build.lib.events.EventHandler; |
| import com.google.devtools.build.lib.packages.AttributeTransitionData; |
| import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException; |
| import com.google.devtools.build.lib.rules.apple.AppleCommandLineOptions; |
| import com.google.devtools.build.lib.rules.apple.ApplePlatform; |
| import com.google.devtools.build.lib.rules.apple.ApplePlatform.PlatformType; |
| import com.google.devtools.build.lib.rules.apple.DottedVersion; |
| import com.google.devtools.build.lib.rules.cpp.CppOptions; |
| import com.google.devtools.build.lib.starlarkbuildapi.SplitTransitionProviderApi; |
| import java.util.List; |
| import java.util.Map; |
| import net.starlark.java.eval.Printer; |
| import net.starlark.java.eval.StarlarkValue; |
| |
| /** |
| * {@link TransitionFactory} implementation for multi-architecture apple rules which can accept |
| * different apple platform types (such as ios or watchos). |
| */ |
| // TODO(https://github.com/bazelbuild/bazel/pull/7825): Rename to MultiArchSplitTransitionFactory. |
| public class MultiArchSplitTransitionProvider |
| implements TransitionFactory<AttributeTransitionData>, |
| SplitTransitionProviderApi, |
| StarlarkValue { |
| |
| @VisibleForTesting |
| static final String UNSUPPORTED_PLATFORM_TYPE_ERROR_FORMAT = |
| "Unsupported platform type \"%s\""; |
| |
| @VisibleForTesting |
| static final String INVALID_VERSION_STRING_ERROR_FORMAT = |
| "Invalid version string \"%s\". Version must be of the form 'x.y' without alphabetic " |
| + "characters, such as '4.3'."; |
| |
| private static final ImmutableSet<PlatformType> SUPPORTED_PLATFORM_TYPES = |
| ImmutableSet.of( |
| PlatformType.IOS, |
| PlatformType.WATCHOS, |
| PlatformType.TVOS, |
| PlatformType.MACOS, |
| PlatformType.CATALYST); |
| |
| /** |
| * Returns the apple platform type in the current rule context. |
| * |
| * @throws RuleErrorException if the platform type attribute in the current rulecontext is |
| * an invalid value |
| */ |
| public static PlatformType getPlatformType(RuleContext ruleContext) throws RuleErrorException { |
| String attributeValue = |
| ruleContext.attributes().get(ObjcRuleClasses.PLATFORM_TYPE_ATTR_NAME, STRING); |
| try { |
| return getPlatformType(attributeValue); |
| } catch ( |
| @SuppressWarnings("UnusedException") |
| ApplePlatform.UnsupportedPlatformTypeException exception) { |
| throw ruleContext.throwWithAttributeError( |
| ObjcRuleClasses.PLATFORM_TYPE_ATTR_NAME, |
| String.format(UNSUPPORTED_PLATFORM_TYPE_ERROR_FORMAT, attributeValue)); |
| } |
| } |
| |
| /** |
| * Returns the apple platform type for the given platform type string (corresponding directly with |
| * platform type attribute value). |
| * |
| * @throws UnsupportedPlatformTypeException if the given platform type string is not a valid type |
| */ |
| private static PlatformType getPlatformType(String platformTypeString) |
| throws ApplePlatform.UnsupportedPlatformTypeException { |
| PlatformType platformType = PlatformType.fromString(platformTypeString); |
| |
| if (!SUPPORTED_PLATFORM_TYPES.contains(platformType)) { |
| throw new ApplePlatform.UnsupportedPlatformTypeException( |
| String.format(UNSUPPORTED_PLATFORM_TYPE_ERROR_FORMAT, platformTypeString)); |
| } else { |
| return platformType; |
| } |
| } |
| |
| @Override |
| public SplitTransition create(AttributeTransitionData data) { |
| String platformTypeString = |
| data.attributes().get(ObjcRuleClasses.PLATFORM_TYPE_ATTR_NAME, STRING); |
| String minimumOsVersionString = |
| data.attributes().get(ObjcRuleClasses.MINIMUM_OS_VERSION, STRING); |
| PlatformType platformType; |
| Optional<DottedVersion> minimumOsVersion; |
| try { |
| platformType = getPlatformType(platformTypeString); |
| // TODO(b/37096178): This should be a mandatory attribute. |
| if (Strings.isNullOrEmpty(minimumOsVersionString)) { |
| minimumOsVersion = Optional.absent(); |
| } else { |
| minimumOsVersion = Optional.of(DottedVersion.fromString(minimumOsVersionString)); |
| } |
| } catch (ApplePlatform.UnsupportedPlatformTypeException |
| | DottedVersion.InvalidDottedVersionException exception) { |
| // There's no opportunity to propagate exception information up cleanly at the transition |
| // provider level. This should later be registered as a rule error during the initialization |
| // of the rule. |
| platformType = PlatformType.IOS; |
| minimumOsVersion = Optional.absent(); |
| } |
| |
| return new AppleBinaryTransition(platformType, minimumOsVersion); |
| } |
| |
| @Override |
| public TransitionType transitionType() { |
| return TransitionType.ATTRIBUTE; |
| } |
| |
| @Override |
| public boolean isSplit() { |
| return true; |
| } |
| |
| @Override |
| public boolean isImmutable() { |
| return true; |
| } |
| |
| @Override |
| public void repr(Printer printer) { |
| printer.append("apple_common.multi_arch_split"); |
| } |
| |
| /** |
| * Transition that results in one configured target per architecture specified in the |
| * platform-specific cpu flag for a particular platform type (for example, --watchos_cpus for |
| * watchos platform type). |
| */ |
| protected static final class AppleBinaryTransition implements SplitTransition { |
| |
| private final PlatformType platformType; |
| // TODO(b/37096178): This should be a mandatory attribute. |
| private final Optional<DottedVersion> minimumOsVersion; |
| |
| public AppleBinaryTransition(PlatformType platformType, |
| Optional<DottedVersion> minimumOsVersion) { |
| this.platformType = platformType; |
| this.minimumOsVersion = minimumOsVersion; |
| } |
| |
| @Override |
| public ImmutableSet<Class<? extends FragmentOptions>> requiresOptionFragments() { |
| return ImmutableSet.of( |
| AppleCommandLineOptions.class, |
| CoreOptions.class, |
| CppOptions.class, |
| ObjcCommandLineOptions.class, |
| PlatformOptions.class); |
| } |
| |
| @Override |
| public final Map<String, BuildOptions> split( |
| BuildOptionsView buildOptions, EventHandler eventHandler) { |
| AppleCommandLineOptions appleOptions = buildOptions.get(AppleCommandLineOptions.class); |
| if (appleOptions.incompatibleUseToolchainResolution) { |
| List<Label> platformsToSplit = appleOptions.applePlatforms; |
| if (platformsToSplit.isEmpty()) { |
| // If --apple_platforms is unset, instead use only the first value from --platforms. |
| Label targetPlatform = |
| Iterables.getFirst(buildOptions.get(PlatformOptions.class).platforms, null); |
| platformsToSplit = ImmutableList.of(targetPlatform); |
| } |
| return MultiArchBinarySupport.handleApplePlatforms( |
| buildOptions, platformType, minimumOsVersion, platformsToSplit); |
| } |
| return MultiArchBinarySupport.handleAppleCpus(buildOptions, platformType, minimumOsVersion); |
| } |
| } |
| } |