// 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.auto.value.AutoValue; import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableListMultimap; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Streams; import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.analysis.RuleContext; import com.google.devtools.build.lib.analysis.TransitiveInfoCollection; import com.google.devtools.build.lib.analysis.TransitiveInfoProvider; import com.google.devtools.build.lib.analysis.config.BuildConfiguration; import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget.Mode; import com.google.devtools.build.lib.analysis.platform.ToolchainInfo; import com.google.devtools.build.lib.collect.nestedset.NestedSet; import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; import com.google.devtools.build.lib.packages.BuiltinProvider; import com.google.devtools.build.lib.packages.Info; import com.google.devtools.build.lib.packages.NativeProvider; import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException; import com.google.devtools.build.lib.rules.apple.ApplePlatform; import com.google.devtools.build.lib.rules.cpp.CcLinkingInfo; import com.google.devtools.build.lib.rules.cpp.CcToolchainProvider; import com.google.devtools.build.lib.rules.objc.CompilationSupport.ExtraLinkArgs; import com.google.devtools.build.lib.rules.proto.ProtoSourcesProvider; import com.google.devtools.build.lib.skyframe.ConfiguredTargetAndData; import java.util.List; import java.util.Map; import java.util.Set; /** * Support utility for creating multi-arch Apple binaries. */ public class MultiArchBinarySupport { private final RuleContext ruleContext; /** * Returns all child configurations for this multi-arch target, mapped to the toolchains that they * should use. */ static ImmutableMap getChildConfigurationsAndToolchains( RuleContext ruleContext) { // This is currently a hack to obtain all child configurations regardless of the attribute // values of this rule -- this rule does not currently use the actual info provided by // this attribute. b/28403953 tracks cc toolchain usage. ImmutableListMultimap configToProvider = ruleContext.getPrerequisitesByConfiguration( ObjcRuleClasses.CHILD_CONFIG_ATTR, Mode.SPLIT, ToolchainInfo.PROVIDER); ImmutableMap.Builder result = ImmutableMap.builder(); for (BuildConfiguration config : configToProvider.keySet()) { CcToolchainProvider toolchain = (CcToolchainProvider) Iterables.getOnlyElement(configToProvider.get(config)); result.put(config, toolchain); } return result.build(); } /** * A tuple of values about dependency trees in a specific child configuration. */ @AutoValue abstract static class DependencySpecificConfiguration { static DependencySpecificConfiguration create( BuildConfiguration config, CcToolchainProvider toolchain, ObjcProvider objcLinkProvider, ObjcProvider objcPropagateProvider) { return new AutoValue_MultiArchBinarySupport_DependencySpecificConfiguration( config, toolchain, objcLinkProvider, objcPropagateProvider); } /** * Returns the child configuration for this tuple. */ abstract BuildConfiguration config(); /** * Returns the cc toolchain for this configuration. */ abstract CcToolchainProvider toolchain(); /** * Returns the {@link ObjcProvider} to use as input to the support controlling link actoins; * dylib symbols should be subtracted from this provider. */ abstract ObjcProvider objcLinkProvider(); /** * Returns the {@link ObjcProvider} to propagate up to dependers; this will not have dylib * symbols subtracted, thus signaling that this target is still responsible for those symbols. */ abstract ObjcProvider objcProviderWithDylibSymbols(); } /** * @param ruleContext the current rule context */ public MultiArchBinarySupport(RuleContext ruleContext) { this.ruleContext = ruleContext; } /** * Registers actions to create a multi-arch Apple binary. * * @param platform the platform for which the binary is targeted * @param extraLinkArgs the extra linker args to add to link actions linking single-architecture * binaries together * @param dependencySpecificConfigurations a set of {@link DependencySpecificConfiguration} that * corresponds to child configurations for this target. Can be obtained via {@link * #getDependencySpecificConfigurations} * @param extraLinkInputs the extra linker inputs to be made available during link actions * @param configToDepsCollectionMap a multimap from dependency configuration to the list of * provider collections which are propagated from the dependencies of that configuration * @param outputMapCollector a map to which output groups created by compile action generation are * added * @return a set containing all single-architecture binaries that are linked from this call * @throws RuleErrorException if there are attribute errors in the current rule context */ public NestedSet registerActions( ApplePlatform platform, ExtraLinkArgs extraLinkArgs, Set dependencySpecificConfigurations, Iterable extraLinkInputs, ImmutableListMultimap configToDepsCollectionMap, Map> outputMapCollector) throws RuleErrorException, InterruptedException { NestedSetBuilder binariesToLipo = NestedSetBuilder.stableOrder(); for (DependencySpecificConfiguration dependencySpecificConfiguration : dependencySpecificConfigurations) { IntermediateArtifacts intermediateArtifacts = ObjcRuleClasses.intermediateArtifacts( ruleContext, dependencySpecificConfiguration.config()); Iterable infoCollections = configToDepsCollectionMap.get(dependencySpecificConfiguration.config()); J2ObjcMappingFileProvider j2ObjcMappingFileProvider = J2ObjcMappingFileProvider.union( getTypedProviders(infoCollections, J2ObjcMappingFileProvider.class)); J2ObjcEntryClassProvider j2ObjcEntryClassProvider = new J2ObjcEntryClassProvider.Builder() .addTransitive(getTypedProviders(infoCollections, J2ObjcEntryClassProvider.class)) .build(); binariesToLipo.add(intermediateArtifacts.strippedSingleArchitectureBinary()); ObjcProvider objcProvider = dependencySpecificConfiguration.objcLinkProvider(); CompilationArtifacts compilationArtifacts = new CompilationArtifacts.Builder() .setIntermediateArtifacts(ObjcRuleClasses.intermediateArtifacts( ruleContext, dependencySpecificConfiguration.config())) .build(); CompilationSupport compilationSupport = new CompilationSupport.Builder() .setRuleContext(ruleContext) .setConfig(dependencySpecificConfiguration.config()) .setToolchainProvider(dependencySpecificConfiguration.toolchain()) .setOutputGroupCollector(outputMapCollector) .build(); compilationSupport .registerCompileAndArchiveActions( compilationArtifacts, objcProvider, dependencySpecificConfiguration.toolchain()) .registerLinkActions( objcProvider, j2ObjcMappingFileProvider, j2ObjcEntryClassProvider, extraLinkArgs, extraLinkInputs, dependencySpecificConfiguration.toolchain()) .validateAttributes(); ruleContext.assertNoErrors(); } return binariesToLipo.build(); } /** * Returns a set of {@link DependencySpecificConfiguration} instances that comprise all * information about the dependencies for each child configuration. This can be used both to * register actions in {@link #registerActions} and collect provider information to be propagated * upstream. * * @param childConfigurationsAndToolchains the set of configurations and toolchains for which * dependencies of the current rule are built * @param configToDepsCollectionMap a map from child configuration to providers that "deps" of the * current rule have propagated in that configuration * @param dylibProviders {@link TransitiveInfoCollection}s that dynamic library dependencies of * the current rule have propagated * @throws RuleErrorException if there are attribute errors in the current rule context */ public ImmutableSet getDependencySpecificConfigurations( Map childConfigurationsAndToolchains, ImmutableListMultimap configToDepsCollectionMap, ImmutableListMultimap configToCTATDepsCollectionMap, Iterable dylibProviders) throws RuleErrorException, InterruptedException { Iterable dylibObjcProviders = getDylibObjcProviders(dylibProviders); Iterable dylibProtoProviders = getTypedProviders(dylibProviders, ObjcProtoProvider.SKYLARK_CONSTRUCTOR); NestedSet protosToAvoid = protoArtifactsToAvoid(dylibProtoProviders); ImmutableSet.Builder childInfoBuilder = ImmutableSet.builder(); for (BuildConfiguration childConfig : childConfigurationsAndToolchains.keySet()) { Iterable infoCollections = configToDepsCollectionMap.get(childConfig); Iterable depProtoProviders = getTypedProviders(infoCollections, ObjcProtoProvider.SKYLARK_CONSTRUCTOR); Optional protosObjcProvider; if (ObjcRuleClasses.objcConfiguration(ruleContext).enableAppleBinaryNativeProtos()) { ProtobufSupport protoSupport = new ProtobufSupport( ruleContext, childConfig, protosToAvoid, ImmutableList.of(), depProtoProviders, ProtobufSupport.getTransitivePortableProtoFilters(depProtoProviders), childConfigurationsAndToolchains.get(childConfig)) .registerGenerationActions() .registerCompilationActions(); protosObjcProvider = protoSupport.getObjcProvider(); } else { protosObjcProvider = Optional.absent(); } IntermediateArtifacts intermediateArtifacts = ObjcRuleClasses.intermediateArtifacts(ruleContext, childConfig); Iterable additionalDepProviders = Iterables.concat( dylibObjcProviders, protosObjcProvider.asSet()); ObjcCommon common = common( ruleContext, childConfig, intermediateArtifacts, nullToEmptyList(configToCTATDepsCollectionMap.get(childConfig)), additionalDepProviders); ObjcProvider objcProviderWithDylibSymbols = common.getObjcProvider(); ObjcProvider objcProvider = objcProviderWithDylibSymbols.subtractSubtrees( dylibObjcProviders, ImmutableList.of()); childInfoBuilder.add( DependencySpecificConfiguration.create( childConfig, childConfigurationsAndToolchains.get(childConfig), objcProvider, objcProviderWithDylibSymbols)); } return childInfoBuilder.build(); } private static Iterable getDylibObjcProviders( Iterable transitiveInfoCollections) { // Dylibs. Iterable frameworkObjcProviders = Streams.stream(getTypedProviders(transitiveInfoCollections, AppleDynamicFrameworkInfo.SKYLARK_CONSTRUCTOR)) .map(frameworkProvider -> frameworkProvider.getDepsObjcProvider()) .collect(ImmutableList.toImmutableList()); // Bundle Loaders. Iterable executableObjcProviders = Streams.stream(getTypedProviders(transitiveInfoCollections, AppleExecutableBinaryInfo.SKYLARK_CONSTRUCTOR)) .map(frameworkProvider -> frameworkProvider.getDepsObjcProvider()) .collect(ImmutableList.toImmutableList()); return Iterables.concat(frameworkObjcProviders, executableObjcProviders, getTypedProviders(transitiveInfoCollections, ObjcProvider.SKYLARK_CONSTRUCTOR)); } private ObjcCommon common( RuleContext ruleContext, BuildConfiguration buildConfiguration, IntermediateArtifacts intermediateArtifacts, List propagatedConfiguredTargetAndDataDeps, Iterable additionalDepProviders) throws InterruptedException { ObjcCommon.Builder commonBuilder = new ObjcCommon.Builder(ruleContext, buildConfiguration) .setCompilationAttributes( CompilationAttributes.Builder.fromRuleContext(ruleContext).build()) .addDeps(propagatedConfiguredTargetAndDataDeps) .addDepObjcProviders(additionalDepProviders) .setIntermediateArtifacts(intermediateArtifacts) .setAlwayslink(false) .setLinkedBinary(intermediateArtifacts.strippedSingleArchitectureBinary()); return commonBuilder.build(); } private List nullToEmptyList(List inputList) { return inputList != null ? inputList : ImmutableList.of(); } private static NestedSet protoArtifactsToAvoid( Iterable avoidedProviders) { NestedSetBuilder avoidArtifacts = NestedSetBuilder.stableOrder(); for (ObjcProtoProvider avoidProvider : avoidedProviders) { for (NestedSet avoidProviderOutputGroup : avoidProvider.getProtoGroups()) { avoidArtifacts.addTransitive(avoidProviderOutputGroup); } } return avoidArtifacts.build(); } @Deprecated // Use BuiltinProvider instead. private static Iterable getTypedProviders( Iterable infoCollections, NativeProvider providerClass) { return Streams.stream(infoCollections) .filter(infoCollection -> infoCollection.get(providerClass) != null) .map(infoCollection -> infoCollection.get(providerClass)) .collect(ImmutableList.toImmutableList()); } private static Iterable getTypedProviders( Iterable infoCollections, BuiltinProvider providerClass) { return Streams.stream(infoCollections) .filter(infoCollection -> infoCollection.get(providerClass) != null) .map(infoCollection -> infoCollection.get(providerClass)) .collect(ImmutableList.toImmutableList()); } private static Iterable getTypedProviders( Iterable infoCollections, Class providerClass) { return Streams.stream(infoCollections) .filter(infoCollection -> infoCollection.getProvider(providerClass) != null) .map(infoCollection -> infoCollection.getProvider(providerClass)) .collect(ImmutableList.toImmutableList()); } }