Skip to content

Commit 1866db0

Browse files
committed
Auto generate module map file for C libs
1 parent 7661b27 commit 1866db0

File tree

21 files changed

+144
-51
lines changed

21 files changed

+144
-51
lines changed

Fixtures/ClangModules/CLibraryFlat/include/module.modulemap

Lines changed: 0 additions & 5 deletions
This file was deleted.

Fixtures/ClangModules/CLibrarySources/Sources/include/module.modulemap

Lines changed: 0 additions & 5 deletions
This file was deleted.

Fixtures/ClangModules/CLibraryiquote/Sources/Bar/include/module.modulemap

Lines changed: 0 additions & 5 deletions
This file was deleted.

Fixtures/ClangModules/CLibraryiquote/Sources/Foo/include/module.modulemap

Lines changed: 0 additions & 5 deletions
This file was deleted.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import PackageDescription
2+
3+
let package = Package(
4+
name: "ModuleMapGenerationCases",
5+
targets: [
6+
Target(name: "Baz", dependencies: ["Foo", "Bar", "Jaz"])]
7+
)
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#include "include/Bar.h"
2+
3+
int bar() {
4+
int a = 6;
5+
int b = a;
6+
a = b;
7+
return a;
8+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
int bar();
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import Foo
2+
import Bar
3+
import Jaz
4+
5+
let _ = foo()
6+
let _ = bar()
7+
let _ = jaz()
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#include "include/Foo/Foo.h"
2+
3+
int foo() {
4+
int a = 5;
5+
int b = a;
6+
a = b;
7+
return a;
8+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
int foo();
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#include "include/Jaz/Paz.h"
2+
3+
int jaz() {
4+
int a = 6;
5+
int b = a;
6+
a = b;
7+
return a;
8+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
int jaz();
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
2+
int noDir() {
3+
int a = 6;
4+
int b = a;
5+
a = b;
6+
return a;
7+
}

Fixtures/ClangModules/SwiftCMixed/Sources/SeaLib/include/module.modulemap

Lines changed: 0 additions & 5 deletions
This file was deleted.

Fixtures/DependencyResolution/External/CUsingCDep/Bar/Sources/SeaLover/include/module.modulemap

Lines changed: 0 additions & 5 deletions
This file was deleted.

Fixtures/DependencyResolution/External/CUsingCDep/Foo/include/module.modulemap

Lines changed: 0 additions & 5 deletions
This file was deleted.

Fixtures/DependencyResolution/External/SimpleCDep/Foo/include/module.modulemap

Lines changed: 0 additions & 5 deletions
This file was deleted.

Sources/Build/describe().swift

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,16 +40,10 @@ public func describe(prefix: String, _ conf: Configuration, _ modules: [Module],
4040
targets.append(compile, for: module)
4141

4242
case let module as ClangModule:
43-
//FIXME: Generate modulemaps if possible
44-
//Since we're not generating modulemaps currently we'll just emit empty module map file
45-
//if it not present
46-
if module.type == .Library && !module.moduleMapPath.isFile {
47-
try POSIX.mkdir(module.moduleMapPath.parentDirectory)
48-
try fopen(module.moduleMapPath, mode: .Write) { fp in
49-
try fputs("\n", fp)
50-
}
43+
if module.type == .Library {
44+
try module.generateModuleMap()
5145
}
52-
46+
5347
let (compile, mkdir) = Command.compile(clangModule: module, externalModules: externalModules, configuration: conf, prefix: prefix)
5448
commands.append(compile)
5549
commands.append(mkdir)

Sources/Build/misc.swift

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
*/
1010

1111
import func POSIX.getenv
12+
import func POSIX.mkdir
13+
import func POSIX.fopen
14+
import func libc.fclose
1215
import PackageType
1316
import Utility
1417

@@ -32,6 +35,83 @@ extension CModule {
3235
}
3336
}
3437

38+
extension ClangModule {
39+
40+
public enum ModuleMapError: ErrorProtocol {
41+
case UnsupportedIncludeLayoutForModule(String)
42+
}
43+
44+
public func generateModuleMap() throws {
45+
46+
//Return if module map is already present
47+
guard !moduleMapPath.isFile else {
48+
return
49+
}
50+
51+
let includeDir = path
52+
53+
//Generate empty module map if include dir is not present
54+
guard includeDir.isDirectory else {
55+
print("warning: No include directory, generating empty module map")
56+
try POSIX.mkdir(includeDir)
57+
try fopen(moduleMapPath, mode: .Write) { fp in
58+
try fputs("\n", fp)
59+
}
60+
return
61+
}
62+
63+
let walked = walk(includeDir, recursively: false).map{$0}
64+
65+
let files = walked.filter{$0.isFile && $0.hasSuffix(".h")}
66+
let dirs = walked.filter{$0.isDirectory}
67+
68+
if dirs.isEmpty {
69+
guard !files.isEmpty else { throw ModuleMapError.UnsupportedIncludeLayoutForModule(name) }
70+
try createModuleMap(.FlatHeaderLayout)
71+
return
72+
}
73+
74+
guard let moduleHeaderDir = dirs.first where moduleHeaderDir.basename == name && files.isEmpty else {
75+
throw ModuleMapError.UnsupportedIncludeLayoutForModule(name)
76+
}
77+
78+
let umbrellaHeader = Path.join(moduleHeaderDir, "\(name).h")
79+
if umbrellaHeader.isFile {
80+
try createModuleMap(.HeaderFile)
81+
} else {
82+
try createModuleMap(.ModuleNameDir)
83+
}
84+
}
85+
86+
private enum UmbrellaType {
87+
case FlatHeaderLayout
88+
case ModuleNameDir
89+
case HeaderFile
90+
}
91+
92+
private func createModuleMap(type: UmbrellaType) throws {
93+
let moduleMap = try fopen(moduleMapPath, mode: .Write)
94+
defer { fclose(moduleMap) }
95+
96+
try fputs("module \(name) {\n", moduleMap)
97+
try fputs(" umbrella ", moduleMap)
98+
99+
switch type {
100+
case .FlatHeaderLayout:
101+
try fputs("\".\"\n", moduleMap)
102+
case .ModuleNameDir:
103+
try fputs("\"\(name)\"\n", moduleMap)
104+
case .HeaderFile:
105+
try fputs("header \"\(name)/\(name).h\"\n", moduleMap)
106+
107+
}
108+
109+
try fputs(" link \"\(name)\"\n", moduleMap)
110+
try fputs(" export *\n", moduleMap)
111+
try fputs("}\n", moduleMap)
112+
}
113+
}
114+
35115
extension Product {
36116
var Info: (_: Void, plist: String) {
37117
let bundleExecutable = "Package"

Sources/PackageType/Module.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,6 @@ public class ClangModule: CModule {
9393

9494
public init(name: String, sources: Sources) {
9595
self.sources = sources
96-
//TODO: generate module map using swiftpm if layout can support
9796
super.init(name: name, path: sources.root + "/include")
9897
}
9998
}

Tests/Functional/TestClangModules.swift

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ class TestClangModulesTestCase: XCTestCase {
7373
XCTAssertEqual(output, "hello 5")
7474
}
7575
}
76-
76+
7777
func testCUsingCDep2() {
7878
//The C dependency "Foo" has different layout
7979
fixture(name: "DependencyResolution/External/CUsingCDep2") { prefix in
@@ -82,6 +82,17 @@ class TestClangModulesTestCase: XCTestCase {
8282
XCTAssertDirectoryExists(prefix, "Bar/Packages/Foo-1.2.3")
8383
}
8484
}
85+
86+
func testModuleMapGenerationCases() {
87+
fixture(name: "ClangModules/ModuleMapGenerationCases") { prefix in
88+
XCTAssertBuilds(prefix)
89+
XCTAssertFileExists(prefix, ".build", "debug", "libFoo.so")
90+
XCTAssertFileExists(prefix, ".build", "debug", "libBar.so")
91+
XCTAssertFileExists(prefix, ".build", "debug", "libJaz.so")
92+
XCTAssertFileExists(prefix, ".build", "debug", "libNoIncludeDir.so")
93+
XCTAssertFileExists(prefix, ".build", "debug", "Baz")
94+
}
95+
}
8596
}
8697

8798

@@ -95,6 +106,7 @@ extension TestClangModulesTestCase {
95106
("testiquoteDep", testiquoteDep),
96107
("testCUsingCDep", testCUsingCDep),
97108
("testCExecutable", testCExecutable),
109+
("testModuleMapGenerationCases", testModuleMapGenerationCases),
98110
]
99111
}
100112
}

0 commit comments

Comments
 (0)