blob: e212eb8d860b1f0cd355fb8235594df81cade2d8 [file] [log] [blame]
// 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.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.Iterables;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode;
import com.google.devtools.build.lib.analysis.RuleContext;
import com.google.devtools.build.lib.analysis.TransitiveInfoCollection;
import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
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.RuleClass.ConfiguredTargetFactory.RuleErrorException;
import com.google.devtools.build.lib.rules.apple.Platform;
import com.google.devtools.build.lib.rules.cpp.CcLinkParamsProvider;
import com.google.devtools.build.lib.rules.objc.CompilationSupport.ExtraLinkArgs;
import com.google.devtools.build.lib.rules.objc.ObjcCommon.ResourceAttributes;
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;
/**
* @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 configurationToObjcProvider a map from from dependency configuration to the
* {@link ObjcProvider} which comprises all information about the dependencies in that
* configuration. Can be obtained via {@link #objcProviderByDepConfiguration}
* @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 outputLipoBinary the artifact (lipo'ed binary) which should be output as a result of
* this support
* @throws RuleErrorException if there are attribute errors in the current rule context
*/
public void registerActions(
Platform platform,
ExtraLinkArgs extraLinkArgs,
Map<BuildConfiguration, ObjcProvider> configurationToObjcProvider,
Iterable<Artifact> extraLinkInputs,
ImmutableListMultimap<BuildConfiguration, TransitiveInfoCollection> configToDepsCollectionMap,
Artifact outputLipoBinary)
throws RuleErrorException, InterruptedException {
NestedSetBuilder<Artifact> binariesToLipo =
NestedSetBuilder.<Artifact>stableOrder();
for (BuildConfiguration childConfig : configurationToObjcProvider.keySet()) {
IntermediateArtifacts intermediateArtifacts =
ObjcRuleClasses.intermediateArtifacts(ruleContext, childConfig);
ImmutableList.Builder<J2ObjcMappingFileProvider> j2ObjcMappingFileProviders =
ImmutableList.builder();
J2ObjcEntryClassProvider.Builder j2ObjcEntryClassProviderBuilder =
new J2ObjcEntryClassProvider.Builder();
for (TransitiveInfoCollection dep : configToDepsCollectionMap.get(childConfig)) {
if (dep.getProvider(J2ObjcMappingFileProvider.class) != null) {
j2ObjcMappingFileProviders.add(dep.getProvider(J2ObjcMappingFileProvider.class));
}
if (dep.getProvider(J2ObjcEntryClassProvider.class) != null) {
j2ObjcEntryClassProviderBuilder.addTransitive(
dep.getProvider(J2ObjcEntryClassProvider.class));
}
}
J2ObjcMappingFileProvider j2ObjcMappingFileProvider =
J2ObjcMappingFileProvider.union(j2ObjcMappingFileProviders.build());
J2ObjcEntryClassProvider j2ObjcEntryClassProvider = j2ObjcEntryClassProviderBuilder.build();
binariesToLipo.add(intermediateArtifacts.strippedSingleArchitectureBinary());
ObjcProvider objcProvider = configurationToObjcProvider.get(childConfig);
CompilationArtifacts compilationArtifacts =
CompilationSupport.compilationArtifacts(
ruleContext, ObjcRuleClasses.intermediateArtifacts(ruleContext, childConfig));
CompilationSupport.createForConfig(ruleContext, childConfig)
.registerCompileAndArchiveActions(compilationArtifacts, objcProvider)
.registerLinkActions(
objcProvider,
j2ObjcMappingFileProvider,
j2ObjcEntryClassProvider,
extraLinkArgs,
extraLinkInputs,
DsymOutputType.APP)
.validateAttributes();
ruleContext.assertNoErrors();
}
new LipoSupport(ruleContext)
.registerCombineArchitecturesAction(
binariesToLipo.build(),
outputLipoBinary,
platform);
}
/**
* Returns a map from from dependency configuration to the {@link ObjcCommon} which comprises all
* information about the dependencies in that configuration. This can be used both to register
* actions in {@link #registerActions} and collect provider information to be propagated upstream.
*
* @param childConfigurations the set of configurations in 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 configurationToNonPropagatedObjcMap a map from child configuration to providers that
* "non_propagated_deps" of the current rule have propagated in that configuration
* @param dylibObjcProviders {@link ObjcProvider}s that dynamic library dependencies of the
* current rule have propagated
* @param dylibProtoProviders {@link ObjcProtoProvider} providers that dynamic library
* dependencies of the current rule have propagated
* @param bundleLoaderObjcProvider Optional ObjcProvider containing artifacts and paths to be
* included in this binary's compilation actions
* @throws RuleErrorException if there are attribute errors in the current rule context
*/
public Map<BuildConfiguration, ObjcProvider> objcProviderByDepConfiguration(
Set<BuildConfiguration> childConfigurations,
ImmutableListMultimap<BuildConfiguration, TransitiveInfoCollection> configToDepsCollectionMap,
ImmutableListMultimap<BuildConfiguration, ObjcProvider> configurationToNonPropagatedObjcMap,
Iterable<ObjcProvider> dylibObjcProviders,
Iterable<ObjcProtoProvider> dylibProtoProviders)
throws RuleErrorException, InterruptedException {
ImmutableMap.Builder<BuildConfiguration, ObjcProvider> configurationToObjcProviderBuilder =
ImmutableMap.builder();
for (BuildConfiguration childConfig : childConfigurations) {
Optional<ObjcProvider> protosObjcProvider;
if (ObjcRuleClasses.objcConfiguration(ruleContext).enableAppleBinaryNativeProtos()) {
ProtobufSupport protoSupport =
new ProtobufSupport(ruleContext, childConfig,
protoArtifactsToAvoid(dylibProtoProviders))
.registerGenerationActions()
.registerCompilationActions();
protosObjcProvider = protoSupport.getObjcProvider();
} else {
protosObjcProvider = Optional.absent();
}
IntermediateArtifacts intermediateArtifacts =
ObjcRuleClasses.intermediateArtifacts(ruleContext, childConfig);
Iterable<ObjcProvider> additionalDepProviders =
Iterables.concat(
dylibObjcProviders,
ruleContext.getPrerequisites("bundles", Mode.TARGET, ObjcProvider.class),
protosObjcProvider.asSet());
ObjcCommon common =
common(
ruleContext,
childConfig,
intermediateArtifacts,
nullToEmptyList(configToDepsCollectionMap.get(childConfig)),
nullToEmptyList(configurationToNonPropagatedObjcMap.get(childConfig)),
additionalDepProviders);
ObjcProvider objcProvider = common.getObjcProvider().subtractSubtrees(dylibObjcProviders,
ImmutableList.<CcLinkParamsProvider>of());
configurationToObjcProviderBuilder.put(childConfig, objcProvider);
}
return configurationToObjcProviderBuilder.build();
}
private ObjcCommon common(
RuleContext ruleContext,
BuildConfiguration buildConfiguration,
IntermediateArtifacts intermediateArtifacts,
List<TransitiveInfoCollection> propagatedDeps,
List<ObjcProvider> nonPropagatedObjcDeps,
Iterable<ObjcProvider> additionalDepProviders) {
CompilationArtifacts compilationArtifacts =
CompilationSupport.compilationArtifacts(ruleContext, intermediateArtifacts);
ObjcCommon.Builder commonBuilder = new ObjcCommon.Builder(ruleContext, buildConfiguration)
.setCompilationAttributes(
CompilationAttributes.Builder.fromRuleContext(ruleContext).build())
.setCompilationArtifacts(compilationArtifacts)
.setResourceAttributes(new ResourceAttributes(ruleContext))
.addDefines(ruleContext.getTokenizedStringListAttr("defines"))
.addDeps(propagatedDeps)
.addDepObjcProviders(additionalDepProviders)
.addNonPropagatedDepObjcProviders(nonPropagatedObjcDeps)
.setIntermediateArtifacts(intermediateArtifacts)
.setAlwayslink(false)
// TODO(b/29152500): Enable module map generation.
.setLinkedBinary(intermediateArtifacts.strippedSingleArchitectureBinary());
if (ObjcRuleClasses.objcConfiguration(ruleContext).generateDsym()) {
commonBuilder.addDebugArtifacts(DsymOutputType.APP);
}
return commonBuilder.build();
}
private <T> List<T> nullToEmptyList(List<T> inputList) {
return inputList != null ? inputList : ImmutableList.<T>of();
}
private static NestedSet<Artifact> protoArtifactsToAvoid(
Iterable<ObjcProtoProvider> avoidedProviders) {
NestedSetBuilder<Artifact> avoidArtifacts = NestedSetBuilder.stableOrder();
for (ObjcProtoProvider avoidProvider : avoidedProviders) {
for (NestedSet<Artifact> avoidProviderOutputGroup : avoidProvider.getProtoGroups()) {
avoidArtifacts.addTransitive(avoidProviderOutputGroup);
}
}
return avoidArtifacts.build();
}
}