Skip to content

Commit 4cd4a34

Browse files
committed
Merge branch 'modulemap-refactor' of https://github.com/aciidb0mb3r/swift-package-manager
2 parents 9ecd75c + 9359960 commit 4cd4a34

File tree

6 files changed

+296
-48
lines changed

6 files changed

+296
-48
lines changed

Sources/Build/Command.compile(ClangModule).swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
import Basic
1212
import PackageModel
13+
import struct PackageLoading.ModuleMapGenerator
1314
import Utility
1415
import POSIX
1516

@@ -116,7 +117,8 @@ extension Command {
116117
let buildMeta = ClangModuleBuildMetadata(module: module, prefix: prefix, otherArgs: otherArgs)
117118

118119
if module.type == .library {
119-
try module.generateModuleMap(inDir: buildMeta.buildDirectory)
120+
var moduleMapGenerator = ModuleMapGenerator(for: module)
121+
try moduleMapGenerator.generateModuleMap(inDir: buildMeta.buildDirectory)
120122
}
121123

122124
///------------------------------ Compile -----------------------------------------

Sources/PackageLoading/ModuleMapGeneration.swift renamed to Sources/PackageLoading/ModuleMapGenerator.swift

Lines changed: 67 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,33 @@ extension ClangModule: ModuleMapProtocol {
4040
}
4141
}
4242

43-
extension ClangModule {
44-
43+
/// A modulemap generator for clang modules.
44+
///
45+
/// Modulemap is generated under the following rules provided it is not already present in include directory:
46+
///
47+
/// * "include/foo/foo.h" exists and `foo` is the only directory under include directory.
48+
/// Generates: `umbrella header "/path/to/include/foo/foo.h"`
49+
/// * "include/foo.h" exists and include contains no other directory.
50+
/// Generates: `umbrella header "/path/to/include/foo.h"`
51+
/// * Otherwise in all other cases.
52+
/// Generates: `umbrella "path/to/include"`
53+
public struct ModuleMapGenerator {
54+
55+
/// The clang module to operate on.
56+
private let module: ClangModule
57+
58+
/// The file system to be used.
59+
private var fileSystem: FileSystem
60+
61+
/// Stream on which warnings will be emitted.
62+
private let warningStream: OutputByteStream
63+
64+
public init(for module: ClangModule, fileSystem: FileSystem = localFileSystem, warningStream: OutputByteStream = stdoutStream) {
65+
self.module = module
66+
self.fileSystem = fileSystem
67+
self.warningStream = warningStream
68+
}
69+
4570
/// A link-declaration specifies a library or framework
4671
/// against which a program should be linked.
4772
/// More info: http://clang.llvm.org/docs/Modules.html#link-declaration
@@ -76,65 +101,62 @@ extension ClangModule {
76101
}
77102

78103
/// Create the synthesized module map, if necessary.
104+
/// Note: modulemap is not generated for test modules.
79105
//
80106
// FIXME: We recompute the generated modulemap's path when building swift
81107
// modules in `XccFlags(prefix: String)` there shouldn't be need to redo
82108
// this there but is difficult in current architecture.
83-
public func generateModuleMap(inDir wd: AbsolutePath, modulemapStyle: ModuleMapStyle = .library) throws {
109+
public mutating func generateModuleMap(inDir wd: AbsolutePath, modulemapStyle: ModuleMapStyle = .library) throws {
84110
// Don't generate modulemap for a Test module.
85-
guard !isTest else {
111+
guard !module.isTest else {
86112
return
87113
}
88114

89115
///Return if module map is already present
90-
guard !isFile(moduleMapPath) else {
116+
guard !fileSystem.isFile(module.moduleMapPath) else {
91117
return
92118
}
93-
119+
120+
let includeDir = module.includeDir
94121
// Warn and return if no include directory.
95-
guard isDirectory(includeDir) else {
96-
print("warning: No include directory found for module '\(name)'. A library can not be imported without any public headers.")
122+
guard fileSystem.isDirectory(includeDir) else {
123+
warningStream <<< "warning: No include directory found for module '\(module.name)'. A library can not be imported without any public headers."
124+
warningStream.flush()
97125
return
98126
}
99127

100-
let walked = try localFileSystem.getDirectoryContents(includeDir).map{ includeDir.appending(component: $0) }
128+
let walked = try fileSystem.getDirectoryContents(includeDir).map{ includeDir.appending(component: $0) }
101129

102-
let files = walked.filter{ isFile($0) && $0.suffix == ".h" }
103-
let dirs = walked.filter{ isDirectory($0) }
104-
105-
// We generate modulemap for a C module `foo` if:
106-
// * `umbrella header "path/to/include/foo/foo.h"` exists and `foo` is the only
107-
// directory under include directory
108-
// * `umbrella header "path/to/include/foo.h"` exists and include contains no other
109-
// directory
110-
// * `umbrella "path/to/include"` in all other cases
111-
112-
let umbrellaHeaderFlat = includeDir.appending(component: c99name + ".h")
113-
if isFile(umbrellaHeaderFlat) {
114-
guard dirs.isEmpty else { throw ModuleMapError.unsupportedIncludeLayoutForModule(name) }
130+
let files = walked.filter{ fileSystem.isFile($0) && $0.suffix == ".h" }
131+
let dirs = walked.filter{ fileSystem.isDirectory($0) }
132+
133+
let umbrellaHeaderFlat = includeDir.appending(component: module.c99name + ".h")
134+
if fileSystem.isFile(umbrellaHeaderFlat) {
135+
guard dirs.isEmpty else { throw ModuleMapError.unsupportedIncludeLayoutForModule(module.name) }
115136
try createModuleMap(inDir: wd, type: .header(umbrellaHeaderFlat), modulemapStyle: modulemapStyle)
116137
return
117138
}
118139
diagnoseInvalidUmbrellaHeader(includeDir)
119140

120-
let umbrellaHeader = includeDir.appending(components: c99name, c99name + ".h")
121-
if isFile(umbrellaHeader) {
122-
guard dirs.count == 1 && files.isEmpty else { throw ModuleMapError.unsupportedIncludeLayoutForModule(name) }
141+
let umbrellaHeader = includeDir.appending(components: module.c99name, module.c99name + ".h")
142+
if fileSystem.isFile(umbrellaHeader) {
143+
guard dirs.count == 1 && files.isEmpty else { throw ModuleMapError.unsupportedIncludeLayoutForModule(module.name) }
123144
try createModuleMap(inDir: wd, type: .header(umbrellaHeader), modulemapStyle: modulemapStyle)
124145
return
125146
}
126-
diagnoseInvalidUmbrellaHeader(includeDir.appending(component: c99name))
147+
diagnoseInvalidUmbrellaHeader(includeDir.appending(component: module.c99name))
127148

128149
try createModuleMap(inDir: wd, type: .directory(includeDir), modulemapStyle: modulemapStyle)
129150
}
130151

131152
/// Warn user if in case module name and c99name are different and there is a
132153
/// `name.h` umbrella header.
133154
private func diagnoseInvalidUmbrellaHeader(_ path: AbsolutePath) {
134-
let umbrellaHeader = path.appending(component: c99name + ".h")
135-
let invalidUmbrellaHeader = path.appending(component: name + ".h")
136-
if c99name != name && isFile(invalidUmbrellaHeader) {
137-
print("warning: \(invalidUmbrellaHeader) should be renamed to \(umbrellaHeader) to be used as an umbrella header")
155+
let umbrellaHeader = path.appending(component: module.c99name + ".h")
156+
let invalidUmbrellaHeader = path.appending(component: module.name + ".h")
157+
if module.c99name != module.name && fileSystem.isFile(invalidUmbrellaHeader) {
158+
warningStream <<< "warning: \(invalidUmbrellaHeader.asString) should be renamed to \(umbrellaHeader.asString) to be used as an umbrella header"
159+
warningStream.flush()
138160
}
139161
}
140162

@@ -143,27 +165,27 @@ extension ClangModule {
143165
case directory(AbsolutePath)
144166
}
145167

146-
private func createModuleMap(inDir wd: AbsolutePath, type: UmbrellaType, modulemapStyle: ModuleMapStyle) throws {
147-
try makeDirectories(wd)
148-
let moduleMapFile = wd.appending(component: moduleMapFilename)
149-
let moduleMap = try fopen(moduleMapFile, mode: .write)
150-
defer { moduleMap.closeFile() }
151-
152-
var output = ""
168+
private mutating func createModuleMap(inDir wd: AbsolutePath, type: UmbrellaType, modulemapStyle: ModuleMapStyle) throws {
169+
let stream = BufferedOutputByteStream()
170+
153171
if let qualifier = modulemapStyle.moduleDeclQualifier {
154-
output += qualifier + " "
172+
stream <<< qualifier <<< " "
155173
}
156-
output += "module \(c99name) {\n"
174+
stream <<< "module \(module.c99name) {\n"
157175
switch type {
158176
case .header(let header):
159-
output += " umbrella header \"\(header.asString)\"\n"
177+
stream <<< " umbrella header \"\(header.asString)\"\n"
160178
case .directory(let path):
161-
output += " umbrella \"\(path.asString)\"\n"
179+
stream <<< " umbrella \"\(path.asString)\"\n"
162180
}
163-
output += " \(modulemapStyle.linkDeclFlag) \"\(c99name)\"\n"
164-
output += " export *\n"
165-
output += "}\n"
181+
stream <<< " \(modulemapStyle.linkDeclFlag) \"\(module.c99name)\"\n"
182+
stream <<< " export *\n"
183+
stream <<< "}\n"
184+
185+
// FIXME: This doesn't belong here.
186+
try fileSystem.createDirectory(wd, recursive: true)
166187

167-
try fputs(output, moduleMap)
188+
let file = wd.appending(component: moduleMapFilename)
189+
try fileSystem.writeFileContents(file, bytes: stream.bytes)
168190
}
169191
}

Sources/Xcodeproj/Module+PBXProj.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,8 @@ extension Module {
277277
} else {
278278
// Generate and drop the modulemap inside Xcodeproj folder.
279279
let path = xcodeProjectPath.appending(components: "GeneratedModuleMap", clangModule.c99name)
280-
try clangModule.generateModuleMap(inDir: path, modulemapStyle: .framework)
280+
var moduleMapGenerator = ModuleMapGenerator(for: clangModule)
281+
try moduleMapGenerator.generateModuleMap(inDir: path, modulemapStyle: .framework)
281282
moduleMapPath = path.appending(component: moduleMapFilename)
282283
}
283284

0 commit comments

Comments
 (0)