| // 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 com.google.common.annotations.VisibleForTesting; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableMap; |
| import com.google.common.collect.Maps; |
| import com.google.devtools.build.lib.actions.Artifact; |
| import com.google.devtools.build.lib.actions.MutableActionGraph.ActionConflictException; |
| import com.google.devtools.build.lib.analysis.RuleContext; |
| import com.google.devtools.build.lib.analysis.TransitiveInfoCollection; |
| import com.google.devtools.build.lib.analysis.config.BuildConfigurationValue; |
| import com.google.devtools.build.lib.analysis.config.transitions.StarlarkExposedRuleTransitionFactory; |
| import com.google.devtools.build.lib.analysis.platform.ConstraintValueInfo; |
| import com.google.devtools.build.lib.analysis.starlark.StarlarkRuleContext; |
| import com.google.devtools.build.lib.collect.nestedset.Depset; |
| import com.google.devtools.build.lib.collect.nestedset.NestedSet; |
| import com.google.devtools.build.lib.packages.BuiltinProvider; |
| import com.google.devtools.build.lib.packages.Provider; |
| import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException; |
| import com.google.devtools.build.lib.packages.StarlarkInfo; |
| import com.google.devtools.build.lib.packages.StructImpl; |
| import com.google.devtools.build.lib.rules.apple.AppleConfiguration; |
| 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.AppleToolchain; |
| import com.google.devtools.build.lib.rules.apple.DottedVersion; |
| import com.google.devtools.build.lib.rules.apple.XcodeConfigInfo; |
| import com.google.devtools.build.lib.rules.apple.XcodeVersionProperties; |
| import com.google.devtools.build.lib.rules.cpp.CcInfo; |
| import com.google.devtools.build.lib.rules.cpp.CcModule; |
| import com.google.devtools.build.lib.rules.cpp.CppSemantics; |
| import com.google.devtools.build.lib.rules.objc.ObjcProvider.Flag; |
| import com.google.devtools.build.lib.starlarkbuildapi.SplitTransitionProviderApi; |
| import com.google.devtools.build.lib.starlarkbuildapi.objc.AppleCommonApi; |
| import java.util.Map; |
| import javax.annotation.Nullable; |
| import net.starlark.java.eval.Dict; |
| import net.starlark.java.eval.EvalException; |
| import net.starlark.java.eval.Sequence; |
| import net.starlark.java.eval.Starlark; |
| import net.starlark.java.eval.StarlarkFunction; |
| import net.starlark.java.eval.StarlarkInt; |
| import net.starlark.java.eval.StarlarkList; |
| import net.starlark.java.eval.StarlarkThread; |
| import net.starlark.java.eval.StarlarkValue; |
| import net.starlark.java.syntax.Location; |
| |
| /** A class that exposes apple rule implementation internals to Starlark. */ |
| public class AppleStarlarkCommon |
| implements AppleCommonApi< |
| Artifact, |
| ConstraintValueInfo, |
| StarlarkRuleContext, |
| CcInfo, |
| ObjcProvider, |
| XcodeConfigInfo, |
| ApplePlatform> { |
| |
| @Override |
| public StarlarkExposedRuleTransitionFactory getAppleCrosstoolTransition() { |
| return new AppleCrosstoolTransition.AppleCrosstoolTransitionFactory(); |
| } |
| |
| @VisibleForTesting |
| public static final String DEPRECATED_KEY_ERROR = |
| "Key '%s' no longer supported in ObjcProvider (use CcInfo instead)."; |
| |
| @VisibleForTesting |
| public static final String BAD_KEY_ERROR = |
| "Argument %s not a recognized key, 'strict_include', or 'providers'."; |
| |
| @VisibleForTesting |
| public static final String BAD_PROVIDERS_ITER_ERROR = |
| "Value for argument 'providers' must be a list of ObjcProvider instances, instead found %s."; |
| |
| @VisibleForTesting |
| public static final String BAD_PROVIDERS_ELEM_ERROR = |
| "Value for argument 'providers' must be a list of ObjcProvider instances, instead found " |
| + "iterable with %s."; |
| |
| @VisibleForTesting |
| public static final String NOT_SET_ERROR = "Value for key %s must be a set, instead found %s."; |
| |
| @Nullable private StructImpl platformType; |
| @Nullable private StructImpl platform; |
| |
| private final CppSemantics cppSemantics; |
| |
| public AppleStarlarkCommon(CppSemantics cppSemantics) { |
| this.cppSemantics = cppSemantics; |
| } |
| |
| @Override |
| public AppleToolchain getAppleToolchain() { |
| return new AppleToolchain(); |
| } |
| |
| @Override |
| public StructImpl getPlatformTypeStruct() { |
| if (platformType == null) { |
| platformType = PlatformType.getStarlarkStruct(); |
| } |
| return platformType; |
| } |
| |
| @Override |
| public StructImpl getPlatformStruct() { |
| if (platform == null) { |
| platform = ApplePlatform.getStarlarkStruct(); |
| } |
| return platform; |
| } |
| |
| @Override |
| public Provider getXcodeVersionPropertiesConstructor() { |
| return XcodeVersionProperties.STARLARK_CONSTRUCTOR; |
| } |
| |
| @Override |
| public Provider getXcodeVersionConfigConstructor() { |
| return XcodeConfigInfo.PROVIDER; |
| } |
| |
| @Override |
| public Provider getObjcProviderConstructor() { |
| return ObjcProvider.STARLARK_CONSTRUCTOR; |
| } |
| |
| @Override |
| public Provider getAppleDynamicFrameworkConstructor() { |
| return AppleDynamicFrameworkInfo.STARLARK_CONSTRUCTOR; |
| } |
| |
| @Override |
| public Provider getAppleExecutableBinaryConstructor() { |
| return AppleExecutableBinaryInfo.STARLARK_CONSTRUCTOR; |
| } |
| |
| @Override |
| public Provider getAppleDebugOutputsConstructor() { |
| return AppleDebugOutputsInfo.STARLARK_CONSTRUCTOR; |
| } |
| |
| @Override |
| public ImmutableMap<String, String> getAppleHostSystemEnv(XcodeConfigInfo xcodeConfig) { |
| return AppleConfiguration.getXcodeVersionEnv(xcodeConfig.getXcodeVersion()); |
| } |
| |
| @Override |
| public ImmutableMap<String, String> getTargetAppleEnvironment( |
| XcodeConfigInfo xcodeConfigApi, ApplePlatform platformApi) { |
| XcodeConfigInfo xcodeConfig = xcodeConfigApi; |
| ApplePlatform platform = (ApplePlatform) platformApi; |
| return AppleConfiguration.appleTargetPlatformEnv( |
| platform, xcodeConfig.getSdkVersionForPlatform(platform)); |
| } |
| |
| @Override |
| public SplitTransitionProviderApi getMultiArchSplitProvider() { |
| return new MultiArchSplitTransitionProvider(); |
| } |
| |
| @Override |
| // This method is registered statically for Starlark, and never called directly. |
| public ObjcProvider newObjcProvider(Dict<String, Object> kwargs, StarlarkThread thread) |
| throws EvalException { |
| ObjcProvider.StarlarkBuilder resultBuilder = |
| new ObjcProvider.StarlarkBuilder(thread.getSemantics()); |
| for (Map.Entry<String, Object> entry : kwargs.entrySet()) { |
| ObjcProvider.Key<?> key = ObjcProvider.getStarlarkKeyForString(entry.getKey()); |
| if (key != null) { |
| resultBuilder.addElementsFromStarlark(key, entry.getValue()); |
| } else { |
| switch (entry.getKey()) { |
| case "cc_library": |
| CcModule.checkPrivateStarlarkificationAllowlist(thread); |
| resultBuilder.uncheckedAddTransitive( |
| ObjcProvider.CC_LIBRARY, |
| ObjcProviderStarlarkConverters.convertToJava( |
| ObjcProvider.CC_LIBRARY, entry.getValue())); |
| break; |
| case "flag": |
| resultBuilder.add(ObjcProvider.FLAG, Flag.USES_CPP); |
| break; |
| case "strict_include": |
| resultBuilder.addStrictIncludeFromStarlark(entry.getValue()); |
| break; |
| case "providers": |
| resultBuilder.addProvidersFromStarlark(entry.getValue()); |
| break; |
| default: |
| throw Starlark.errorf(BAD_KEY_ERROR, entry.getKey()); |
| } |
| } |
| } |
| return resultBuilder.build(); |
| } |
| |
| @Override |
| public AppleDynamicFrameworkInfo newDynamicFrameworkProvider( |
| Object dylibBinary, |
| Object depsCcInfo, |
| ObjcProvider depsObjcProvider, |
| Object dynamicFrameworkDirs, |
| Object dynamicFrameworkFiles) |
| throws EvalException { |
| NestedSet<String> frameworkDirs = |
| Depset.noneableCast(dynamicFrameworkDirs, String.class, "framework_dirs"); |
| NestedSet<Artifact> frameworkFiles = |
| Depset.noneableCast(dynamicFrameworkFiles, Artifact.class, "framework_files"); |
| Artifact binary = (dylibBinary != Starlark.NONE) ? (Artifact) dylibBinary : null; |
| // TODO(b/252909384): Disallow Starlark.NONE once rules have been migrated to supply CcInfo. |
| CcInfo ccInfo = (depsCcInfo != Starlark.NONE) ? (CcInfo) depsCcInfo : CcInfo.EMPTY; |
| |
| return new AppleDynamicFrameworkInfo( |
| binary, ccInfo, depsObjcProvider, frameworkDirs, frameworkFiles); |
| } |
| |
| @Override |
| public AppleExecutableBinaryInfo newExecutableBinaryProvider( |
| Object executableBinary, Object depsCcInfo, ObjcProvider depsObjcProvider) |
| throws EvalException { |
| Artifact binary = (executableBinary != Starlark.NONE) ? (Artifact) executableBinary : null; |
| // TODO(b/252909384): Disallow Starlark.NONE once rules have been migrated to supply CcInfo. |
| CcInfo ccInfo = (depsCcInfo != Starlark.NONE) ? (CcInfo) depsCcInfo : CcInfo.EMPTY; |
| return new AppleExecutableBinaryInfo(binary, ccInfo, depsObjcProvider); |
| } |
| |
| @Override |
| public StructImpl linkMultiArchBinary( |
| StarlarkRuleContext starlarkRuleContext, |
| Object avoidDeps, |
| Sequence<?> extraLinkopts, |
| Sequence<?> extraLinkInputs, |
| StarlarkInt stamp, |
| StarlarkThread thread) |
| throws EvalException, InterruptedException { |
| try { |
| RuleContext ruleContext = starlarkRuleContext.getRuleContext(); |
| ImmutableList<TransitiveInfoCollection> avoidDepsList = |
| (avoidDeps != Starlark.NONE) |
| ? ImmutableList.copyOf( |
| Sequence.cast(avoidDeps, TransitiveInfoCollection.class, "avoid_deps")) |
| : ImmutableList.of(); |
| boolean isStampingEnabled = |
| isStampingEnabled(stamp.toInt("stamp"), ruleContext.getConfiguration()); |
| AppleLinkingOutputs linkingOutputs = |
| AppleBinary.linkMultiArchBinary( |
| ruleContext, |
| cppSemantics, |
| avoidDepsList, |
| ImmutableList.copyOf(Sequence.cast(extraLinkopts, String.class, "extra_linkopts")), |
| Sequence.cast(extraLinkInputs, Artifact.class, "extra_link_inputs"), |
| isStampingEnabled); |
| return createStarlarkLinkingOutputs(linkingOutputs, thread); |
| } catch (RuleErrorException | ActionConflictException exception) { |
| throw new EvalException(exception); |
| } |
| } |
| |
| @Override |
| public StructImpl linkMultiArchStaticLibrary( |
| StarlarkRuleContext starlarkRuleContext, StarlarkThread thread) |
| throws EvalException, InterruptedException { |
| try { |
| RuleContext ruleContext = starlarkRuleContext.getRuleContext(); |
| StarlarkFunction linkMultiArchLibrary = |
| (StarlarkFunction) |
| ruleContext.getStarlarkDefinedBuiltin("link_multi_arch_static_library"); |
| Dict<String, StructImpl> splitTargetTriplets = |
| MultiArchBinarySupport.getSplitTargetTripletFromCtads( |
| ruleContext.getSplitPrerequisiteConfiguredTargetAndTargets( |
| ObjcRuleClasses.CHILD_CONFIG_ATTR)); |
| return (StructImpl) |
| ruleContext.callStarlarkOrThrowRuleError( |
| linkMultiArchLibrary, |
| ImmutableList.of(), |
| ImmutableMap.of( |
| "ctx", |
| ruleContext.getStarlarkRuleContext(), |
| "split_target_triplets", |
| splitTargetTriplets)); |
| } catch (RuleErrorException exception) { |
| throw new EvalException(exception); |
| } |
| } |
| |
| @Override |
| public DottedVersion dottedVersion(String version) throws EvalException { |
| try { |
| return DottedVersion.fromString(version); |
| } catch (DottedVersion.InvalidDottedVersionException e) { |
| throw new EvalException(e.getMessage()); |
| } |
| } |
| |
| /** |
| * Returns the given value unless it is null, in which case the Starlark value {@code NONE} is |
| * returned. |
| */ |
| private Object valueOrNone(Object value) { |
| if (value != null) { |
| return value; |
| } |
| return Starlark.NONE; |
| } |
| |
| /** |
| * Creates a Starlark struct that contains the results of the {@code link_multi_arch_binary} |
| * function. |
| */ |
| private StructImpl createStarlarkLinkingOutputs( |
| AppleLinkingOutputs linkingOutputs, StarlarkThread thread) { |
| Provider linkingOutputConstructor = |
| new BuiltinProvider<StructImpl>("apple_linking_output", StructImpl.class) {}; |
| ImmutableList.Builder<StarlarkInfo> outputStructs = ImmutableList.builder(); |
| |
| for (AppleLinkingOutputs.LinkingOutput linkingOutput : linkingOutputs.getOutputs()) { |
| AppleLinkingOutputs.TargetTriplet targetTriplet = linkingOutput.getTargetTriplet(); |
| outputStructs.add( |
| StarlarkInfo.create( |
| linkingOutputConstructor, |
| ImmutableMap.<String, Object>builder() |
| .put("platform", targetTriplet.platform()) |
| .put("architecture", targetTriplet.architecture()) |
| .put("environment", targetTriplet.environment()) |
| .put("binary", linkingOutput.getBinary()) |
| .put("bitcode_symbols", valueOrNone(linkingOutput.getBitcodeSymbols())) |
| .put("dsym_binary", valueOrNone(linkingOutput.getDsymBinary())) |
| .put("linkmap", valueOrNone(linkingOutput.getLinkmap())) |
| .buildOrThrow(), |
| Location.BUILTIN)); |
| } |
| |
| // We have to transform the output group dictionary into one that contains StarlarkValues |
| // instead of plain NestedSets because the Starlark caller may want to return this directly from |
| // their implementation function. |
| Map<String, StarlarkValue> outputGroups = |
| Maps.transformValues(linkingOutputs.getOutputGroups(), v -> Depset.of(Artifact.TYPE, v)); |
| |
| ImmutableMap.Builder<String, Object> fields = ImmutableMap.builder(); |
| fields.put("objc", linkingOutputs.getDepsObjcProvider()); |
| fields.put("cc_info", linkingOutputs.getDepsCcInfo()); |
| fields.put("output_groups", Dict.copyOf(thread.mutability(), outputGroups)); |
| fields.put("outputs", StarlarkList.copyOf(thread.mutability(), outputStructs.build())); |
| |
| // TODO(b/110264170): Remove this field after clients have been migrated to use a provider |
| // defined in Starlark and propagated by rules_apple instead. |
| fields.put("debug_outputs_provider", linkingOutputs.getLegacyDebugOutputsProvider()); |
| |
| Provider linkingOutputsConstructor = |
| new BuiltinProvider<StructImpl>("apple_linking_outputs", StructImpl.class) {}; |
| return StarlarkInfo.create(linkingOutputsConstructor, fields.buildOrThrow(), Location.BUILTIN); |
| } |
| |
| private static boolean isStampingEnabled(int stamp, BuildConfigurationValue config) |
| throws EvalException { |
| if (stamp == 0) { |
| return false; |
| } |
| if (stamp == 1) { |
| return true; |
| } |
| if (stamp == -1) { |
| return config.stampBinaries(); |
| } |
| throw Starlark.errorf( |
| "stamp value %d is not supported; must be 0 (disabled), 1 (enabled), or -1 (default)", |
| stamp); |
| } |
| } |