Skip to content

Commit df90a2f

Browse files
committed
Auto generate module map file for C libs
1 parent e702428 commit df90a2f

File tree

21 files changed

+143
-50
lines changed

21 files changed

+143
-50
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: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,8 @@ public func describe(prefix: String, _ conf: Configuration, _ modules: [Module],
4343
targets.append(compile, for: module)
4444

4545
case let module as ClangModule:
46-
//FIXME: Generate modulemaps if possible
47-
//Since we're not generating modulemaps currently we'll just emit empty module map file
48-
//if it not present
49-
if module.type == .Library && !module.moduleMapPath.isFile {
50-
try POSIX.mkdir(module.moduleMapPath.parentDirectory)
51-
try fopen(module.moduleMapPath, mode: .Write) { fp in
52-
try fputs("\n", fp)
53-
}
46+
if module.type == .Library {
47+
try module.generateModuleMap()
5448
}
5549

5650
let (compile, mkdir) = Command.compile(clangModule: module, externalModules: externalModules, configuration: conf, prefix: prefix, CC: CC)

Sources/Build/misc.swift

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

1111
import func POSIX.getenv
1212
import func POSIX.popen
13+
import func POSIX.mkdir
14+
import func POSIX.fopen
15+
import func libc.fclose
1316
import PackageType
1417
import Utility
1518

@@ -34,6 +37,83 @@ extension CModule {
3437
}
3538
}
3639

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

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)