diff options
author | Dmitry Shevchenko <dmishe@google.com> | 2015-08-11 18:19:18 +0000 |
---|---|---|
committer | Kristina Chodorow <kchodorow@google.com> | 2015-08-12 15:22:22 +0000 |
commit | 6c630794b9a3eea04a2b9de0c00edfec920eff66 (patch) | |
tree | 3c0df31848ad8a91facf1bb1c6adbf526eb88f38 /src/main/java/com/google/devtools/build/lib | |
parent | ee5e5e17d1ab9b1f2bb10115b23ff1a70fccd014 (diff) |
Experimental support for ios_framework rules
* Allows for building and linking to a framework in ios_application
* Currently only works for single arch builds
* Xcode generation produces correct target type, but is mostly untested
The implementation is very similar to that of objc_framework:
1) Build the ios_framework_binary as a dynamic library (-dynamiclib)
2) Symlink the library and public headers to a staging location, inside of "X.framework" bundle. Where X is the name under ios_framework_binary#framework_name
3) Pass the bundle content to ObjcCommon.addFrameworkImports, reusing the core of objc_framework rule implementation. This results in correctly set -F/-framework flags and allows clients to use the framework in a way they would use any SDK/3rd-party framework. It's allowed to import headers via #import <X/X.h> call.
4) Copy the binary and all resources into final application bundle under Frameworks/X.framework
5) Sign the app and nested frameworks
--
MOS_MIGRATED_REVID=100397239
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib')
20 files changed, 521 insertions, 53 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java index 0944470754..b31168d619 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java @@ -101,6 +101,8 @@ import com.google.devtools.build.lib.rules.objc.IosApplicationRule; import com.google.devtools.build.lib.rules.objc.IosDeviceRule; import com.google.devtools.build.lib.rules.objc.IosExtensionBinaryRule; import com.google.devtools.build.lib.rules.objc.IosExtensionRule; +import com.google.devtools.build.lib.rules.objc.IosFrameworkBinaryRule; +import com.google.devtools.build.lib.rules.objc.IosFrameworkRule; import com.google.devtools.build.lib.rules.objc.J2ObjcLibraryBaseRule; import com.google.devtools.build.lib.rules.objc.ObjcBinaryRule; import com.google.devtools.build.lib.rules.objc.ObjcBuildInfoFactory; @@ -333,6 +335,8 @@ public class BazelRuleClassProvider { builder.addRuleDefinition(new IosApplicationRule()); builder.addRuleDefinition(new IosExtensionBinaryRule()); builder.addRuleDefinition(new IosExtensionRule()); + builder.addRuleDefinition(new IosFrameworkBinaryRule()); + builder.addRuleDefinition(new IosFrameworkRule()); builder.addRuleDefinition(new J2ObjcLibraryBaseRule()); builder.addRuleDefinition(new BazelJ2ObjcLibraryRule()); diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/BinaryLinkingTargetFactory.java b/src/main/java/com/google/devtools/build/lib/rules/objc/BinaryLinkingTargetFactory.java index 9117f07b89..21f4518f6f 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/BinaryLinkingTargetFactory.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/BinaryLinkingTargetFactory.java @@ -49,16 +49,22 @@ abstract class BinaryLinkingTargetFactory implements RuleConfiguredTargetFactory } private final HasReleaseBundlingSupport hasReleaseBundlingSupport; - private final ExtraLinkArgs extraLinkArgs; private final XcodeProductType productType; protected BinaryLinkingTargetFactory(HasReleaseBundlingSupport hasReleaseBundlingSupport, - ExtraLinkArgs extraLinkArgs, XcodeProductType productType) { + XcodeProductType productType) { this.hasReleaseBundlingSupport = hasReleaseBundlingSupport; - this.extraLinkArgs = extraLinkArgs; 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."; @@ -93,7 +99,8 @@ abstract class BinaryLinkingTargetFactory implements RuleConfiguredTargetFactory .registerJ2ObjcCompileAndArchiveActions(objcProvider) .registerCompileAndArchiveActions(common) .addXcodeSettings(xcodeProviderBuilder, common) - .registerLinkActions(objcProvider, extraLinkArgs, ImmutableList.<Artifact>of()) + .registerLinkActions(objcProvider, getExtraLinkArgs(ruleContext), + ImmutableList.<Artifact>of()) .validateAttributes(); if (ruleContext.hasErrors()) { @@ -160,6 +167,7 @@ abstract class BinaryLinkingTargetFactory implements RuleConfiguredTargetFactory RunfilesSupport runfilesSupport = maybeRunfilesSupport.get(); targetBuilder.setRunfilesSupport(runfilesSupport, runfilesSupport.getExecutable()); } + configureTarget(targetBuilder, ruleContext); return targetBuilder.build(); } @@ -202,4 +210,10 @@ abstract class BinaryLinkingTargetFactory implements RuleConfiguredTargetFactory 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) {}; } diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/CompilationSupport.java b/src/main/java/com/google/devtools/build/lib/rules/objc/CompilationSupport.java index 4aadc81b36..5af6e94f81 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/CompilationSupport.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/CompilationSupport.java @@ -20,6 +20,7 @@ import static com.google.devtools.build.lib.rules.objc.ObjcProvider.FORCE_LOAD_L import static com.google.devtools.build.lib.rules.objc.ObjcProvider.FRAMEWORK_DIR; import static com.google.devtools.build.lib.rules.objc.ObjcProvider.FRAMEWORK_FILE; import static com.google.devtools.build.lib.rules.objc.ObjcProvider.Flag.USES_CPP; +import static com.google.devtools.build.lib.rules.objc.ObjcProvider.Flag.USES_FRAMEWORKS; import static com.google.devtools.build.lib.rules.objc.ObjcProvider.Flag.USES_SWIFT; import static com.google.devtools.build.lib.rules.objc.ObjcProvider.HEADER; import static com.google.devtools.build.lib.rules.objc.ObjcProvider.IMPORTED_LIBRARY; @@ -536,8 +537,12 @@ final class CompilationSupport { } if (objcProvider.is(USES_SWIFT)) { + commandLine.add("-L").add(IosSdkCommands.swiftLibDir(objcConfiguration)); + } + + if (objcProvider.is(USES_SWIFT) || objcProvider.is(USES_FRAMEWORKS)) { + // Enable loading bundled frameworks. commandLine - .add("-L").add(IosSdkCommands.swiftLibDir(objcConfiguration)) .add("-Xlinker").add("-rpath") .add("-Xlinker").add("@executable_path/Frameworks"); } diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/IosApplication.java b/src/main/java/com/google/devtools/build/lib/rules/objc/IosApplication.java index 7b18b97b47..335acb8c3e 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/IosApplication.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/IosApplication.java @@ -44,7 +44,7 @@ public class IosApplication extends ReleaseBundlingTargetFactory { public IosApplication() { super(ReleaseBundlingSupport.APP_BUNDLE_DIR_FORMAT, XcodeProductType.APPLICATION, - ExposeAsNestedBundle.NO, DEPENDENCY_ATTRIBUTES, ConfigurationDistinguisher.APPLICATION); + DEPENDENCY_ATTRIBUTES, ConfigurationDistinguisher.APPLICATION); } @Override diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/IosExtension.java b/src/main/java/com/google/devtools/build/lib/rules/objc/IosExtension.java index f16327d3c2..821fa5e8e1 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/IosExtension.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/IosExtension.java @@ -14,6 +14,8 @@ package com.google.devtools.build.lib.rules.objc; +import static com.google.devtools.build.lib.rules.objc.ObjcProvider.MERGE_ZIP; + import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; @@ -48,8 +50,7 @@ public class IosExtension extends ReleaseBundlingTargetFactory { public IosExtension() { super(ReleaseBundlingSupport.EXTENSION_BUNDLE_DIR_FORMAT, XcodeProductType.EXTENSION, - ExposeAsNestedBundle.YES, ImmutableSet.of(new Attribute("binary", Mode.SPLIT)), - ConfigurationDistinguisher.EXTENSION); + ImmutableSet.of(new Attribute("binary", Mode.SPLIT)), ConfigurationDistinguisher.EXTENSION); } @Override @@ -57,6 +58,14 @@ public class IosExtension extends ReleaseBundlingTargetFactory { return determineMinimumOsVersion(ObjcRuleClasses.objcConfiguration(ruleContext).getMinimumOs()); } + @Override + protected ObjcProvider exposedObjcProvider(RuleContext ruleContext) { + // Nest this target's bundle under final IPA + return new ObjcProvider.Builder() + .add(MERGE_ZIP, ruleContext.getImplicitOutputArtifact(ReleaseBundlingSupport.IPA)) + .build(); + } + private static String determineMinimumOsVersion(String fromFlag) { if (Double.parseDouble(fromFlag) < Double.parseDouble(EXTENSION_MINIMUM_OS_VERSION)) { // Extensions are not accepted by Apple below version 8.0. While applications built with a diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/IosExtensionBinary.java b/src/main/java/com/google/devtools/build/lib/rules/objc/IosExtensionBinary.java index e85e4c44f5..77742442b8 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/IosExtensionBinary.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/IosExtensionBinary.java @@ -14,6 +14,7 @@ package com.google.devtools.build.lib.rules.objc; +import com.google.devtools.build.lib.analysis.RuleContext; import com.google.devtools.build.lib.rules.objc.CompilationSupport.ExtraLinkArgs; /** @@ -21,8 +22,11 @@ import com.google.devtools.build.lib.rules.objc.CompilationSupport.ExtraLinkArgs */ public class IosExtensionBinary extends BinaryLinkingTargetFactory { public IosExtensionBinary() { - super(HasReleaseBundlingSupport.NO, - new ExtraLinkArgs("-e", "_NSExtensionMain", "-fapplication-extension"), - XcodeProductType.LIBRARY_STATIC); + super(HasReleaseBundlingSupport.NO, XcodeProductType.LIBRARY_STATIC); + } + + @Override + protected ExtraLinkArgs getExtraLinkArgs(RuleContext ruleContext) { + return new ExtraLinkArgs("-e", "_NSExtensionMain", "-fapplication-extension"); } } diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/IosFramework.java b/src/main/java/com/google/devtools/build/lib/rules/objc/IosFramework.java new file mode 100644 index 0000000000..0774559a35 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/IosFramework.java @@ -0,0 +1,155 @@ +// Copyright 2014 Google Inc. 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.FLAG; +import static com.google.devtools.build.lib.rules.objc.ObjcProvider.FRAMEWORK_DIR; +import static com.google.devtools.build.lib.rules.objc.ObjcProvider.FRAMEWORK_FILE; +import static com.google.devtools.build.lib.rules.objc.ObjcProvider.Flag.USES_FRAMEWORKS; +import static com.google.devtools.build.lib.rules.objc.ObjcProvider.MERGE_ZIP; + +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.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.rules.cpp.CcCommon; +import com.google.devtools.build.lib.rules.objc.ReleaseBundlingSupport.SplitArchTransition.ConfigurationDistinguisher; +import com.google.devtools.build.lib.vfs.PathFragment; + +/** + * Implementation for {@code ios_framework}. + */ +public class IosFramework extends ReleaseBundlingTargetFactory { + + @VisibleForTesting static final String MINIMUM_OS_VERSION = "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 String bundleMinimumOsVersion(RuleContext ruleContext) { + String flagValue = ObjcRuleClasses.objcConfiguration(ruleContext).getMinimumOs(); + + return String.valueOf( + Math.max(Double.parseDouble(MINIMUM_OS_VERSION), Double.parseDouble(flagValue))); + } + + /** + * 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<Artifact> 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 (Artifact header : headers) { + Artifact frameworkHeader = + outputArtifact(ruleContext, new PathFragment("Headers/" + header.getFilename())); + + builder.put(header, frameworkHeader); + } + + return builder.build(); + } + + @Override + protected ObjcProvider exposedObjcProvider(RuleContext ruleContext) { + // 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)) + .add(FLAG, USES_FRAMEWORKS) + .addAll(FRAMEWORK_FILE, frameworkImports) + .addAll( + 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()); + } +} diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/IosFrameworkBinary.java b/src/main/java/com/google/devtools/build/lib/rules/objc/IosFrameworkBinary.java new file mode 100644 index 0000000000..cd2930b5d9 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/IosFrameworkBinary.java @@ -0,0 +1,49 @@ +// Copyright 2014 Google Inc. 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.devtools.build.lib.analysis.RuleConfiguredTargetBuilder; +import com.google.devtools.build.lib.analysis.RuleContext; +import com.google.devtools.build.lib.rules.objc.CompilationSupport.ExtraLinkArgs; + +/** + * Implementation for the "ios_framework_binary" rule. + */ +public class IosFrameworkBinary extends BinaryLinkingTargetFactory { + public IosFrameworkBinary() { + super(HasReleaseBundlingSupport.NO, XcodeProductType.LIBRARY_STATIC); + } + + @Override + protected ExtraLinkArgs getExtraLinkArgs(RuleContext ruleContext) { + String frameworkName = getFrameworkName(ruleContext); + + return new ExtraLinkArgs( + "-dynamiclib", + String.format("-install_name @rpath/%1$s.framework/%1$s", frameworkName)); + } + + private String getFrameworkName(RuleContext ruleContext) { + return ruleContext.getLabel().getName(); + } + + @Override + protected void configureTarget(RuleConfiguredTargetBuilder target, RuleContext ruleContext) { + IosFrameworkProvider frameworkProvider = + new IosFrameworkProvider(getFrameworkName(ruleContext)); + + target.addProvider(IosFrameworkProvider.class, frameworkProvider); + } +} diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/IosFrameworkBinaryRule.java b/src/main/java/com/google/devtools/build/lib/rules/objc/IosFrameworkBinaryRule.java new file mode 100644 index 0000000000..af8f8b07bf --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/IosFrameworkBinaryRule.java @@ -0,0 +1,65 @@ +// Copyright 2014 Google Inc. 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.devtools.build.lib.analysis.BaseRuleClasses; +import com.google.devtools.build.lib.analysis.RuleDefinition; +import com.google.devtools.build.lib.analysis.RuleDefinitionEnvironment; +import com.google.devtools.build.lib.packages.RuleClass; +import com.google.devtools.build.lib.packages.RuleClass.Builder; + +/** + * Rule definition for ios_framework_binary. + */ +public class IosFrameworkBinaryRule implements RuleDefinition { + + @Override + public RuleClass build(Builder builder, RuleDefinitionEnvironment environment) { + return builder + /*<!-- #BLAZE_RULE(ios_framework_binary).IMPLICIT_OUTPUTS --> + <ul> + <li><code><var>name</var>.xcodeproj/project.pbxproj</code>: An Xcode project file which + can be used to develop or build on a Mac.</li> + </ul> + <!-- #END_BLAZE_RULE.IMPLICIT_OUTPUTS -->*/ + .setImplicitOutputsFunction(XcodeSupport.PBXPROJ) + // TODO(bazel-team): Add version fields that are passed to the linker as + // -compatibility_version X -current_version Y and then embedded into dynamic library. + .build(); + } + + @Override + public Metadata getMetadata() { + return RuleDefinition.Metadata.builder() + .name("ios_framework_binary") + .factoryClass(IosFrameworkBinary.class) + .ancestors(BaseRuleClasses.BaseRule.class, ObjcRuleClasses.LinkingRule.class, + ObjcRuleClasses.XcodegenRule.class) + .build(); + } +} + +/*<!-- #BLAZE_RULE (NAME = ios_framework_binary, TYPE = BINARY, FAMILY = Objective-C) --> + +${ATTRIBUTE_SIGNATURE} + +<p>This rule produces a dynamic library for a framework by linking one or more Objective-C +libraries.</p> + +${IMPLICIT_OUTPUTS} + +${ATTRIBUTE_DEFINITION} + +<!-- #END_BLAZE_RULE -->*/
\ No newline at end of file diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/IosFrameworkProvider.java b/src/main/java/com/google/devtools/build/lib/rules/objc/IosFrameworkProvider.java new file mode 100644 index 0000000000..b9e55186cf --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/IosFrameworkProvider.java @@ -0,0 +1,39 @@ +// Copyright 2015 Google Inc. 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.Preconditions; +import com.google.devtools.build.lib.analysis.TransitiveInfoProvider; +import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; + +/** + * Provider for iOS Framework info. + */ +@Immutable +public final class IosFrameworkProvider implements TransitiveInfoProvider { + + private final String frameworkName; + + public IosFrameworkProvider(String frameworkName) { + this.frameworkName = Preconditions.checkNotNull(frameworkName); + } + + /** + * Returns the name of the framework. + */ + public String getFrameworkName() { + return frameworkName; + } +} diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/IosFrameworkRule.java b/src/main/java/com/google/devtools/build/lib/rules/objc/IosFrameworkRule.java new file mode 100644 index 0000000000..766b86ca17 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/IosFrameworkRule.java @@ -0,0 +1,86 @@ +// Copyright 2015 Google Inc. 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.packages.Attribute.attr; +import static com.google.devtools.build.lib.packages.Type.LABEL; +import static com.google.devtools.build.lib.packages.Type.LABEL_LIST; + +import com.google.devtools.build.lib.analysis.BaseRuleClasses; +import com.google.devtools.build.lib.analysis.RuleDefinition; +import com.google.devtools.build.lib.analysis.RuleDefinitionEnvironment; +import com.google.devtools.build.lib.packages.ImplicitOutputsFunction; +import com.google.devtools.build.lib.packages.RuleClass; +import com.google.devtools.build.lib.packages.RuleClass.Builder; + +/** + * Rule definition for ios_framework. + */ +public class IosFrameworkRule implements RuleDefinition { + + @Override + public RuleClass build(Builder builder, RuleDefinitionEnvironment environment) { + return builder + // TODO(blaze-team): IPA is not right here, should probably be just zipped framework bundle. + .setImplicitOutputsFunction( + ImplicitOutputsFunction.fromFunctions(ReleaseBundlingSupport.IPA, XcodeSupport.PBXPROJ)) + /* <!-- #BLAZE_RULE(ios_framework).ATTRIBUTE(binary) --> + The binary target included in the framework bundle. + ${SYNOPSIS} + <!-- #END_BLAZE_RULE.ATTRIBUTE -->*/ + .add(attr("binary", LABEL) + .allowedRuleClasses("ios_framework_binary") + .allowedFileTypes() + .mandatory() + .direct_compile_time_input() + .cfg(IosExtension.MINIMUM_OS_AND_SPLIT_ARCH_TRANSITION)) + /* <!-- #BLAZE_RULE(ios_framework).ATTRIBUTE(hdrs) --> + Public headers to include in the framework bundle. + ${SYNOPSIS} + <!-- #END_BLAZE_RULE.ATTRIBUTE -->*/ + .add(attr("hdrs", LABEL_LIST) + .direct_compile_time_input() + .allowedFileTypes(ObjcRuleClasses.HDRS_TYPE)) + .build(); + } + + @Override + public Metadata getMetadata() { + return RuleDefinition.Metadata.builder() + .name("ios_framework") + .factoryClass(IosFramework.class) + .ancestors(BaseRuleClasses.BaseRule.class, ObjcRuleClasses.ReleaseBundlingRule.class, + ObjcRuleClasses.XcodegenRule.class) + .build(); + } +} + +/*<!-- #BLAZE_RULE (NAME = ios_framework, TYPE = BINARY, FAMILY = Objective-C) --> + +${ATTRIBUTE_SIGNATURE} + +<p>This rule produces a bundled binary for a framework from a compiled binary and bundle +metadata.</p> + +<p>A framework is a bundle that contains a dynamic library (the "binary"), public headers (that +clients of the framework can use) and any resources. Framework headers are stripped out of the +final app bundle and only used during compilation stage. + +<p>Bundles generated by this rule use a bundle directory called +<code>Frameworks/<var>framework_name</var>.framework</code>. + +${ATTRIBUTE_DEFINITION} + +<!-- #END_BLAZE_RULE -->*/ diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcBinary.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcBinary.java index 02bf67f88c..9e19fc8461 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcBinary.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcBinary.java @@ -14,8 +14,6 @@ package com.google.devtools.build.lib.rules.objc; -import com.google.devtools.build.lib.rules.objc.CompilationSupport.ExtraLinkArgs; - /** * Implementation for the "objc_binary" rule. */ @@ -25,7 +23,6 @@ public class ObjcBinary extends BinaryLinkingTargetFactory { // TODO(bazel-team): Remove the enum and delete all code depending on YES case once all // bundle users are migrated to ios_application. HasReleaseBundlingSupport.YES, - new ExtraLinkArgs(), // TODO(bazel-team): Use LIBRARY_STATIC as parameter instead of APPLICATION once objc_binary // no longer creates an application bundle diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcCommon.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcCommon.java index f76964bc50..688fa13738 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcCommon.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcCommon.java @@ -505,7 +505,7 @@ public final class ObjcCommon { static final FileType ASSET_CATALOG_CONTAINER_TYPE = FileType.of(".xcassets"); - static final FileType FRAMEWORK_CONTAINER_TYPE = FileType.of(".framework"); + public static final FileType FRAMEWORK_CONTAINER_TYPE = FileType.of(".framework"); private final ObjcProvider objcProvider; private final Optional<CompilationArtifacts> compilationArtifacts; diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProvider.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProvider.java index 1737c0de00..413ed2d5b2 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProvider.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProvider.java @@ -212,7 +212,13 @@ public final class ObjcProvider implements TransitiveInfoProvider { * Indicates that Swift source files are present. This affects bundling, compiling and linking * actions. */ - USES_SWIFT + USES_SWIFT, + + /** + * Indicates that the resulting bundle will have embedded frameworks. This affects linking step. + */ + USES_FRAMEWORKS + } private final ImmutableMap<Key<?>, NestedSet<?>> items; diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcRuleClasses.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcRuleClasses.java index 4009d5e292..05789407c4 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcRuleClasses.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcRuleClasses.java @@ -327,6 +327,9 @@ public class ObjcRuleClasses { static final FileType XIB_TYPE = FileType.of(".xib"); + // TODO(bazel-team): Restrict this to actual header files only. + static final FileTypeSet HDRS_TYPE = FileTypeSet.ANY_FILE; + /** * Common attributes for {@code objc_*} rules that allow the definition of resources such as * storyboards. These resources are used during compilation of the declaring rule as well as when @@ -513,7 +516,7 @@ public class ObjcRuleClasses { <!-- #END_BLAZE_RULE.ATTRIBUTE -->*/ .add(attr("hdrs", LABEL_LIST) .direct_compile_time_input() - .allowedFileTypes(FileTypeSet.ANY_FILE)) + .allowedFileTypes(HDRS_TYPE)) /* <!-- #BLAZE_RULE($objc_compile_dependency_rule).ATTRIBUTE(includes) --> List of <code>#include/#import</code> search paths to add to this target and all depending targets. @@ -558,7 +561,8 @@ public class ObjcRuleClasses { "objc_framework", "objc_proto_library", "j2objc_library", - "cc_library"); + "cc_library", + "ios_framework"); @Override public RuleClass build(Builder builder, RuleDefinitionEnvironment env) { diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcXcodeprojRule.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcXcodeprojRule.java index 395806e7ba..fda1e68233 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcXcodeprojRule.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcXcodeprojRule.java @@ -50,7 +50,9 @@ public class ObjcXcodeprojRule implements RuleDefinition { "ios_application", "ios_extension_binary", "ios_extension", - "ios_test", + "ios_framework", + "ios_framework_binary", + "ios_test", "objc_bundle_library", "objc_import", "objc_library") diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ReleaseBundlingSupport.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ReleaseBundlingSupport.java index f17a8bf24f..d7644bc84d 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/ReleaseBundlingSupport.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ReleaseBundlingSupport.java @@ -93,6 +93,8 @@ public final class ReleaseBundlingSupport { static final String APP_BUNDLE_DIR_FORMAT = "Payload/%s.app"; @VisibleForTesting static final String EXTENSION_BUNDLE_DIR_FORMAT = "PlugIns/%s.appex"; + @VisibleForTesting + static final String FRAMEWORK_BUNDLE_DIR_FORMAT = "Frameworks/%s.framework"; /** * Command string for "sed" that tries to extract the application version number from a larger @@ -141,22 +143,47 @@ public final class ReleaseBundlingSupport { * the latter * @param bundleDirFormat format string representing the bundle's directory with a single * placeholder for the target name (e.g. {@code "Payload/%s.app"}) + * @param bundleName name of the bundle, used with bundleDirFormat * @param bundleMinimumOsVersion the minimum OS version this bundle's plist should be generated * for (<b>not</b> the minimum OS version its binary is compiled with, that needs to be set * through the configuration) */ ReleaseBundlingSupport(RuleContext ruleContext, ObjcProvider objcProvider, - LinkedBinary linkedBinary, String bundleDirFormat, String bundleMinimumOsVersion) { + LinkedBinary linkedBinary, String bundleDirFormat, String bundleName, + String bundleMinimumOsVersion) { this.linkedBinary = linkedBinary; this.attributes = new Attributes(ruleContext); this.ruleContext = ruleContext; this.objcProvider = objcProvider; this.intermediateArtifacts = ObjcRuleClasses.intermediateArtifacts(ruleContext); - bundling = bundling(ruleContext, objcProvider, bundleDirFormat, bundleMinimumOsVersion); + bundling = bundling(ruleContext, objcProvider, bundleDirFormat, bundleName, + bundleMinimumOsVersion); bundleSupport = new BundleSupport(ruleContext, bundling, extraActoolArgs()); } /** + * Creates a new application support within the given rule context. + * + * {@code bundleName} defaults to label name + * + * @param ruleContext context for the application-generating rule + * @param objcProvider provider containing all dependencies' information as well as some of this + * rule's + * @param linkedBinary whether to look for a linked binary from this rule and dependencies or just + * the latter + * @param bundleDirFormat format string representing the bundle's directory with a single + * placeholder for the target name (e.g. {@code "Payload/%s.app"}) + * @param bundleMinimumOsVersion the minimum OS version this bundle's plist should be generated + * for (<b>not</b> the minimum OS version its binary is compiled with, that needs to be set + * through the configuration) + */ + ReleaseBundlingSupport(RuleContext ruleContext, ObjcProvider objcProvider, + LinkedBinary linkedBinary, String bundleDirFormat, String bundleMinimumOsVersion) { + this(ruleContext, objcProvider, linkedBinary, bundleDirFormat, ruleContext.getLabel().getName(), + bundleMinimumOsVersion); + } + + /** * Validates application-related attributes set on this rule and registers any errors with the * rule context. * @@ -440,7 +467,7 @@ public final class ReleaseBundlingSupport { } private Bundling bundling(RuleContext ruleContext, ObjcProvider objcProvider, - String bundleDirFormat, String minimumOsVersion) { + String bundleDirFormat, String bundleName, String minimumOsVersion) { ImmutableList<BundleableFile> extraBundleFiles; ObjcConfiguration objcConfiguration = ObjcRuleClasses.objcConfiguration(ruleContext); if (objcConfiguration.getBundlingPlatform() == Platform.DEVICE) { @@ -461,7 +488,7 @@ public final class ReleaseBundlingSupport { } return new Bundling.Builder() - .setName(ruleContext.getLabel().getName()) + .setName(bundleName) // Architecture that determines which nested bundles are kept. .setArchitecture(objcConfiguration.getDependencySingleArchitecture()) .setBundleDirFormat(bundleDirFormat) @@ -544,7 +571,7 @@ public final class ReleaseBundlingSupport { ImmutableList.Builder<String> dirsToSign = new ImmutableList.Builder<>(); - // Explicitly sign Swift frameworks. Unfortunately --deep option on codesign doesn't do this + // Explicitly sign Swift dylibs. Unfortunately --deep option on codesign doesn't do this // automatically. // The order here is important. The innermost code must singed first. String bundleDir = ShellUtils.shellEscape(bundling.getBundleDir()); @@ -943,7 +970,7 @@ public final class ReleaseBundlingSupport { * transition may exist with the same value in a single Bazel invocation. */ enum ConfigurationDistinguisher { - EXTENSION, APPLICATION, UNKNOWN + EXTENSION, APPLICATION, FRAMEWORK, UNKNOWN } } } diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ReleaseBundlingTargetFactory.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ReleaseBundlingTargetFactory.java index 5d474f20b9..b1d26cc9fa 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/ReleaseBundlingTargetFactory.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ReleaseBundlingTargetFactory.java @@ -14,9 +14,6 @@ package com.google.devtools.build.lib.rules.objc; -import static com.google.devtools.build.lib.rules.objc.ObjcProvider.MERGE_ZIP; - -import com.google.common.base.Optional; import com.google.common.collect.ImmutableSet; import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.analysis.ConfiguredTarget; @@ -27,39 +24,34 @@ import com.google.devtools.build.lib.rules.RuleConfiguredTargetFactory; import com.google.devtools.build.lib.rules.objc.ReleaseBundlingSupport.LinkedBinary; import com.google.devtools.build.lib.rules.objc.ReleaseBundlingSupport.SplitArchTransition.ConfigurationDistinguisher; +import javax.annotation.Nullable; + /** * Base class for rules that bundle releases. */ public abstract class ReleaseBundlingTargetFactory implements RuleConfiguredTargetFactory { - /** - * Indicates whether a target factory should export an {@link ObjcProvider} containing itself as - * a nested bundle. - */ - protected enum ExposeAsNestedBundle { YES, NO } - private final String bundleDirFormat; private final XcodeProductType xcodeProductType; - private final ExposeAsNestedBundle exposeAsNestedBundle; private final ImmutableSet<Attribute> dependencyAttributes; private final ConfigurationDistinguisher configurationDistinguisher; /** * @param bundleDirFormat format string representing the bundle's directory with a single * placeholder for the target name (e.g. {@code "Payload/%s.app"}) - * @param exposeAsNestedBundle whether to export an {@link ObjcProvider} with this target as a * @param dependencyAttributes all attributes that contain dependencies of this rule. Any * dependency so listed must expose {@link XcodeProvider} and {@link ObjcProvider}. * @param configurationDistinguisher distinguisher used for cases where inputs from dependencies * of this bundle may need distinguishing because they come from configurations that are only * different by this value */ - public ReleaseBundlingTargetFactory(String bundleDirFormat, XcodeProductType xcodeProductType, - ExposeAsNestedBundle exposeAsNestedBundle, ImmutableSet<Attribute> dependencyAttributes, + public ReleaseBundlingTargetFactory( + String bundleDirFormat, + XcodeProductType xcodeProductType, + ImmutableSet<Attribute> dependencyAttributes, ConfigurationDistinguisher configurationDistinguisher) { this.bundleDirFormat = bundleDirFormat; this.xcodeProductType = xcodeProductType; - this.exposeAsNestedBundle = exposeAsNestedBundle; this.dependencyAttributes = dependencyAttributes; this.configurationDistinguisher = configurationDistinguisher; } @@ -73,7 +65,7 @@ public abstract class ReleaseBundlingTargetFactory implements RuleConfiguredTarg ReleaseBundlingSupport releaseBundlingSupport = new ReleaseBundlingSupport( ruleContext, common.getObjcProvider(), LinkedBinary.DEPENDENCIES_ONLY, bundleDirFormat, - bundleMinimumOsVersion(ruleContext)); + bundleName(ruleContext), bundleMinimumOsVersion(ruleContext)); releaseBundlingSupport .registerActions() .addXcodeSettings(xcodeProviderBuilder) @@ -94,22 +86,16 @@ public abstract class ReleaseBundlingTargetFactory implements RuleConfiguredTarg xcodeSupport.registerActions(xcodeProviderBuilder.build()); - Optional<ObjcProvider> exposedObjcProvider; - if (exposeAsNestedBundle == ExposeAsNestedBundle.YES) { - exposedObjcProvider = Optional.of(new ObjcProvider.Builder() - .add(MERGE_ZIP, ruleContext.getImplicitOutputArtifact(ReleaseBundlingSupport.IPA)) - .build()); - } else { - exposedObjcProvider = Optional.absent(); - } - RuleConfiguredTargetBuilder targetBuilder = ObjcRuleClasses.ruleConfiguredTarget(ruleContext, filesToBuild.build()) .addProvider(XcTestAppProvider.class, releaseBundlingSupport.xcTestAppProvider()) .addProvider(XcodeProvider.class, xcodeProviderBuilder.build()); - if (exposedObjcProvider.isPresent()) { - targetBuilder.addProvider(ObjcProvider.class, exposedObjcProvider.get()); + + ObjcProvider exposedObjcProvider = exposedObjcProvider(ruleContext); + if (exposedObjcProvider != null) { + targetBuilder.addProvider(ObjcProvider.class, exposedObjcProvider); } + configureTarget(targetBuilder, ruleContext, releaseBundlingSupport); return targetBuilder.build(); } @@ -130,6 +116,21 @@ public abstract class ReleaseBundlingTargetFactory implements RuleConfiguredTarg protected void configureTarget(RuleConfiguredTargetBuilder target, RuleContext ruleContext, ReleaseBundlingSupport releaseBundlingSupport) {} + /** + * Returns the name of this target's bundle. + */ + protected String bundleName(RuleContext ruleContext) { + return ruleContext.getLabel().getName(); + } + + /** + * Returns an exposed {@code ObjcProvider} object. + */ + @Nullable + protected ObjcProvider exposedObjcProvider(RuleContext ruleContext) { + return null; + } + private ObjcCommon common(RuleContext ruleContext) { ObjcCommon.Builder builder = new ObjcCommon.Builder(ruleContext) .setIntermediateArtifacts(ObjcRuleClasses.intermediateArtifacts(ruleContext)); diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/XcodeProductType.java b/src/main/java/com/google/devtools/build/lib/rules/objc/XcodeProductType.java index 1a68206d61..72e2f0b095 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/XcodeProductType.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/XcodeProductType.java @@ -23,7 +23,8 @@ enum XcodeProductType { BUNDLE("com.apple.product-type.bundle"), APPLICATION("com.apple.product-type.application"), UNIT_TEST("com.apple.product-type.bundle.unit-test"), - EXTENSION("com.apple.product-type.app-extension"); + EXTENSION("com.apple.product-type.app-extension"), + FRAMEWORK("com.apple.product-type.framework"); private final String identifier; diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/XcodeProvider.java b/src/main/java/com/google/devtools/build/lib/rules/objc/XcodeProvider.java index 445f50b4b7..58db685f08 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/XcodeProvider.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/XcodeProvider.java @@ -464,7 +464,7 @@ public final class XcodeProvider implements TransitiveInfoProvider { @VisibleForTesting static final EnumSet<XcodeProductType> CAN_LINK_PRODUCT_TYPES = EnumSet.of( XcodeProductType.APPLICATION, XcodeProductType.BUNDLE, XcodeProductType.UNIT_TEST, - XcodeProductType.EXTENSION); + XcodeProductType.EXTENSION, XcodeProductType.FRAMEWORK); /** * Returns the name of the Xcode target that corresponds to a build target with the given name. |