diff options
author | 2015-09-15 00:00:54 +0000 | |
---|---|---|
committer | 2015-09-15 20:26:21 +0000 | |
commit | 415aae660a932ead71628072610948afd11d7dc2 (patch) | |
tree | c2b038ca199463663974c3d2eee5f13a38a15c70 /src/main/java | |
parent | 1c289af299227a73de2eb8903696d906105451ef (diff) |
Generate module maps for each objc_* target with compilation support.
This uses CppModuleMap and CppModuleMapAction to generate clang module maps for the target and its transitive dependencies. To enable this feature, you must pass -experimental_objc_enable_module_maps to bazel.
For objc_* targets that need to use the "modules" language features (@import, Swift interop, etc), set the "enable_modules" attribute to 1.
--
MOS_MIGRATED_REVID=103045673
Diffstat (limited to 'src/main/java')
12 files changed, 457 insertions, 115 deletions
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 21f4518f6f..9e757e2bdc 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 @@ -201,6 +201,7 @@ abstract class BinaryLinkingTargetFactory implements RuleConfiguredTargetFactory "non_propagated_deps", Mode.TARGET, ObjcProvider.class)) .setIntermediateArtifacts(intermediateArtifacts) .setAlwayslink(false) + .setHasModuleMap() .addExtraImportLibraries(ObjcRuleClasses.j2ObjcLibraries(ruleContext)) .setLinkedBinary(intermediateArtifacts.strippedSingleArchitectureBinary()); 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 d31e9891fa..f8d41c3b4d 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 @@ -27,6 +27,7 @@ import static com.google.devtools.build.lib.rules.objc.ObjcProvider.IMPORTED_LIB import static com.google.devtools.build.lib.rules.objc.ObjcProvider.INCLUDE; import static com.google.devtools.build.lib.rules.objc.ObjcProvider.INCLUDE_SYSTEM; import static com.google.devtools.build.lib.rules.objc.ObjcProvider.LIBRARY; +import static com.google.devtools.build.lib.rules.objc.ObjcProvider.MODULE_MAP; import static com.google.devtools.build.lib.rules.objc.ObjcProvider.SDK_DYLIB; import static com.google.devtools.build.lib.rules.objc.ObjcProvider.SDK_FRAMEWORK; import static com.google.devtools.build.lib.rules.objc.ObjcProvider.WEAK_SDK_FRAMEWORK; @@ -66,6 +67,8 @@ import com.google.devtools.build.lib.analysis.actions.SpawnAction; import com.google.devtools.build.lib.collect.nestedset.NestedSet; import com.google.devtools.build.lib.packages.TargetUtils; import com.google.devtools.build.lib.packages.Type; +import com.google.devtools.build.lib.rules.cpp.CppModuleMap; +import com.google.devtools.build.lib.rules.cpp.CppModuleMapAction; import com.google.devtools.build.lib.rules.cpp.LinkerInputs; import com.google.devtools.build.lib.rules.java.J2ObjcConfiguration; import com.google.devtools.build.lib.rules.objc.ObjcCommon.CompilationAttributes; @@ -112,6 +115,17 @@ public final class CompilationSupport { }; /** + * Predicate to remove '.inc' files from an iterable. + */ + private static final Predicate<Artifact> NON_INC_FILES = + new Predicate<Artifact>() { + @Override + public boolean apply(Artifact artifact) { + return !artifact.getFilename().endsWith(".inc"); + } + }; + + /** * Iterable wrapper providing strong type safety for arguments to binary linking. */ static final class ExtraLinkArgs extends IterableWrapper<String> { @@ -161,10 +175,20 @@ public final class CompilationSupport { */ CompilationSupport registerCompileAndArchiveActions(ObjcCommon common) { if (common.getCompilationArtifacts().isPresent()) { + registerGenerateModuleMapAction(common.getCompilationArtifacts()); + IntermediateArtifacts intermediateArtifacts = + ObjcRuleClasses.intermediateArtifacts(ruleContext); + Optional<CppModuleMap> moduleMap; + if (ObjcRuleClasses.objcConfiguration(ruleContext).moduleMapsEnabled()) { + moduleMap = Optional.of(intermediateArtifacts.moduleMap()); + } else { + moduleMap = Optional.<CppModuleMap>absent(); + } registerCompileAndArchiveActions( common.getCompilationArtifacts().get(), - ObjcRuleClasses.intermediateArtifacts(ruleContext), + intermediateArtifacts, common.getObjcProvider(), + moduleMap, ruleContext.getConfiguration().isCodeCoverageEnabled()); } return this; @@ -174,25 +198,42 @@ public final class CompilationSupport { * Creates actions to compile each source file individually, and link all the compiled object * files into a single archive library. */ - private void registerCompileAndArchiveActions(CompilationArtifacts compilationArtifacts, - IntermediateArtifacts intermediateArtifacts, ObjcProvider objcProvider, + private void registerCompileAndArchiveActions( + CompilationArtifacts compilationArtifacts, + IntermediateArtifacts intermediateArtifacts, + ObjcProvider objcProvider, + Optional<CppModuleMap> moduleMap, boolean isCodeCoverageEnabled) { ImmutableList.Builder<Artifact> objFiles = new ImmutableList.Builder<>(); for (Artifact sourceFile : compilationArtifacts.getSrcs()) { Artifact objFile = intermediateArtifacts.objFile(sourceFile); objFiles.add(objFile); if (ObjcRuleClasses.SWIFT_SOURCES.matches(sourceFile.getFilename())) { - registerSwiftCompileAction(sourceFile, objFile, intermediateArtifacts); + registerSwiftCompileAction(sourceFile, objFile, intermediateArtifacts, objcProvider); } else { - registerCompileAction(sourceFile, objFile, objcProvider, intermediateArtifacts, - compilationArtifacts, ImmutableList.of("-fobjc-arc"), isCodeCoverageEnabled); + registerCompileAction( + sourceFile, + objFile, + objcProvider, + moduleMap, + intermediateArtifacts, + compilationArtifacts, + ImmutableList.of("-fobjc-arc"), + isCodeCoverageEnabled); } } for (Artifact nonArcSourceFile : compilationArtifacts.getNonArcSrcs()) { Artifact objFile = intermediateArtifacts.objFile(nonArcSourceFile); objFiles.add(objFile); - registerCompileAction(nonArcSourceFile, objFile, objcProvider, intermediateArtifacts, - compilationArtifacts, ImmutableList.of("-fno-objc-arc"), isCodeCoverageEnabled); + registerCompileAction( + nonArcSourceFile, + objFile, + objcProvider, + moduleMap, + intermediateArtifacts, + compilationArtifacts, + ImmutableList.of("-fno-objc-arc"), + isCodeCoverageEnabled); } if (compilationArtifacts.hasSwiftSources()) { @@ -208,6 +249,7 @@ public final class CompilationSupport { Artifact sourceFile, Artifact objFile, ObjcProvider objcProvider, + Optional<CppModuleMap> moduleMap, IntermediateArtifacts intermediateArtifacts, CompilationArtifacts compilationArtifacts, Iterable<String> otherFlags, @@ -259,6 +301,28 @@ public final class CompilationSupport { .add("-MD") .addExecPath("-MF", dotdFile); + if (moduleMap.isPresent()) { + // -fmodule-map-file only loads the module in Xcode 7, so we add the module maps's directory + // to the include path instead. + // TODO(bazel-team): Use -fmodule-map-file when Xcode 6 support is dropped. + commandLine + .add(attributes.enableModules() ? "-fmodules" : "-fmodule-maps") + .add("-iquote") + .add( + moduleMap + .get() + .getArtifact() + .getExecPath() + .getParentDirectory() + .toString()) + .add("-fmodule-name=" + moduleMap.get().getName()); + if (attributes.enableModules()) { + String cachePath = + ruleContext.getConfiguration().getGenfilesFragment() + "/_objc_module_cache"; + commandLine.add("-fmodules-cache-path=" + cachePath); + } + } + // TODO(bazel-team): Remote private headers from inputs once they're added to the provider. ruleContext.registerAction(ObjcRuleClasses.spawnOnDarwinActionBuilder(ruleContext) .setMnemonic("ObjcCompile") @@ -270,6 +334,7 @@ public final class CompilationSupport { .addOutputs(gcnoFiles.build()) .addOutput(dotdFile) .addTransitiveInputs(objcProvider.get(HEADER)) + .addTransitiveInputs(objcProvider.get(MODULE_MAP)) .addInputs(compilationArtifacts.getPrivateHdrs()) .addTransitiveInputs(objcProvider.get(FRAMEWORK_FILE)) .addInputs(compilationArtifacts.getPchFile().asSet()) @@ -285,7 +350,8 @@ public final class CompilationSupport { private void registerSwiftCompileAction( Artifact sourceFile, Artifact objFile, - IntermediateArtifacts intermediateArtifacts) { + IntermediateArtifacts intermediateArtifacts, + ObjcProvider objcProvider) { ObjcConfiguration objcConfiguration = ObjcRuleClasses.objcConfiguration(ruleContext); // Compiling a single swift file requires knowledge of all of the other @@ -331,16 +397,27 @@ public final class CompilationSupport { inputHeaders.add(bridgingHeader.get()); } - ruleContext.registerAction(ObjcRuleClasses.spawnOnDarwinActionBuilder(ruleContext) - .setMnemonic("SwiftCompile") - .setExecutable(SWIFT) - .setCommandLine(commandLine.build()) - .addInput(sourceFile) - .addInputs(otherSwiftSources) - .addInputs(inputHeaders.build()) - .addOutput(objFile) - .addOutput(intermediateArtifacts.swiftModuleFile(sourceFile)) - .build(ruleContext)); + // Import the Objective-C module map. + // TODO(bazel-team): Find a way to import the module map directly, instead of the parent + // directory? + if (objcConfiguration.moduleMapsEnabled()) { + PathFragment moduleMapPath = intermediateArtifacts.moduleMap().getArtifact().getExecPath(); + commandLine.add("-I").add(moduleMapPath.getParentDirectory().toString()); + } + + ruleContext.registerAction( + ObjcRuleClasses.spawnOnDarwinActionBuilder(ruleContext) + .setMnemonic("SwiftCompile") + .setExecutable(SWIFT) + .setCommandLine(commandLine.build()) + .addInput(sourceFile) + .addInputs(otherSwiftSources) + .addInputs(inputHeaders.build()) + .addTransitiveInputs(objcProvider.get(HEADER)) + .addTransitiveInputs(objcProvider.get(MODULE_MAP)) + .addOutput(objFile) + .addOutput(intermediateArtifacts.swiftModuleFile(sourceFile)) + .build(ruleContext)); } /** @@ -459,6 +536,53 @@ public final class CompilationSupport { return this; } + /** + * Registers an action that will generate a clang module map for this target, using the hdrs + * attribute of this rule. + */ + public CompilationSupport registerGenerateModuleMapAction( + Optional<CompilationArtifacts> compilationArtifacts) { + if (ObjcRuleClasses.objcConfiguration(ruleContext).moduleMapsEnabled()) { + Iterable<Artifact> publicHeaders = attributes.hdrs(); + Iterable<Artifact> privateHeaders = ImmutableList.<Artifact>of(); + if (compilationArtifacts.isPresent()) { + CompilationArtifacts artifacts = compilationArtifacts.get(); + publicHeaders = Iterables.concat(publicHeaders, artifacts.getAdditionalHdrs()); + privateHeaders = Iterables.concat(privateHeaders, artifacts.getPrivateHdrs()); + } + CppModuleMap moduleMap = ObjcRuleClasses.intermediateArtifacts(ruleContext).moduleMap(); + registerGenerateModuleMapAction(moduleMap, publicHeaders, privateHeaders); + } + return this; + } + + /** + * Registers an action that will generate a clang module map. + * + * @param moduleMap the module map to generate + * @param publicHeaders the headers that should be directly accessible by dependers + * @param privateHeaders the headers that should only be directly accessible by this module + */ + private void registerGenerateModuleMapAction( + CppModuleMap moduleMap, Iterable<Artifact> publicHeaders, Iterable<Artifact> privateHeaders) { + // The current clang (clang-600.0.57) on Darwin doesn't support 'textual', so we can't have + // '.inc' files in the module map (since they're implictly textual). + // TODO(bazel-team): Remove filtering once clang-700 is the base clang we support. + publicHeaders = Iterables.filter(publicHeaders, NON_INC_FILES); + privateHeaders = Iterables.filter(privateHeaders, NON_INC_FILES); + ruleContext.registerAction( + new CppModuleMapAction( + ruleContext.getActionOwner(), + moduleMap, + privateHeaders, + publicHeaders, + attributes.moduleMapsForDirectDeps(), + ImmutableList.<PathFragment>of(), + /*compiledModule=*/ true, + /*moduleMapHomeIsCwd=*/ false, + /*generateSubModules=*/ true)); + } + private void registerLinkAction(ObjcProvider objcProvider, ExtraLinkArgs extraLinkArgs, Iterable<Artifact> extraLinkInputs, Optional<Artifact> dsymBundle) { ObjcConfiguration objcConfiguration = ObjcRuleClasses.objcConfiguration(ruleContext); @@ -688,7 +812,22 @@ public final class CompilationSupport { .setIntermediateArtifacts(intermediateArtifacts) .setPchFile(Optional.<Artifact>absent()) .build(); - registerCompileAndArchiveActions(compilationArtifact, intermediateArtifacts, objcProvider, + // The module map that the above intermediateArtifacts would point to is a combination of + // the current target and the java target. Instead, we want the one generated for that + // source, but there is no easy way to get that. So for now, compile the source with the + // module map (and therefore module name) for this rule. + // TODO(bazel-team): Generate module maps along with the J2Objc sources, and use that here. + Optional<CppModuleMap> moduleMap; + if (ObjcRuleClasses.objcConfiguration(ruleContext).moduleMapsEnabled()) { + moduleMap = Optional.of(ObjcRuleClasses.intermediateArtifacts(ruleContext).moduleMap()); + } else { + moduleMap = Optional.<CppModuleMap>absent(); + } + registerCompileAndArchiveActions( + compilationArtifact, + intermediateArtifacts, + objcProvider, + moduleMap, ruleContext.getConfiguration().isCodeCoverageEnabled()); } } @@ -696,6 +835,24 @@ public final class CompilationSupport { return this; } + /** + * Registers actions that generates a module map for all {@link J2ObjcSource}s in + * {@link J2ObjcSrcsProvider}. + * + * @return this compilation support + */ + public CompilationSupport registerJ2ObjcGenerateModuleMapAction(J2ObjcSrcsProvider provider) { + if (ObjcRuleClasses.objcConfiguration(ruleContext).moduleMapsEnabled()) { + CppModuleMap moduleMap = ObjcRuleClasses.intermediateArtifacts(ruleContext).moduleMap(); + ImmutableSet.Builder<Artifact> headers = ImmutableSet.builder(); + for (J2ObjcSource j2ObjcSource : provider.getSrcs()) { + headers.addAll(j2ObjcSource.getObjcHdrs()); + } + registerGenerateModuleMapAction(moduleMap, headers.build(), ImmutableList.<Artifact>of()); + } + return this; + } + private void registerJ2ObjcDeadCodeRemovalActions(Iterable<J2ObjcSource> j2ObjcSources, Iterable<String> entryClasses) { Artifact pruner = ruleContext.getPrerequisiteArtifact("$j2objc_dead_code_pruner", Mode.HOST); diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/IntermediateArtifacts.java b/src/main/java/com/google/devtools/build/lib/rules/objc/IntermediateArtifacts.java index 59f9b07f67..923ed6c79f 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/IntermediateArtifacts.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/IntermediateArtifacts.java @@ -17,8 +17,10 @@ package com.google.devtools.build.lib.rules.objc; import com.google.common.base.Optional; import com.google.common.base.Preconditions; import com.google.devtools.build.lib.actions.Artifact; +import com.google.devtools.build.lib.actions.Root; import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode; import com.google.devtools.build.lib.analysis.RuleContext; +import com.google.devtools.build.lib.rules.cpp.CppModuleMap; import com.google.devtools.build.lib.syntax.Label; import com.google.devtools.build.lib.vfs.FileSystemUtils; import com.google.devtools.build.lib.vfs.PathFragment; @@ -28,7 +30,7 @@ import com.google.devtools.build.lib.vfs.PathFragment; */ // TODO(bazel-team): This should really be named DerivedArtifacts as it contains methods for // final as well as intermediate artifacts. -final class IntermediateArtifacts { +public final class IntermediateArtifacts { /** * Extension used on the temporary zipped dsym bundle location. Must contain {@code .dSYM} for @@ -100,6 +102,16 @@ final class IntermediateArtifacts { } /** + * Returns a derived artifact in the genfiles directory obtained by appending some extension to + * the end of the {@link PathFragment} corresponding to the owner {@link Label}. + */ + private Artifact appendExtensionInGenfiles(String extension) { + PathFragment name = new PathFragment(ruleContext.getLabel().getName()); + return scopedArtifact( + name.replaceName(name.getBaseName() + extension), /* inGenfiles = */ true); + } + + /** * The output of using {@code actoolzip} to run {@code actool} for a given bundle which is * merged under the {@code .app} or {@code .bundle} directory root. */ @@ -161,23 +173,34 @@ final class IntermediateArtifacts { return appendExtension("_lipobin"); } - private Artifact scopedArtifact(PathFragment scopeRelative) { + private Artifact scopedArtifact(PathFragment scopeRelative, boolean inGenfiles) { + Root root = + inGenfiles + ? ruleContext.getConfiguration().getGenfilesDirectory() + : ruleContext.getConfiguration().getBinDirectory(); if (scopingLabel.isPresent()) { // The path of this artifact will be // RULE_PACKAGE/_intermediate_scoped/RULE_LABEL/SCOPING_PACKAGE/SCOPING_LABEL/SCOPERELATIVE - return ruleContext.getUniqueDirectoryArtifact("_intermediate_scoped", - scopingLabel.get().getPackageIdentifier().getPathFragment() + return ruleContext.getUniqueDirectoryArtifact( + "_intermediate_scoped", + scopingLabel + .get() + .getPackageIdentifier() + .getPathFragment() .getRelative(scopingLabel.get().getName()) .getRelative(scopeRelative), - ruleContext.getConfiguration().getBinDirectory()); + root); } else { // The path of this artifact will be // RULE_PACKAGE/SCOPERELATIVE - return ruleContext.getPackageRelativeArtifact(scopeRelative, - ruleContext.getConfiguration().getBinDirectory()); + return ruleContext.getPackageRelativeArtifact(scopeRelative, root); } } + private Artifact scopedArtifact(PathFragment scopeRelative) { + return scopedArtifact(scopeRelative, /* inGenfiles = */ false); + } + /** * The {@code .a} file which contains all the compiled sources for a rule. */ @@ -339,4 +362,23 @@ final class IntermediateArtifacts { public Artifact dotdFile(Artifact source) { return inUniqueObjsDir(source, ".d"); } + + /** + * {@link CppModuleMap} that provides the clang module map for this target. + */ + public CppModuleMap moduleMap() { + if (!ObjcRuleClasses.objcConfiguration(ruleContext).moduleMapsEnabled()) { + throw new IllegalStateException(); + } + String moduleName = + ruleContext + .getLabel() + .toString() + .replace("//", "") + .replace("/", "_") + .replace(":", "_"); + // To get Swift to pick up module maps, we need to name them "module.modulemap" and have their + // parent directory in the module map search paths. + return new CppModuleMap(appendExtensionInGenfiles(".modulemaps/module.modulemap"), moduleName); + } } diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/J2ObjcLibrary.java b/src/main/java/com/google/devtools/build/lib/rules/objc/J2ObjcLibrary.java index 0281538fbe..2e2b23eca0 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/J2ObjcLibrary.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/J2ObjcLibrary.java @@ -27,6 +27,7 @@ import com.google.devtools.build.lib.analysis.RunfilesProvider; import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; import com.google.devtools.build.lib.packages.Type; import com.google.devtools.build.lib.rules.RuleConfiguredTargetFactory; +import com.google.devtools.build.lib.rules.cpp.CppModuleMap; import com.google.devtools.build.lib.rules.java.J2ObjcConfiguration; import com.google.devtools.build.lib.vfs.PathFragment; @@ -97,6 +98,10 @@ public class J2ObjcLibrary implements RuleConfiguredTargetFactory { ImmutableList.of(j2objcSource.getObjcFilePath(), genDirHeaderSearchPath)); } + if (ObjcRuleClasses.objcConfiguration(ruleContext).moduleMapsEnabled()) { + configureModuleMap(ruleContext, objcProviderBuilder, j2ObjcSrcsProvider); + } + ObjcProvider objcProvider = objcProviderBuilder.build(); xcodeSupport.addXcodeSettings(xcodeProviderBuilder, objcProvider, LIBRARY_STATIC); @@ -111,6 +116,21 @@ public class J2ObjcLibrary implements RuleConfiguredTargetFactory { .build(); } + /** + * Configures a module map for all the sources in {@code j2ObjcSrcsProvider}, registering + * an action to generate the module map and exposing that module map through {@code objcProvider}. + */ + private void configureModuleMap( + RuleContext ruleContext, + ObjcProvider.Builder objcProvider, + J2ObjcSrcsProvider j2ObjcSrcsProvider) { + new CompilationSupport(ruleContext).registerJ2ObjcGenerateModuleMapAction(j2ObjcSrcsProvider); + + CppModuleMap moduleMap = ObjcRuleClasses.intermediateArtifacts(ruleContext).moduleMap(); + objcProvider.add(ObjcProvider.MODULE_MAP, moduleMap.getArtifact()); + objcProvider.add(ObjcProvider.TOP_LEVEL_MODULE_MAP, moduleMap); + } + private static void checkAttributes(RuleContext ruleContext) { checkAttributes(ruleContext, "deps"); checkAttributes(ruleContext, "exports"); diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcCommandLineOptions.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcCommandLineOptions.java index c4f70e36fe..08a69a6818 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcCommandLineOptions.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcCommandLineOptions.java @@ -152,12 +152,23 @@ public class ObjcCommandLineOptions extends FragmentOptions { help = "Uses these strings as objc fastbuild compiler options.") public List<String> fastbuildOptions; - @Option(name = "objc_enable_binary_stripping", - defaultValue = "false", - category = "flags", - help = "Whether to perform symbol and dead-code strippings on linked binaries. Binary " - + "strippings will be performed if both this flag and --compilationMode=opt are " - + "specified.") + @Option( + name = "experimental_objc_enable_module_maps", + defaultValue = "false", + category = "undocumented", + help = "Enables module map generation and interpretation." + ) + public boolean enableModuleMaps; + + @Option( + name = "objc_enable_binary_stripping", + defaultValue = "false", + category = "flags", + help = + "Whether to perform symbol and dead-code strippings on linked binaries. Binary " + + "strippings will be performed if both this flag and --compilationMode=opt are " + + "specified." + ) public boolean enableBinaryStripping; // This option exists because two configurations are not allowed to have the same cache key 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 657062b730..775c53db8e 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 @@ -37,11 +37,13 @@ import static com.google.devtools.build.lib.rules.objc.ObjcProvider.INCLUDE_SYST import static com.google.devtools.build.lib.rules.objc.ObjcProvider.INSTRUMENTED_SOURCE; import static com.google.devtools.build.lib.rules.objc.ObjcProvider.LIBRARY; import static com.google.devtools.build.lib.rules.objc.ObjcProvider.LINKED_BINARY; +import static com.google.devtools.build.lib.rules.objc.ObjcProvider.MODULE_MAP; import static com.google.devtools.build.lib.rules.objc.ObjcProvider.SDK_DYLIB; import static com.google.devtools.build.lib.rules.objc.ObjcProvider.SDK_FRAMEWORK; import static com.google.devtools.build.lib.rules.objc.ObjcProvider.SOURCE; import static com.google.devtools.build.lib.rules.objc.ObjcProvider.STORYBOARD; import static com.google.devtools.build.lib.rules.objc.ObjcProvider.STRINGS; +import static com.google.devtools.build.lib.rules.objc.ObjcProvider.TOP_LEVEL_MODULE_MAP; import static com.google.devtools.build.lib.rules.objc.ObjcProvider.WEAK_SDK_FRAMEWORK; import static com.google.devtools.build.lib.rules.objc.ObjcProvider.XCASSETS_DIR; import static com.google.devtools.build.lib.rules.objc.ObjcProvider.XCDATAMODEL; @@ -63,10 +65,12 @@ import com.google.devtools.build.lib.packages.Type; import com.google.devtools.build.lib.rules.cpp.CcCommon; import com.google.devtools.build.lib.rules.cpp.CcLinkParamsProvider; import com.google.devtools.build.lib.rules.cpp.CppCompilationContext; +import com.google.devtools.build.lib.rules.cpp.CppModuleMap; import com.google.devtools.build.lib.util.FileType; import com.google.devtools.build.lib.util.RegexFilter; import com.google.devtools.build.lib.vfs.PathFragment; +import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -188,6 +192,46 @@ public final class ObjcCommon { } return optionsProvider.getCopts(); } + + /** + * The clang module maps of direct dependencies of this rule. These are needed to generate + * this rule's module map. + */ + public List<CppModuleMap> moduleMapsForDirectDeps() { + // Make sure all dependencies that have headers are included here. If a module map is missing, + // its private headers will be treated as public! + ArrayList<CppModuleMap> moduleMaps = new ArrayList<>(); + collectModuleMapsFromAttributeIfExists(moduleMaps, "deps"); + collectModuleMapsFromAttributeIfExists(moduleMaps, "non_propagated_deps"); + return moduleMaps; + } + + /** + * Collects all module maps from the targets in a certain attribute and adds them into + * {@code moduleMaps}. + * + * @param moduleMaps an {@link ArrayList} to collect the module maps into + * @param attribute the name of a label list attribute to collect module maps from + */ + private void collectModuleMapsFromAttributeIfExists( + ArrayList<CppModuleMap> moduleMaps, String attribute) { + if (ruleContext.attributes().has(attribute, Type.LABEL_LIST)) { + Iterable<ObjcProvider> providers = + ruleContext.getPrerequisites(attribute, Mode.TARGET, ObjcProvider.class); + for (ObjcProvider provider : providers) { + moduleMaps.addAll(provider.get(TOP_LEVEL_MODULE_MAP).toCollection()); + } + } + } + + /** + * Returns whether this target uses language features that require clang modules, such as + * @import. + */ + public boolean enableModules() { + return ruleContext.attributes().has("enable_modules", Type.BOOLEAN) + && ruleContext.attributes().get("enable_modules", Type.BOOLEAN); + } } /** @@ -248,6 +292,7 @@ public final class ObjcCommon { private Iterable<PathFragment> userHeaderSearchPaths = ImmutableList.of(); private IntermediateArtifacts intermediateArtifacts; private boolean alwayslink; + private boolean hasModuleMap; private Iterable<Artifact> extraImportLibraries = ImmutableList.of(); private Optional<Artifact> linkedBinary = Optional.absent(); private Optional<Artifact> breakpadFile = Optional.absent(); @@ -341,6 +386,17 @@ public final class ObjcCommon { } /** + * Specifies that this target has a clang module map. This should be called if this target + * compiles sources or exposes headers for other targets to use. Note that this does not add + * the action to generate the module map. It simply indicates that it should be added to the + * provider. + */ + Builder setHasModuleMap() { + this.hasModuleMap = true; + return this; + } + + /** * Adds additional static libraries to be linked into the final ObjC application bundle. */ Builder addExtraImportLibraries(Iterable<Artifact> extraImportLibraries) { @@ -506,6 +562,12 @@ public final class ObjcCommon { } } + if (hasModuleMap && ObjcRuleClasses.objcConfiguration(context).moduleMapsEnabled()) { + CppModuleMap moduleMap = intermediateArtifacts.moduleMap(); + objcProvider.add(MODULE_MAP, moduleMap.getArtifact()); + objcProvider.add(TOP_LEVEL_MODULE_MAP, moduleMap); + } + objcProvider.addAll(LINKED_BINARY, linkedBinary.asSet()) .addAll(BREAKPAD_FILE, breakpadFile.asSet()); diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcConfiguration.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcConfiguration.java index 8f073a18ad..9209c4ebda 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcConfiguration.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcConfiguration.java @@ -66,6 +66,7 @@ public class ObjcConfiguration extends BuildConfiguration.Fragment { private final boolean perProtoIncludes; private final List<String> fastbuildOptions; private final boolean enableBinaryStripping; + private final boolean moduleMapsEnabled; private final ConfigurationDistinguisher configurationDistinguisher; @Nullable private final Path clientWorkspaceRoot; @@ -104,6 +105,7 @@ public class ObjcConfiguration extends BuildConfiguration.Fragment { this.perProtoIncludes = objcOptions.perProtoIncludes; this.fastbuildOptions = ImmutableList.copyOf(objcOptions.fastbuildOptions); this.enableBinaryStripping = objcOptions.enableBinaryStripping; + this.moduleMapsEnabled = objcOptions.enableModuleMaps; this.configurationDistinguisher = objcOptions.configurationDistinguisher; this.clientWorkspaceRoot = directories != null ? directories.getWorkspace() : null; } @@ -261,6 +263,13 @@ public class ObjcConfiguration extends BuildConfiguration.Fragment { } /** + * Whether module map generation and interpretation is enabled. + */ + public boolean moduleMapsEnabled() { + return moduleMapsEnabled; + } + + /** * Returns the unique identifier distinguishing configurations that are otherwise the same. * * <p>Use this value for situations in which two configurations create two outputs that are the diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcImport.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcImport.java index c3b35c22cc..143858de7d 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcImport.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcImport.java @@ -16,6 +16,7 @@ package com.google.devtools.build.lib.rules.objc; import static com.google.devtools.build.lib.rules.objc.XcodeProductType.LIBRARY_STATIC; +import com.google.common.base.Optional; 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; @@ -32,21 +33,24 @@ import com.google.devtools.build.lib.rules.objc.ObjcCommon.ResourceAttributes; public class ObjcImport implements RuleConfiguredTargetFactory { @Override public ConfiguredTarget create(RuleContext ruleContext) throws InterruptedException { - ObjcCommon common = new ObjcCommon.Builder(ruleContext) - .setCompilationAttributes(new CompilationAttributes(ruleContext)) - .setResourceAttributes(new ResourceAttributes(ruleContext)) - .setIntermediateArtifacts(ObjcRuleClasses.intermediateArtifacts(ruleContext)) - .setAlwayslink(ruleContext.attributes().get("alwayslink", Type.BOOLEAN)) - .addExtraImportLibraries( - ruleContext.getPrerequisiteArtifacts("archives", Mode.TARGET).list()) - .addDepObjcProviders( - ruleContext.getPrerequisites("bundles", Mode.TARGET, ObjcProvider.class)) - .build(); + ObjcCommon common = + new ObjcCommon.Builder(ruleContext) + .setCompilationAttributes(new CompilationAttributes(ruleContext)) + .setResourceAttributes(new ResourceAttributes(ruleContext)) + .setIntermediateArtifacts(ObjcRuleClasses.intermediateArtifacts(ruleContext)) + .setAlwayslink(ruleContext.attributes().get("alwayslink", Type.BOOLEAN)) + .setHasModuleMap() + .addExtraImportLibraries( + ruleContext.getPrerequisiteArtifacts("archives", Mode.TARGET).list()) + .addDepObjcProviders( + ruleContext.getPrerequisites("bundles", Mode.TARGET, ObjcProvider.class)) + .build(); XcodeProvider.Builder xcodeProviderBuilder = new XcodeProvider.Builder(); NestedSetBuilder<Artifact> filesToBuild = NestedSetBuilder.stableOrder(); new CompilationSupport(ruleContext) + .registerGenerateModuleMapAction(Optional.<CompilationArtifacts>absent()) .addXcodeSettings(xcodeProviderBuilder, common) .validateAttributes(); diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcLibrary.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcLibrary.java index 98f4fe9597..e03d9ba814 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcLibrary.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcLibrary.java @@ -68,14 +68,15 @@ public class ObjcLibrary implements RuleConfiguredTargetFactory { .addDepObjcProviders(ruleContext.getPrerequisites("deps", Mode.TARGET, ObjcProvider.class)) .addDepObjcProviders( ruleContext.getPrerequisites("bundles", Mode.TARGET, ObjcProvider.class)) - .addNonPropagatedDepObjcProviders(ruleContext.getPrerequisites("non_propagated_deps", - Mode.TARGET, ObjcProvider.class)) + .addNonPropagatedDepObjcProviders( + ruleContext.getPrerequisites("non_propagated_deps", Mode.TARGET, ObjcProvider.class)) .addDepCcHeaderProviders( ruleContext.getPrerequisites("deps", Mode.TARGET, CppCompilationContext.class)) .addDepCcLinkProviders( ruleContext.getPrerequisites("deps", Mode.TARGET, CcLinkParamsProvider.class)) .setIntermediateArtifacts(ObjcRuleClasses.intermediateArtifacts(ruleContext)) .setAlwayslink(alwayslink) + .setHasModuleMap() .addExtraImportLibraries(extraImportLibraries) .addDepObjcProviders(extraDepObjcProviders) .build(); diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProtoLibrary.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProtoLibrary.java index 4057c25d50..8349b48d29 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProtoLibrary.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProtoLibrary.java @@ -175,13 +175,16 @@ public class ObjcProtoLibrary implements RuleConfiguredTargetFactory { } ImmutableSet<PathFragment> searchPathEntries = searchPathEntriesBuilder.build(); - ObjcCommon common = new ObjcCommon.Builder(ruleContext) - .setCompilationArtifacts(compilationArtifacts) - .addUserHeaderSearchPaths(searchPathEntries) - .addDepObjcProviders(ruleContext.getPrerequisites( - ObjcProtoLibraryRule.LIBPROTOBUF_ATTR, Mode.TARGET, ObjcProvider.class)) - .setIntermediateArtifacts(intermediateArtifacts) - .build(); + ObjcCommon common = + new ObjcCommon.Builder(ruleContext) + .setCompilationArtifacts(compilationArtifacts) + .addUserHeaderSearchPaths(searchPathEntries) + .addDepObjcProviders( + ruleContext.getPrerequisites( + ObjcProtoLibraryRule.LIBPROTOBUF_ATTR, Mode.TARGET, ObjcProvider.class)) + .setIntermediateArtifacts(intermediateArtifacts) + .setHasModuleMap() + .build(); NestedSetBuilder<Artifact> filesToBuild = NestedSetBuilder.<Artifact>stableOrder() .addAll(common.getCompiledArchive().asSet()) 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 e8be3684fa..bbee7c675c 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 @@ -27,6 +27,7 @@ import com.google.devtools.build.lib.collect.nestedset.NestedSet; import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; import com.google.devtools.build.lib.collect.nestedset.Order; import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; +import com.google.devtools.build.lib.rules.cpp.CppModuleMap; import com.google.devtools.build.lib.rules.cpp.LinkerInputs; import com.google.devtools.build.lib.vfs.PathFragment; import com.google.devtools.build.xcode.xcodegen.proto.XcodeGenProtos.TargetControl; @@ -150,6 +151,19 @@ public final class ObjcProvider implements TransitiveInfoProvider { public static final Key<Flag> FLAG = new Key<>(STABLE_ORDER); /** + * Clang module maps, used to enforce proper use of private header files. + */ + public static final Key<Artifact> MODULE_MAP = new Key<>(STABLE_ORDER); + + /** + * Information about this provider's module map, in the form of a {@link CppModuleMap}. This + * is intransitive, and can be used to get just the target's module map to pass to clang or to + * get the module maps for direct but not transitive dependencies. You should only add module maps + * for this key using {@link Builder#addWithoutPropagating}. + */ + public static final Key<CppModuleMap> TOP_LEVEL_MODULE_MAP = new Key<>(STABLE_ORDER); + + /** * Merge zips to include in the bundle. The entries of these zip files are included in the final * bundle with the same path. The entries in the merge zips should not include the bundle root * path (e.g. {@code Foo.app}). @@ -287,9 +301,10 @@ public final class ObjcProvider implements TransitiveInfoProvider { } @SuppressWarnings({"rawtypes", "unchecked"}) - private void uncheckedAddAll(Key key, Iterable toAdd) { - maybeAddEmptyBuilder(items, key); - items.get(key).addAll(toAdd); + private void uncheckedAddAll(Key key, Iterable toAdd, boolean propagate) { + Map<Key<?>, NestedSetBuilder<?>> set = propagate ? items : nonPropagatedItems; + maybeAddEmptyBuilder(set, key); + set.get(key).addAll(toAdd); } @SuppressWarnings({"rawtypes", "unchecked"}) @@ -357,7 +372,7 @@ public final class ObjcProvider implements TransitiveInfoProvider { * Add element, and propagate it to any (transitive) dependers on this ObjcProvider. */ public <E> Builder add(Key<E> key, E toAdd) { - uncheckedAddAll(key, ImmutableList.of(toAdd)); + uncheckedAddAll(key, ImmutableList.of(toAdd), true); return this; } @@ -365,7 +380,17 @@ public final class ObjcProvider implements TransitiveInfoProvider { * Add elements in toAdd, and propagate them to any (transitive) dependers on this ObjcProvider. */ public <E> Builder addAll(Key<E> key, Iterable<? extends E> toAdd) { - uncheckedAddAll(key, toAdd); + uncheckedAddAll(key, toAdd, true); + return this; + } + + /** + * Add element, but don't propagate dependers on this ObjcProvider. These elements will be + * exposed to {@link #get(Key)} calls, but not to any ObjcProviders which add this provider to + * themselves. + */ + public <E> Builder addWithoutPropagating(Key<E> key, E toAdd) { + uncheckedAddAll(key, ImmutableList.of(toAdd), false); return this; } 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 04be1426cd..7b53bcae59 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 @@ -74,8 +74,8 @@ public class ObjcRuleClasses { throw new UnsupportedOperationException("static-only"); } - static IntermediateArtifacts intermediateArtifacts(RuleContext ruleContext) { - return new IntermediateArtifacts(ruleContext, /*archiveFileNameSuffix=*/""); + public static IntermediateArtifacts intermediateArtifacts(RuleContext ruleContext) { + return new IntermediateArtifacts(ruleContext, /*archiveFileNameSuffix=*/ ""); } /** @@ -617,68 +617,75 @@ public class ObjcRuleClasses { public RuleClass build(Builder builder, RuleDefinitionEnvironment env) { return builder /* <!-- #BLAZE_RULE($objc_compiling_rule).ATTRIBUTE(srcs) --> - The list of C, C++, Objective-C, and Objective-C++ source and header - files that are processed to create the library target. - ${SYNOPSIS} - These are your checked-in files, plus any generated files. - Source files are compiled into .o files with Clang. Header files - may be included/imported by any source or header in the srcs attribute - of this target, but not by headers in hdrs or any targets that depend - on this rule. - <!-- #END_BLAZE_RULE.ATTRIBUTE -->*/ - .add(attr("srcs", LABEL_LIST) - .direct_compile_time_input() - .allowedFileTypes(SRCS_TYPE)) + The list of C, C++, Objective-C, and Objective-C++ source and header + files that are processed to create the library target. + ${SYNOPSIS} + These are your checked-in files, plus any generated files. + Source files are compiled into .o files with Clang. Header files + may be included/imported by any source or header in the srcs attribute + of this target, but not by headers in hdrs or any targets that depend + on this rule. + <!-- #END_BLAZE_RULE.ATTRIBUTE -->*/ + .add(attr("srcs", LABEL_LIST).direct_compile_time_input().allowedFileTypes(SRCS_TYPE)) /* <!-- #BLAZE_RULE($objc_compiling_rule).ATTRIBUTE(non_arc_srcs) --> - The list of Objective-C files that are processed to create the - library target that DO NOT use ARC. - ${SYNOPSIS} - The files in this attribute are treated very similar to those in the - srcs attribute, but are compiled without ARC enabled. - <!-- #END_BLAZE_RULE.ATTRIBUTE -->*/ - .add(attr("non_arc_srcs", LABEL_LIST) - .direct_compile_time_input() - .allowedFileTypes(NON_ARC_SRCS_TYPE)) + The list of Objective-C files that are processed to create the + library target that DO NOT use ARC. + ${SYNOPSIS} + The files in this attribute are treated very similar to those in the + srcs attribute, but are compiled without ARC enabled. + <!-- #END_BLAZE_RULE.ATTRIBUTE -->*/ + .add( + attr("non_arc_srcs", LABEL_LIST) + .direct_compile_time_input() + .allowedFileTypes(NON_ARC_SRCS_TYPE)) /* <!-- #BLAZE_RULE($objc_compiling_rule).ATTRIBUTE(pch) --> - Header file to prepend to every source file being compiled (both arc - and non-arc). - ${SYNOPSIS} - Note that the file will not be precompiled - this is simply a - convenience, not a build-speed enhancement. - <!-- #END_BLAZE_RULE.ATTRIBUTE -->*/ - .add(attr("pch", LABEL) - .direct_compile_time_input() - .allowedFileTypes(FileType.of(".pch"))) + Header file to prepend to every source file being compiled (both arc + and non-arc). + ${SYNOPSIS} + Note that the file will not be precompiled - this is simply a + convenience, not a build-speed enhancement. + <!-- #END_BLAZE_RULE.ATTRIBUTE -->*/ + .add(attr("pch", LABEL).direct_compile_time_input().allowedFileTypes(FileType.of(".pch"))) /* <!-- #BLAZE_RULE($objc_compiling_rule).ATTRIBUTE(deps) --> - The list of targets that are linked together to form the final bundle. - ${SYNOPSIS} - <!-- #END_BLAZE_RULE.ATTRIBUTE -->*/ - .override(attr("deps", LABEL_LIST) - .direct_compile_time_input() - .allowedRuleClasses(ALLOWED_DEPS_RULE_CLASSES) - .allowedFileTypes()) + The list of targets that are linked together to form the final bundle. + ${SYNOPSIS} + <!-- #END_BLAZE_RULE.ATTRIBUTE -->*/ + .override( + attr("deps", LABEL_LIST) + .direct_compile_time_input() + .allowedRuleClasses(ALLOWED_DEPS_RULE_CLASSES) + .allowedFileTypes()) /* <!-- #BLAZE_RULE($objc_compiling_rule).ATTRIBUTE(non_propagated_deps) --> - The list of targets that are required in order to build this target, - but which are not included in the final bundle. - ${SYNOPSIS} - This attribute should only rarely be used, and probably only for proto - dependencies. - ${SYNOPSIS} - <!-- #END_BLAZE_RULE.ATTRIBUTE -->*/ - .add(attr("non_propagated_deps", LABEL_LIST) - .direct_compile_time_input() - .allowedRuleClasses(ALLOWED_DEPS_RULE_CLASSES) - .allowedFileTypes()) + The list of targets that are required in order to build this target, + but which are not included in the final bundle. + ${SYNOPSIS} + This attribute should only rarely be used, and probably only for proto + dependencies. + ${SYNOPSIS} + <!-- #END_BLAZE_RULE.ATTRIBUTE -->*/ + .add( + attr("non_propagated_deps", LABEL_LIST) + .direct_compile_time_input() + .allowedRuleClasses(ALLOWED_DEPS_RULE_CLASSES) + .allowedFileTypes()) /* <!-- #BLAZE_RULE($objc_compiling_rule).ATTRIBUTE(defines) --> - Extra <code>-D</code> flags to pass to the compiler. They should be in - the form <code>KEY=VALUE</code> or simply <code>KEY</code> and are - passed not only the compiler for this target (as <code>copts</code> - are) but also to all <code>objc_</code> dependers of this target. - ${SYNOPSIS} - Subject to <a href="#make_variables">"Make variable"</a> substitution and - <a href="#sh-tokenization">Bourne shell tokenization</a>. - <!-- #END_BLAZE_RULE.ATTRIBUTE -->*/ + Extra <code>-D</code> flags to pass to the compiler. They should be in + the form <code>KEY=VALUE</code> or simply <code>KEY</code> and are + passed not only the compiler for this target (as <code>copts</code> + are) but also to all <code>objc_</code> dependers of this target. + ${SYNOPSIS} + Subject to <a href="#make_variables">"Make variable"</a> substitution and + <a href="#sh-tokenization">Bourne shell tokenization</a>. + <!-- #END_BLAZE_RULE.ATTRIBUTE -->*/ .add(attr("defines", STRING_LIST)) + /* <!-- #BLAZE_RULE($objc_compiling_rule).ATTRIBUTE(enable_modules) --> + Enables clang module support (via -fmodules). + ${SYNOPSIS} + Setting this to 1 will allow you to @import system headers and other targets: + @import UIKit; + @import path_to_package_target; + <!-- #END_BLAZE_RULE.ATTRIBUTE -->*/ + .add(attr("enable_modules", BOOLEAN)) .build(); } @Override |