| // Copyright 2014 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.rules.objc.ObjcProvider.IMPORTED_LIBRARY; |
| import static com.google.devtools.build.lib.rules.objc.ObjcProvider.LIBRARY; |
| |
| import com.google.common.annotations.VisibleForTesting; |
| import com.google.common.base.Optional; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.Iterables; |
| import com.google.devtools.build.lib.actions.Artifact; |
| import com.google.devtools.build.lib.analysis.ConfiguredTarget; |
| import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode; |
| import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder; |
| import com.google.devtools.build.lib.analysis.RuleContext; |
| import com.google.devtools.build.lib.analysis.RunfilesSupport; |
| import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; |
| import com.google.devtools.build.lib.rules.RuleConfiguredTargetFactory; |
| import com.google.devtools.build.lib.rules.apple.AppleConfiguration; |
| import com.google.devtools.build.lib.rules.apple.Platform; |
| import com.google.devtools.build.lib.rules.apple.Platform.PlatformType; |
| import com.google.devtools.build.lib.rules.objc.CompilationSupport.ExtraLinkArgs; |
| import com.google.devtools.build.lib.rules.objc.ObjcCommon.ResourceAttributes; |
| import com.google.devtools.build.lib.rules.objc.ReleaseBundlingSupport.LinkedBinary; |
| import com.google.devtools.build.lib.rules.test.InstrumentedFilesProvider; |
| |
| /** Implementation for rules that link binaries. */ |
| abstract class BinaryLinkingTargetFactory implements RuleConfiguredTargetFactory { |
| /** |
| * Indicates whether this binary generates an application bundle. If so, it causes the {@code |
| * infoplist} attribute to be read and a bundle to be added to the files-to-build. |
| */ |
| enum HasReleaseBundlingSupport { |
| YES, NO; |
| } |
| |
| private final HasReleaseBundlingSupport hasReleaseBundlingSupport; |
| private final XcodeProductType productType; |
| |
| protected BinaryLinkingTargetFactory( |
| HasReleaseBundlingSupport hasReleaseBundlingSupport, |
| XcodeProductType productType) { |
| this.hasReleaseBundlingSupport = hasReleaseBundlingSupport; |
| this.productType = productType; |
| } |
| |
| /** |
| * Returns extra linker arguments. Default implementation returns empty list. |
| * Subclasses can override and customize. |
| */ |
| protected ExtraLinkArgs getExtraLinkArgs(RuleContext ruleContext) { |
| return new ExtraLinkArgs(); |
| } |
| |
| @VisibleForTesting |
| static final String REQUIRES_AT_LEAST_ONE_LIBRARY_OR_SOURCE_FILE = |
| "At least one library dependency or source file is required."; |
| |
| @Override |
| public final ConfiguredTarget create(RuleContext ruleContext) |
| throws InterruptedException, RuleErrorException { |
| ProtobufSupport protoSupport = |
| new ProtobufSupport(ruleContext).registerGenerationActions().registerCompilationActions(); |
| |
| Optional<ObjcProvider> protosObjcProvider = protoSupport.getObjcProvider(); |
| Optional<XcodeProvider> protosXcodeProvider = protoSupport.getXcodeProvider(); |
| |
| ObjcCommon common = common(ruleContext, protosObjcProvider); |
| |
| ObjcProvider objcProvider = common.getObjcProvider(); |
| assertLibraryOrSources(objcProvider, ruleContext); |
| |
| XcodeProvider.Builder xcodeProviderBuilder = |
| new XcodeProvider.Builder().addPropagatedDependencies(protosXcodeProvider.asSet()); |
| |
| IntermediateArtifacts intermediateArtifacts = |
| ObjcRuleClasses.intermediateArtifacts(ruleContext); |
| |
| NestedSetBuilder<Artifact> filesToBuild = |
| NestedSetBuilder.<Artifact>stableOrder() |
| .add(intermediateArtifacts.strippedSingleArchitectureBinary()); |
| |
| new ResourceSupport(ruleContext) |
| .validateAttributes() |
| .addXcodeSettings(xcodeProviderBuilder); |
| |
| ruleContext.assertNoErrors(); |
| |
| J2ObjcMappingFileProvider j2ObjcMappingFileProvider = J2ObjcMappingFileProvider.union( |
| ruleContext.getPrerequisites("deps", Mode.TARGET, J2ObjcMappingFileProvider.class)); |
| J2ObjcEntryClassProvider j2ObjcEntryClassProvider = new J2ObjcEntryClassProvider.Builder() |
| .addTransitive( |
| ruleContext.getPrerequisites("deps", Mode.TARGET, J2ObjcEntryClassProvider.class)) |
| .build(); |
| |
| CompilationSupport compilationSupport = CompilationSupport.create(ruleContext) |
| .validateAttributes() |
| .addXcodeSettings(xcodeProviderBuilder, common) |
| .registerCompileAndArchiveActions(common) |
| .registerFullyLinkAction( |
| common.getObjcProvider(), |
| ruleContext.getImplicitOutputArtifact(CompilationSupport.FULLY_LINKED_LIB)) |
| .registerLinkActions( |
| objcProvider, |
| j2ObjcMappingFileProvider, |
| j2ObjcEntryClassProvider, |
| getExtraLinkArgs(ruleContext), |
| ImmutableList.<Artifact>of(), |
| DsymOutputType.APP); |
| |
| Optional<XcTestAppProvider> xcTestAppProvider; |
| Optional<RunfilesSupport> maybeRunfilesSupport = Optional.absent(); |
| switch (hasReleaseBundlingSupport) { |
| case YES: |
| AppleConfiguration appleConfiguration = ruleContext.getFragment(AppleConfiguration.class); |
| // TODO(bazel-team): Remove once all bundle users are migrated to ios_application. |
| ReleaseBundlingSupport releaseBundlingSupport = |
| new ReleaseBundlingSupport( |
| ruleContext, |
| objcProvider, |
| LinkedBinary.LOCAL_AND_DEPENDENCIES, |
| ReleaseBundlingSupport.APP_BUNDLE_DIR_FORMAT, |
| appleConfiguration.getMinimumOsForPlatformType(PlatformType.IOS), |
| appleConfiguration.getSingleArchPlatform()); |
| releaseBundlingSupport |
| .registerActions(DsymOutputType.APP) |
| .addXcodeSettings(xcodeProviderBuilder) |
| .addFilesToBuild(filesToBuild, Optional.of(DsymOutputType.APP)) |
| .validateResources() |
| .validateAttributes(); |
| |
| xcTestAppProvider = Optional.of(releaseBundlingSupport.xcTestAppProvider()); |
| if (appleConfiguration.getMultiArchPlatform(PlatformType.IOS) == Platform.IOS_SIMULATOR) { |
| Artifact runnerScript = intermediateArtifacts.runnerScript(); |
| Artifact ipaFile = ruleContext.getImplicitOutputArtifact(ReleaseBundlingSupport.IPA); |
| releaseBundlingSupport.registerGenerateRunnerScriptAction(runnerScript, ipaFile); |
| maybeRunfilesSupport = Optional.of(releaseBundlingSupport.runfilesSupport(runnerScript)); |
| } |
| break; |
| case NO: |
| xcTestAppProvider = Optional.absent(); |
| break; |
| default: |
| throw new AssertionError(); |
| } |
| |
| XcodeSupport xcodeSupport = new XcodeSupport(ruleContext) |
| // TODO(bazel-team): Use LIBRARY_STATIC as parameter instead of APPLICATION once objc_binary |
| // no longer creates an application bundle |
| .addXcodeSettings(xcodeProviderBuilder, objcProvider, productType) |
| .addDependencies(xcodeProviderBuilder, new Attribute("bundles", Mode.TARGET)) |
| .addDependencies(xcodeProviderBuilder, new Attribute("deps", Mode.TARGET)) |
| .addNonPropagatedDependencies( |
| xcodeProviderBuilder, new Attribute("non_propagated_deps", Mode.TARGET)) |
| .addFilesToBuild(filesToBuild); |
| |
| if (productType != XcodeProductType.LIBRARY_STATIC) { |
| xcodeSupport.generateCompanionLibXcodeTarget(xcodeProviderBuilder); |
| } |
| XcodeProvider xcodeProvider = xcodeProviderBuilder.build(); |
| xcodeSupport.registerActions(xcodeProvider); |
| |
| RuleConfiguredTargetBuilder targetBuilder = |
| ObjcRuleClasses.ruleConfiguredTarget(ruleContext, filesToBuild.build()) |
| .addProvider(XcodeProvider.class, xcodeProvider) |
| .addProvider(ObjcProvider.class, objcProvider) |
| .addProvider( |
| InstrumentedFilesProvider.class, |
| compilationSupport.getInstrumentedFilesProvider(common)); |
| if (xcTestAppProvider.isPresent()) { |
| // TODO(bazel-team): Stop exporting an XcTestAppProvider once objc_binary no longer creates an |
| // application bundle. |
| targetBuilder.addProvider(XcTestAppProvider.class, xcTestAppProvider.get()); |
| } |
| if (maybeRunfilesSupport.isPresent()) { |
| RunfilesSupport runfilesSupport = maybeRunfilesSupport.get(); |
| targetBuilder.setRunfilesSupport(runfilesSupport, runfilesSupport.getExecutable()); |
| } |
| configureTarget(targetBuilder, ruleContext); |
| return targetBuilder.build(); |
| } |
| |
| private void assertLibraryOrSources(ObjcProvider objcProvider, RuleContext ruleContext) |
| throws RuleErrorException { |
| if (Iterables.isEmpty(objcProvider.get(LIBRARY)) // Includes sources from this target. |
| && Iterables.isEmpty(objcProvider.get(IMPORTED_LIBRARY))) { |
| ruleContext.throwWithRuleError(REQUIRES_AT_LEAST_ONE_LIBRARY_OR_SOURCE_FILE); |
| } |
| } |
| |
| private ObjcCommon common(RuleContext ruleContext, Optional<ObjcProvider> protosObjcProvider) { |
| IntermediateArtifacts intermediateArtifacts = |
| ObjcRuleClasses.intermediateArtifacts(ruleContext); |
| CompilationArtifacts compilationArtifacts = |
| CompilationSupport.compilationArtifacts(ruleContext); |
| |
| ObjcCommon.Builder builder = |
| new ObjcCommon.Builder(ruleContext) |
| .setCompilationAttributes( |
| CompilationAttributes.Builder.fromRuleContext(ruleContext).build()) |
| .setCompilationArtifacts(compilationArtifacts) |
| .setResourceAttributes(new ResourceAttributes(ruleContext)) |
| .addDefines(ruleContext.getTokenizedStringListAttr("defines")) |
| .addDeps(ruleContext.getPrerequisites("deps", Mode.TARGET)) |
| .addRuntimeDeps(ruleContext.getPrerequisites("runtime_deps", Mode.TARGET)) |
| .addDeps(ruleContext.getPrerequisites("bundles", Mode.TARGET)) |
| .addDepObjcProviders(protosObjcProvider.asSet()) |
| .addNonPropagatedDepObjcProviders( |
| ruleContext.getPrerequisites( |
| "non_propagated_deps", Mode.TARGET, ObjcProvider.class)) |
| .setIntermediateArtifacts(intermediateArtifacts) |
| .setAlwayslink(false) |
| .setHasModuleMap() |
| .setLinkedBinary(intermediateArtifacts.strippedSingleArchitectureBinary()); |
| |
| if (ObjcRuleClasses.objcConfiguration(ruleContext).generateDsym()) { |
| builder.addDebugArtifacts(DsymOutputType.APP); |
| } |
| |
| if (ObjcRuleClasses.objcConfiguration(ruleContext).generateLinkmap()) { |
| builder.setLinkmapFile(intermediateArtifacts.linkmap()); |
| } |
| |
| return builder.build(); |
| } |
| |
| /** |
| * Performs additional configuration of the target. The default implementation does nothing, but |
| * subclasses may override it to add logic. |
| */ |
| protected void configureTarget(RuleConfiguredTargetBuilder target, RuleContext ruleContext) {}; |
| } |