Skip to content
This repository has been archived by the owner on Nov 10, 2023. It is now read-only.

Commit

Permalink
Allow creation of an "umbrella directory" module map
Browse files Browse the repository at this point in the history
Summary:
This adds a separate mode for module map generation that uses an umbrella directory declaration, rather than an umbrella header declaration.

This fits Buck's general approach to headers better - it's kind of like an autoglob for every header in the library, without the need to either generate or manually maintain a valid umbrella header.

It's implemented by adding a new `ModuleMapMode` enum, and a new case to `HeaderMode` to differentiate between the two associated forms of module maps. I decided to have it specified at the `.buckconfig` level, rather than per-library, as this is simpler and is sufficient for the use-case.

Reviewed By: williamtwilson

shipit-source-id: 10842bbd24
  • Loading branch information
natestedman authored and facebook-github-bot committed Oct 2, 2019
1 parent ef8007c commit 89981e5
Show file tree
Hide file tree
Showing 33 changed files with 436 additions and 27 deletions.
8 changes: 8 additions & 0 deletions src/com/facebook/buck/apple/AppleConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package com.facebook.buck.apple;

import com.facebook.buck.apple.clang.ModuleMapMode;
import com.facebook.buck.apple.toolchain.ApplePlatform;
import com.facebook.buck.core.config.BuckConfig;
import com.facebook.buck.core.config.ConfigView;
Expand Down Expand Up @@ -462,6 +463,13 @@ public boolean shouldWorkAroundDsymutilLTOStackOverflowBug() {
APPLE_SECTION, "work_around_dsymutil_lto_stack_overflow_bug", false);
}

/** @return The module map mode to use for modular libraries. */
public ModuleMapMode moduleMapMode() {
return delegate
.getEnum(APPLE_SECTION, "modulemap_mode", ModuleMapMode.class)
.orElse(ModuleMapMode.UMBRELLA_HEADER);
}

public Path shellPath() {
return delegate.getPath(APPLE_SECTION, "xcode_build_script_shell").orElse(Paths.get("/bin/sh"));
}
Expand Down
12 changes: 8 additions & 4 deletions src/com/facebook/buck/apple/AppleLibraryDescription.java
Original file line number Diff line number Diff line change
Expand Up @@ -635,7 +635,7 @@ BuildRule requireSingleArchUnstrippedBuildRule(
&& libType.isPresent()
&& libType.get().equals(Type.EXPORTED_HEADERS)
&& headerMode.isPresent()
&& headerMode.get().equals(HeaderMode.SYMLINK_TREE_WITH_MODULEMAP)) {
&& headerMode.get().includesModuleMap()) {
return createExportedModuleSymlinkTreeBuildRule(
buildTarget,
context.getProjectFilesystem(),
Expand Down Expand Up @@ -711,7 +711,7 @@ private HeaderSymlinkTree createExportedModuleSymlinkTreeBuildRule(
return CxxDescriptionEnhancer.createHeaderSymlinkTree(
buildTarget,
projectFilesystem,
HeaderMode.SYMLINK_TREE_WITH_MODULEMAP,
HeaderMode.forModuleMapMode(appleConfig.moduleMapMode()),
headers.build(),
HeaderVisibility.PUBLIC);
}
Expand All @@ -733,7 +733,11 @@ private HeaderSymlinkTree createUnderlyingModuleSymlinkTreeBuildRule(

Path root = BuildTargetPaths.getGenPath(projectFilesystem, buildTarget, "%s");
return CxxPreprocessables.createHeaderSymlinkTreeBuildRule(
buildTarget, projectFilesystem, root, headers, HeaderMode.SYMLINK_TREE_WITH_MODULEMAP);
buildTarget,
projectFilesystem,
root,
headers,
HeaderMode.forModuleMapMode(appleConfig.moduleMapMode()));
}

<U> Optional<U> createMetadataForLibrary(
Expand Down Expand Up @@ -910,7 +914,7 @@ private <U> Optional<U> createCxxPreprocessorInputMetadata(
.withAppendedFlavors(
CxxLibraryDescription.Type.EXPORTED_HEADERS.getFlavor(),
platformEntry.getKey(),
HeaderMode.SYMLINK_TREE_WITH_MODULEMAP.getFlavor()));
HeaderMode.forModuleMapMode(appleConfig.moduleMapMode()).getFlavor()));
cxxPreprocessorInputBuilder.addIncludes(
CxxSymlinkTreeHeaders.from(symlinkTree, CxxPreprocessables.IncludeType.LOCAL));
CxxPreprocessorInput cxxPreprocessorInput = cxxPreprocessorInputBuilder.build();
Expand Down
1 change: 1 addition & 0 deletions src/com/facebook/buck/apple/BUCK
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ java_library_with_plugins(
],
deps = [
"//src/com/facebook/buck/android/toolchain/ndk:ndk",
"//src/com/facebook/buck/apple/clang:clang",
"//src/com/facebook/buck/apple/platform_type:type",
"//src/com/facebook/buck/apple/simulator:simulator",
"//src/com/facebook/buck/apple/toolchain:toolchain",
Expand Down
47 changes: 47 additions & 0 deletions src/com/facebook/buck/apple/clang/ModuleMapFactory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright 2014-present Facebook, Inc.
*
* 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.facebook.buck.apple.clang;

/**
* Creates module map instances.
*
* <p>Use this instead of directly creating UmbrellaHeaderModuleMap or UmbrellaDirectoryModuleMap
* instances.
*/
public class ModuleMapFactory {

/**
* Creates a module map.
*
* @param moduleName The name of the module.
* @param moduleMapMode The module map mode to use.
* @param swiftMode The Swift mode to use for umbrella header module maps. This parameter is
* unused with umbrella directory module maps.
* @return A module map instance.
*/
public static ModuleMap createModuleMap(
String moduleName, ModuleMapMode moduleMapMode, UmbrellaHeaderModuleMap.SwiftMode swiftMode) {
switch (moduleMapMode) {
case UMBRELLA_HEADER:
return new UmbrellaHeaderModuleMap(moduleName, swiftMode);
case UMBRELLA_DIRECTORY:
return new UmbrellaDirectoryModuleMap(moduleName);
}

throw new RuntimeException();
}
}
26 changes: 26 additions & 0 deletions src/com/facebook/buck/apple/clang/ModuleMapMode.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright 2014-present Facebook, Inc.
*
* 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.facebook.buck.apple.clang;

/** Enumerates the module map generation modes that Buck supports. */
public enum ModuleMapMode {
/** Generate a module map that requires an umbrella header. */
UMBRELLA_HEADER,

/** Generate a module map that uses the library's headers in an umbrella directory. */
UMBRELLA_DIRECTORY,
}
66 changes: 66 additions & 0 deletions src/com/facebook/buck/apple/clang/UmbrellaDirectoryModuleMap.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Copyright 2014-present Facebook, Inc.
*
* 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.facebook.buck.apple.clang;

import com.google.common.base.Objects;
import javax.annotation.Nullable;
import org.stringtemplate.v4.ST;

/**
* A module map using an umbrella directory, rather than an umbrella header. This removes the need
* to maintain or generate an umbrella header: all exported headers in the library will be included
* in the module automatically.
*/
public class UmbrellaDirectoryModuleMap implements ModuleMap {
private final String moduleName;

@Nullable private String generatedModule;
private static final String template =
"module <module_name> {\n"
+ " umbrella \".\"\n"
+ "\n"
+ " module * { export * }\n"
+ "}\n"
+ "\n";

public UmbrellaDirectoryModuleMap(String moduleName) {
this.moduleName = moduleName;
}

@Override
public String render() {
if (this.generatedModule == null) {
ST st = new ST(template).add("module_name", moduleName);
this.generatedModule = st.render();
}
return this.generatedModule;
}

@Override
public boolean equals(Object obj) {
if (!(obj instanceof UmbrellaDirectoryModuleMap)) {
return false;
}
UmbrellaDirectoryModuleMap that = (UmbrellaDirectoryModuleMap) obj;
return Objects.equal(this.moduleName, that.moduleName);
}

@Override
public int hashCode() {
return Objects.hashCode(moduleName);
}
}
9 changes: 7 additions & 2 deletions src/com/facebook/buck/cxx/CxxPreprocessables.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package com.facebook.buck.cxx;

import com.facebook.buck.apple.clang.ModuleMapMode;
import com.facebook.buck.core.model.BuildTarget;
import com.facebook.buck.core.rules.ActionGraphBuilder;
import com.facebook.buck.core.rules.BuildRule;
Expand Down Expand Up @@ -138,8 +139,12 @@ public static HeaderSymlinkTree createHeaderSymlinkTreeBuildRule(
switch (headerMode) {
case SYMLINK_TREE_WITH_HEADER_MAP:
return HeaderSymlinkTreeWithHeaderMap.create(target, filesystem, root, links);
case SYMLINK_TREE_WITH_MODULEMAP:
return HeaderSymlinkTreeWithModuleMap.create(target, filesystem, root, links);
case SYMLINK_TREE_WITH_UMBRELLA_HEADER_MODULEMAP:
return HeaderSymlinkTreeWithModuleMap.create(
target, filesystem, root, links, ModuleMapMode.UMBRELLA_HEADER);
case SYMLINK_TREE_WITH_UMBRELLA_DIRECTORY_MODULEMAP:
return HeaderSymlinkTreeWithModuleMap.create(
target, filesystem, root, links, ModuleMapMode.UMBRELLA_DIRECTORY);
case HEADER_MAP_ONLY:
return new DirectHeaderMap(target, filesystem, root, links);
default:
Expand Down
48 changes: 46 additions & 2 deletions src/com/facebook/buck/cxx/toolchain/HeaderMode.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package com.facebook.buck.cxx.toolchain;

import com.facebook.buck.apple.clang.ModuleMapMode;
import com.facebook.buck.core.model.Flavor;
import com.facebook.buck.core.model.FlavorConvertible;
import com.facebook.buck.core.model.InternalFlavor;
Expand All @@ -34,9 +35,16 @@ public enum HeaderMode implements FlavorConvertible {
SYMLINK_TREE_WITH_HEADER_MAP,
/**
* Creates the tree of symbolic links of headers and creates a module map that references the
* symbolic links to the headers.
* symbolic links to the headers. The generated module map will refer to an umbrella header, with
* the same name as the library.
*/
SYMLINK_TREE_WITH_UMBRELLA_HEADER_MODULEMAP,
/**
* Creates the tree of symbolic links of headers and creates a module map that references the
* symbolic links to the headers. The generated module map will refer to an umbrella directory,
* avoiding the need for a valid and complete umbrella header.
*/
SYMLINK_TREE_WITH_MODULEMAP,
SYMLINK_TREE_WITH_UMBRELLA_DIRECTORY_MODULEMAP,
;

private final Flavor flavor;
Expand All @@ -54,4 +62,40 @@ public enum HeaderMode implements FlavorConvertible {
public Flavor getFlavor() {
return flavor;
}

/**
* Returns the appropriate header mode for module map mode.
*
* @param moduleMapMode The module map mode to convert.
* @return The equivalent header mode.
*/
public static HeaderMode forModuleMapMode(ModuleMapMode moduleMapMode) {
switch (moduleMapMode) {
case UMBRELLA_HEADER:
return SYMLINK_TREE_WITH_UMBRELLA_HEADER_MODULEMAP;
case UMBRELLA_DIRECTORY:
return SYMLINK_TREE_WITH_UMBRELLA_DIRECTORY_MODULEMAP;
}

throw new RuntimeException("Unexpected value of enum ModuleMapMode");
}

/**
* Returns whether or not the header mode will include a module map.
*
* @return true if the header mode will include a module map, otherwise false.
*/
public boolean includesModuleMap() {
switch (this) {
case SYMLINK_TREE_ONLY:
case SYMLINK_TREE_WITH_HEADER_MAP:
case HEADER_MAP_ONLY:
return false;
case SYMLINK_TREE_WITH_UMBRELLA_HEADER_MODULEMAP:
case SYMLINK_TREE_WITH_UMBRELLA_DIRECTORY_MODULEMAP:
return true;
}

throw new RuntimeException("Unexpected value of enum HeaderMode");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package com.facebook.buck.cxx.toolchain;

import com.facebook.buck.apple.clang.ModuleMapFactory;
import com.facebook.buck.apple.clang.ModuleMapMode;
import com.facebook.buck.apple.clang.UmbrellaHeader;
import com.facebook.buck.apple.clang.UmbrellaHeaderModuleMap;
import com.facebook.buck.core.build.buildable.context.BuildableContext;
Expand All @@ -38,24 +40,29 @@
public final class HeaderSymlinkTreeWithModuleMap extends HeaderSymlinkTree {

@AddToRuleKey private final Optional<String> moduleName;
@AddToRuleKey private final ModuleMapMode moduleMapMode;

private HeaderSymlinkTreeWithModuleMap(
BuildTarget target,
ProjectFilesystem filesystem,
Path root,
ImmutableMap<Path, SourcePath> links,
Optional<String> moduleName) {
Optional<String> moduleName,
ModuleMapMode moduleMapMode) {
super(target, filesystem, root, links);
this.moduleName = moduleName;
this.moduleMapMode = moduleMapMode;
}

public static HeaderSymlinkTreeWithModuleMap create(
BuildTarget target,
ProjectFilesystem filesystem,
Path root,
ImmutableMap<Path, SourcePath> links) {
ImmutableMap<Path, SourcePath> links,
ModuleMapMode moduleMapMode) {
Optional<String> moduleName = getModuleName(links);
return new HeaderSymlinkTreeWithModuleMap(target, filesystem, root, links, moduleName);
return new HeaderSymlinkTreeWithModuleMap(
target, filesystem, root, links, moduleName, moduleMapMode);
}

@Override
Expand All @@ -81,8 +88,9 @@ public ImmutableList<Step> getBuildSteps(
new ModuleMapStep(
getProjectFilesystem(),
moduleMapPath(getProjectFilesystem(), getBuildTarget(), moduleName),
new UmbrellaHeaderModuleMap(
ModuleMapFactory.createModuleMap(
moduleName,
moduleMapMode,
containsSwiftHeader(paths, moduleName)
? UmbrellaHeaderModuleMap.SwiftMode.INCLUDE_SWIFT_HEADER
: UmbrellaHeaderModuleMap.SwiftMode.NO_SWIFT)));
Expand Down
Loading

0 comments on commit 89981e5

Please sign in to comment.