@@ -14,17 +14,25 @@ import Utility
1414
1515import class PackageDescription. Target
1616
17+ /// An error in the structure or layout of a package.
1718public enum ModuleError : Swift . Error {
19+
20+ /// One or more referenced modules could not be found.
1821 case modulesNotFound( [ String ] )
22+
23+ /// Package layout is invalid.
1924 case invalidLayout( InvalidLayoutType )
25+
26+ /// Describes a way in which a package layout is invalid.
27+ public enum InvalidLayoutType {
28+ case multipleSourceRoots( [ String ] )
29+ case invalidLayout( [ String ] )
30+ }
31+
32+ /// A module was marked as being dependent on an executable.
2033 case executableAsDependency( module: String , dependency: String )
2134}
2235
23- public enum InvalidLayoutType {
24- case multipleSourceRoots( [ String ] )
25- case invalidLayout( [ String ] )
26- }
27-
2836extension ModuleError : FixableError {
2937 public var error : String {
3038 switch self {
@@ -49,7 +57,7 @@ extension ModuleError: FixableError {
4957 }
5058}
5159
52- extension InvalidLayoutType : FixableError {
60+ extension ModuleError . InvalidLayoutType : FixableError {
5361 public var error : String {
5462 switch self {
5563 case . multipleSourceRoots( let paths) :
@@ -208,10 +216,12 @@ public struct PackageBuilder {
208216 }
209217
210218 /// Returns path to all the items in a directory.
219+ /// FIXME: This is generic functionality, and should move to FileSystem.
211220 func directoryContents( _ path: AbsolutePath ) throws -> [ AbsolutePath ] {
212221 return try fileSystem. getDirectoryContents ( path) . map { path. appending ( component: $0) }
213222 }
214223
224+ /// Returns the path of the source directory, throwing an error in case of an invalid layout (such as the presence of both `Sources` and `src` directories).
215225 func sourceRoot( ) throws -> AbsolutePath {
216226 let viableRoots = try fileSystem. getDirectoryContents ( packagePath) . filter { basename in
217227 let entry = packagePath. appending ( component: basename)
@@ -234,54 +244,67 @@ public struct PackageBuilder {
234244 }
235245 }
236246
237- /// Collects the modules which are defined by a package.
247+ /// Private function that creates and returns a list of non-test Modules defined by a package.
238248 private func constructModules( ) throws -> [ Module ] {
249+
250+ // Check for a modulemap file, which indicates a system module.
239251 let moduleMapPath = packagePath. appending ( component: " module.modulemap " )
240252 if fileSystem. isFile ( moduleMapPath) {
253+ // Package contains a modulemap at the top level, so we assuming it's a system module.
241254 let sources = Sources ( paths: [ moduleMapPath] , root: packagePath)
242255 return [ try CModule ( name: manifest. name, sources: sources, path: packagePath, pkgConfig: pkgConfigPath, providers: manifest. package . providers) ]
243256 }
244257
258+ // If everything is excluded, just return an empty array.
245259 if manifest. package . exclude. contains ( " . " ) {
246260 return [ ]
247261 }
248-
249- let srcroot = try sourceRoot ( )
250-
251- if srcroot != packagePath {
262+
263+ // Locate the source directory inside the package.
264+ let srcDir = try sourceRoot ( )
265+
266+ // If there is a source directory, we expect all source files to be located in it.
267+ if srcDir != packagePath {
252268 let invalidRootFiles = try directoryContents ( packagePath) . filter ( isValidSource)
253269 guard invalidRootFiles. isEmpty else {
254270 throw ModuleError . invalidLayout ( . invalidLayout( invalidRootFiles. map { $0. asString } ) )
255271 }
256272 }
257-
258- let maybeModules = try directoryContents ( srcroot) . filter ( shouldConsiderDirectory)
259-
260- if maybeModules. count == 1 && maybeModules [ 0 ] != srcroot {
261- let invalidModuleFiles = try directoryContents ( srcroot) . filter ( isValidSource)
273+
274+ // Locate any directories that might be the roots of modules inside the source directory.
275+ let potentialModulePaths = try directoryContents ( srcDir) . filter ( shouldConsiderDirectory)
276+
277+ // If there's a single module inside the source directory, make sure there are no loose source files in the sources directory.
278+ if potentialModulePaths. count == 1 && potentialModulePaths [ 0 ] != srcDir {
279+ let invalidModuleFiles = try directoryContents ( srcDir) . filter ( isValidSource)
262280 guard invalidModuleFiles. isEmpty else {
263281 throw ModuleError . invalidLayout ( . invalidLayout( invalidModuleFiles. map { $0. asString } ) )
264282 }
265283 }
266-
284+
285+ // With preliminary checks done, we can start creating modules.
267286 let modules : [ Module ]
268- if maybeModules . isEmpty {
269- // If there are no sources subdirectories, we have at most a one target package .
287+ if potentialModulePaths . isEmpty {
288+ // There are no directories that look like modules, so try to create a module for the source directory itself (with the name coming from the name in the manifest) .
270289 do {
271- modules = [ try modulify ( srcroot, name: manifest. name, isTest: false ) ]
272- } catch Module . Error . noSources {
290+ modules = [ try createModule ( srcDir, name: manifest. name, isTest: false ) ]
291+ }
292+ catch Module . Error . noSources {
273293 // Completely empty packages are allowed as a special case.
274294 modules = [ ]
275295 }
276- } else {
277- modules = try maybeModules. map { path in
296+ }
297+ else {
298+ // We have at least one directory that looks like a module, so we try to create a module for each one.
299+ modules = try potentialModulePaths. map { path in
300+ // FIXME: Can this really happen; we shouldn't be able to get here if path == srcDir.
278301 let name : String
279- if path == srcroot {
302+ if path == srcDir {
280303 name = manifest. name
281304 } else {
282305 name = path. basename
283306 }
284- return try modulify ( path, name: name, isTest: false )
307+ return try createModule ( path, name: name, isTest: false )
285308 }
286309 }
287310
@@ -326,19 +349,26 @@ public struct PackageBuilder {
326349 return modules
327350 }
328351
329- private func modulify( _ path: AbsolutePath , name: String , isTest: Bool ) throws -> Module {
352+ /// Private function that constructs a single Module object for the moduel at `path`, having the name `name`. If `isTest` is true, the module is constructed as a test module; if false, it is a regular module.
353+ private func createModule( _ path: AbsolutePath , name: String , isTest: Bool ) throws -> Module {
354+ // Find all the files under the module path.
330355 let walked = try walk ( path, fileSystem: fileSystem, recursing: shouldConsiderDirectory) . map { $0 }
331356
357+ // Select any source files for the C-based languages and for Swift.
332358 let cSources = walked. filter { isValidSource ( $0, validExtensions: SupportedLanguageExtension . cFamilyExtensions) }
333359 let swiftSources = walked. filter { isValidSource ( $0, validExtensions: SupportedLanguageExtension . swiftExtensions) }
334360
335- if !cSources. isEmpty {
361+ // Create and return the right kind of module depending on what kind of sources we found.
362+ if cSources. isEmpty {
363+ // No C sources, so we expect to have Swift sources, and we create a Swift module.
364+ guard !swiftSources. isEmpty else { throw Module . Error. noSources ( path. asString) }
365+ return try SwiftModule ( name: name, isTest: isTest, sources: Sources ( paths: swiftSources, root: path) )
366+ }
367+ else {
368+ // No Swift sources, so we expect to have C sources, and we create a C module.
336369 guard swiftSources. isEmpty else { throw Module . Error. mixedSources ( path. asString) }
337370 return try ClangModule ( name: name, isTest: isTest, sources: Sources ( paths: cSources, root: path) )
338371 }
339-
340- guard !swiftSources. isEmpty else { throw Module . Error. noSources ( path. asString) }
341- return try SwiftModule ( name: name, isTest: isTest, sources: Sources ( paths: swiftSources, root: path) )
342372 }
343373
344374 /// Collects the products defined by a package.
@@ -422,7 +452,7 @@ public struct PackageBuilder {
422452
423453 // Create the test modules
424454 let testModules = try directoryContents ( testsPath) . filter ( shouldConsiderDirectory) . flatMap { dir in
425- return [ try modulify ( dir, name: dir. basename, isTest: true ) ]
455+ return [ try createModule ( dir, name: dir. basename, isTest: true ) ]
426456 }
427457
428458 // Populate the test module dependencies.
0 commit comments