@@ -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,48 +244,59 @@ 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 }
276296 } else {
277- modules = try maybeModules. map { path in
278- try modulify ( path, name: path. basename, isTest: false )
297+ // We have at least one directory that looks like a module, so we try to create a module for each one.
298+ modules = try potentialModulePaths. map { path in
299+ try createModule ( path, name: path. basename, isTest: false )
279300 }
280301 }
281302
@@ -320,19 +341,25 @@ public struct PackageBuilder {
320341 return modules
321342 }
322343
323- private func modulify( _ path: AbsolutePath , name: String , isTest: Bool ) throws -> Module {
344+ /// 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.
345+ private func createModule( _ path: AbsolutePath , name: String , isTest: Bool ) throws -> Module {
346+ // Find all the files under the module path.
324347 let walked = try walk ( path, fileSystem: fileSystem, recursing: shouldConsiderDirectory) . map { $0 }
325348
349+ // Select any source files for the C-based languages and for Swift.
326350 let cSources = walked. filter { isValidSource ( $0, validExtensions: SupportedLanguageExtension . cFamilyExtensions) }
327351 let swiftSources = walked. filter { isValidSource ( $0, validExtensions: SupportedLanguageExtension . swiftExtensions) }
328352
329- if !cSources. isEmpty {
353+ // Create and return the right kind of module depending on what kind of sources we found.
354+ if cSources. isEmpty {
355+ // No C sources, so we expect to have Swift sources, and we create a Swift module.
356+ guard !swiftSources. isEmpty else { throw Module . Error. noSources ( path. asString) }
357+ return try SwiftModule ( name: name, isTest: isTest, sources: Sources ( paths: swiftSources, root: path) )
358+ } else {
359+ // No Swift sources, so we expect to have C sources, and we create a C module.
330360 guard swiftSources. isEmpty else { throw Module . Error. mixedSources ( path. asString) }
331361 return try ClangModule ( name: name, isTest: isTest, sources: Sources ( paths: cSources, root: path) )
332362 }
333-
334- guard !swiftSources. isEmpty else { throw Module . Error. noSources ( path. asString) }
335- return try SwiftModule ( name: name, isTest: isTest, sources: Sources ( paths: swiftSources, root: path) )
336363 }
337364
338365 /// Collects the products defined by a package.
@@ -416,7 +443,7 @@ public struct PackageBuilder {
416443
417444 // Create the test modules
418445 let testModules = try directoryContents ( testsPath) . filter ( shouldConsiderDirectory) . flatMap { dir in
419- return [ try modulify ( dir, name: dir. basename, isTest: true ) ]
446+ return [ try createModule ( dir, name: dir. basename, isTest: true ) ]
420447 }
421448
422449 // Populate the test module dependencies.
0 commit comments