@@ -40,8 +40,33 @@ extension ClangModule: ModuleMapProtocol {
40
40
}
41
41
}
42
42
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
+
45
70
/// A link-declaration specifies a library or framework
46
71
/// against which a program should be linked.
47
72
/// More info: http://clang.llvm.org/docs/Modules.html#link-declaration
@@ -76,65 +101,62 @@ extension ClangModule {
76
101
}
77
102
78
103
/// Create the synthesized module map, if necessary.
104
+ /// Note: modulemap is not generated for test modules.
79
105
//
80
106
// FIXME: We recompute the generated modulemap's path when building swift
81
107
// modules in `XccFlags(prefix: String)` there shouldn't be need to redo
82
108
// 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 {
84
110
// Don't generate modulemap for a Test module.
85
- guard !isTest else {
111
+ guard !module . isTest else {
86
112
return
87
113
}
88
114
89
115
///Return if module map is already present
90
- guard !isFile( moduleMapPath) else {
116
+ guard !fileSystem . isFile ( module . moduleMapPath) else {
91
117
return
92
118
}
93
-
119
+
120
+ let includeDir = module. includeDir
94
121
// 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 ( )
97
125
return
98
126
}
99
127
100
- let walked = try localFileSystem . getDirectoryContents ( includeDir) . map { includeDir. appending ( component: $0) }
128
+ let walked = try fileSystem . getDirectoryContents ( includeDir) . map { includeDir. appending ( component: $0) }
101
129
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) }
115
136
try createModuleMap ( inDir: wd, type: . header( umbrellaHeaderFlat) , modulemapStyle: modulemapStyle)
116
137
return
117
138
}
118
139
diagnoseInvalidUmbrellaHeader ( includeDir)
119
140
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) }
123
144
try createModuleMap ( inDir: wd, type: . header( umbrellaHeader) , modulemapStyle: modulemapStyle)
124
145
return
125
146
}
126
- diagnoseInvalidUmbrellaHeader ( includeDir. appending ( component: c99name) )
147
+ diagnoseInvalidUmbrellaHeader ( includeDir. appending ( component: module . c99name) )
127
148
128
149
try createModuleMap ( inDir: wd, type: . directory( includeDir) , modulemapStyle: modulemapStyle)
129
150
}
130
151
131
152
/// Warn user if in case module name and c99name are different and there is a
132
153
/// `name.h` umbrella header.
133
154
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 ( )
138
160
}
139
161
}
140
162
@@ -143,27 +165,27 @@ extension ClangModule {
143
165
case directory( AbsolutePath )
144
166
}
145
167
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
+
153
171
if let qualifier = modulemapStyle. moduleDeclQualifier {
154
- output += qualifier + " "
172
+ stream <<< qualifier <<< " "
155
173
}
156
- output += " module \( c99name) { \n "
174
+ stream <<< " module \( module . c99name) { \n "
157
175
switch type {
158
176
case . header( let header) :
159
- output += " umbrella header \" \( header. asString) \" \n "
177
+ stream <<< " umbrella header \" \( header. asString) \" \n "
160
178
case . directory( let path) :
161
- output += " umbrella \" \( path. asString) \" \n "
179
+ stream <<< " umbrella \" \( path. asString) \" \n "
162
180
}
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 )
166
187
167
- try fputs ( output, moduleMap)
188
+ let file = wd. appending ( component: moduleMapFilename)
189
+ try fileSystem. writeFileContents ( file, bytes: stream. bytes)
168
190
}
169
191
}
0 commit comments