| // 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.common.base.Preconditions.checkNotNull; |
| import static com.google.devtools.build.lib.rules.objc.ObjcCommon.uniqueContainers; |
| import static com.google.devtools.build.lib.rules.objc.ObjcProvider.MERGE_ZIP; |
| import static com.google.devtools.build.lib.rules.objc.ObjcProvider.STATIC_FRAMEWORK_DIR; |
| import static com.google.devtools.build.lib.rules.objc.ObjcProvider.STATIC_FRAMEWORK_FILE; |
| |
| import com.google.common.annotations.VisibleForTesting; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableMap; |
| import com.google.common.collect.ImmutableSet; |
| import com.google.common.collect.Ordering; |
| import com.google.devtools.build.lib.actions.Artifact; |
| 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.actions.SymlinkAction; |
| import com.google.devtools.build.lib.cmdline.Label; |
| import com.google.devtools.build.lib.rules.apple.AppleConfiguration; |
| import com.google.devtools.build.lib.rules.apple.AppleConfiguration.ConfigurationDistinguisher; |
| import com.google.devtools.build.lib.rules.apple.DottedVersion; |
| import com.google.devtools.build.lib.rules.apple.Platform.PlatformType; |
| import com.google.devtools.build.lib.rules.cpp.CcCommon; |
| import com.google.devtools.build.lib.util.Pair; |
| import com.google.devtools.build.lib.vfs.PathFragment; |
| |
| /** |
| * Implementation for {@code ios_framework}. |
| */ |
| public class IosFramework extends ReleaseBundlingTargetFactory { |
| |
| @VisibleForTesting |
| static final DottedVersion MINIMUM_OS_VERSION = DottedVersion.fromString("8.0"); |
| |
| public IosFramework() { |
| super( |
| ReleaseBundlingSupport.FRAMEWORK_BUNDLE_DIR_FORMAT, |
| XcodeProductType.FRAMEWORK, |
| ImmutableSet.of(new Attribute("binary", Mode.SPLIT)), |
| ConfigurationDistinguisher.FRAMEWORK); |
| } |
| |
| @Override |
| protected String bundleName(RuleContext ruleContext) { |
| String frameworkName = null; |
| |
| for (IosFrameworkProvider provider : |
| ruleContext.getPrerequisites("binary", Mode.SPLIT, IosFrameworkProvider.class)) { |
| frameworkName = provider.getFrameworkName(); |
| } |
| |
| |
| return checkNotNull(frameworkName); |
| } |
| |
| @Override |
| protected DottedVersion bundleMinimumOsVersion(RuleContext ruleContext) { |
| // Frameworks are not accepted by Apple below version 8.0. While applications built with a |
| // minimum iOS version of less than 8.0 may contain frameworks in their bundle, the framework |
| // itself needs to be built with 8.0 or higher. This logic overrides (if necessary) any |
| // flag-set minimum iOS version for framework only so that this requirement is not violated. |
| DottedVersion fromFlag = ruleContext.getFragment(AppleConfiguration.class) |
| .getMinimumOsForPlatformType(PlatformType.IOS); |
| return Ordering.natural().max(fromFlag, MINIMUM_OS_VERSION); |
| } |
| |
| /** |
| * Returns a map of original {@code Artifact} to symlinked {@code Artifact} inside framework |
| * bundle. |
| */ |
| private ImmutableMap<Artifact, Artifact> getExtraArtifacts(RuleContext ruleContext) { |
| IntermediateArtifacts intermediateArtifacts = |
| ObjcRuleClasses.intermediateArtifacts(ruleContext); |
| |
| ImmutableList<Pair<Artifact, Label>> headers = |
| ImmutableList.copyOf(CcCommon.getHeaders(ruleContext)); |
| |
| ImmutableMap.Builder<Artifact, Artifact> builder = new ImmutableMap.Builder<>(); |
| |
| // Create framework binary |
| Artifact frameworkBinary = |
| outputArtifact(ruleContext, new PathFragment(bundleName(ruleContext))); |
| builder.put(intermediateArtifacts.combinedArchitectureBinary(), frameworkBinary); |
| |
| // Create framework headers |
| for (Pair<Artifact, Label> header : headers) { |
| Artifact frameworkHeader = |
| outputArtifact(ruleContext, new PathFragment("Headers/" + header.first.getFilename())); |
| |
| builder.put(header.first, frameworkHeader); |
| } |
| |
| return builder.build(); |
| } |
| |
| @Override |
| protected ObjcProvider exposedObjcProvider( |
| RuleContext ruleContext, ReleaseBundlingSupport releaseBundlingSupport) |
| throws InterruptedException { |
| // Assemble framework binary and headers in the label-scoped location, so that it's possible to |
| // pass -F X.framework to the compiler and -framework X to the linker. This mimics usage of |
| // frameworks when built from Xcode. |
| |
| // To do this, we symlink all required artifacts into destination and pass them to |
| // FRAMEWORK_IMPORTS list, thus utilizing ObjcFramework rule to do the work of propagating |
| // them correctly. |
| Iterable<Artifact> frameworkImports = getExtraArtifacts(ruleContext).values(); |
| |
| ObjcProvider frameworkProvider = |
| new ObjcProvider.Builder() |
| .add(MERGE_ZIP, ruleContext.getImplicitOutputArtifact(ReleaseBundlingSupport.IPA)) |
| // TODO(dmishe): The framework returned by this rule is dynamic, not static. But because |
| // this rule (incorrectly?) propagates its contents as a bundle (see "IPA" above) we |
| // cannot declare its files as dynamic framework files because they would then be copied |
| // into the final IPA, conflicting with the exported bundle. Instead we propagate using |
| // the static frameworks key which will see the files correctly added to compile and |
| // linker actions but not added to the final bundle. |
| .addAll(STATIC_FRAMEWORK_FILE, frameworkImports) |
| .addAll( |
| STATIC_FRAMEWORK_DIR, |
| uniqueContainers(frameworkImports, ObjcCommon.FRAMEWORK_CONTAINER_TYPE)) |
| .build(); |
| |
| return frameworkProvider; |
| } |
| |
| @Override |
| protected void configureTarget( |
| RuleConfiguredTargetBuilder target, |
| RuleContext ruleContext, |
| ReleaseBundlingSupport releaseBundlingSupport) { |
| // Create generating actions for framework artifacts |
| for (ImmutableMap.Entry<Artifact, Artifact> entry : getExtraArtifacts(ruleContext).entrySet()) { |
| ruleContext.registerAction( |
| new SymlinkAction( |
| ruleContext.getActionOwner(), |
| entry.getKey(), |
| entry.getValue(), |
| "Symlinking framework artifact")); |
| } |
| } |
| |
| /** |
| * Returns an artifact at given path under "package/_frameworks/bundleName.framework" directory. |
| */ |
| private Artifact outputArtifact(RuleContext ruleContext, PathFragment path) { |
| PathFragment frameworkRoot = |
| new PathFragment( |
| new PathFragment("_frameworks"), |
| new PathFragment(bundleName(ruleContext) + ".framework"), |
| path); |
| |
| return ruleContext.getPackageRelativeArtifact( |
| frameworkRoot, ruleContext.getBinOrGenfilesDirectory()); |
| } |
| } |