@@ -440,6 +440,138 @@ class ConventionTests: XCTestCase {
440440 }
441441 }
442442
443+ func testValidSources( ) throws {
444+ var fs = InMemoryFileSystem ( )
445+ try fs. createEmptyFiles ( " /main.swift " ,
446+ " /noExtension " ,
447+ " /Package.swift " ,
448+ " /.git/anchor " ,
449+ " /.xcodeproj/anchor " ,
450+ " /.playground/anchor " ,
451+ " /Package.swift " ,
452+ " /Packages/MyPackage/main.c " )
453+ let name = " pkg "
454+ PackageBuilderTester ( name, in: fs) { result in
455+ result. checkModule ( name) { moduleResult in
456+ moduleResult. check ( type: . executable, isTest: false )
457+ moduleResult. checkSources ( root: " / " , paths: " main.swift " )
458+ }
459+ }
460+ }
461+
462+ func testCustomTargetDependencies( ) throws {
463+ var fs = InMemoryFileSystem ( )
464+ try fs. createEmptyFiles ( " /Sources/Foo/Foo.swift " ,
465+ " /Sources/Bar/Bar.swift " ,
466+ " /Sources/Baz/Baz.swift " )
467+
468+ // Direct.
469+ var package = PackageDescription . Package ( name: " pkg " , targets: [ Target ( name: " Foo " , dependencies: [ " Bar " ] ) ] )
470+ PackageBuilderTester ( package , in: fs) { result in
471+ result. checkModule ( " Foo " ) { moduleResult in
472+ moduleResult. check ( c99name: " Foo " , type: . library, isTest: false )
473+ moduleResult. checkSources ( root: " /Sources/Foo " , paths: " Foo.swift " )
474+ moduleResult. check ( dependencies: [ " Bar " ] )
475+ moduleResult. check ( recursiveDependencies: [ " Bar " ] )
476+ }
477+
478+ for module in [ " Bar " , " Baz " ] {
479+ result. checkModule ( module) { moduleResult in
480+ moduleResult. check ( c99name: module, type: . library, isTest: false )
481+ moduleResult. checkSources ( root: " /Sources/ \( module) " , paths: " \( module) .swift " )
482+ }
483+ }
484+ }
485+
486+ // Transitive.
487+ package = PackageDescription . Package ( name: " pkg " ,
488+ targets: [ Target ( name: " Foo " , dependencies: [ " Bar " ] ) ,
489+ Target ( name: " Bar " , dependencies: [ " Baz " ] ) ] )
490+ PackageBuilderTester ( package , in: fs) { result in
491+ result. checkModule ( " Foo " ) { moduleResult in
492+ moduleResult. check ( c99name: " Foo " , type: . library, isTest: false )
493+ moduleResult. checkSources ( root: " /Sources/Foo " , paths: " Foo.swift " )
494+ moduleResult. check ( dependencies: [ " Bar " ] )
495+ moduleResult. check ( recursiveDependencies: [ " Baz " , " Bar " ] )
496+ }
497+
498+ result. checkModule ( " Bar " ) { moduleResult in
499+ moduleResult. check ( c99name: " Bar " , type: . library, isTest: false )
500+ moduleResult. checkSources ( root: " /Sources/Bar " , paths: " Bar.swift " )
501+ moduleResult. check ( dependencies: [ " Baz " ] )
502+ moduleResult. check ( recursiveDependencies: [ " Baz " ] )
503+ }
504+
505+ result. checkModule ( " Baz " ) { moduleResult in
506+ moduleResult. check ( c99name: " Baz " , type: . library, isTest: false )
507+ moduleResult. checkSources ( root: " /Sources/Baz " , paths: " Baz.swift " )
508+ }
509+ }
510+ }
511+
512+ func testManifestTargetDeclErrors( ) throws {
513+ // Reference a target which doesn't exist.
514+ var fs = InMemoryFileSystem ( )
515+ try fs. createEmptyFiles ( " /Foo.swift " )
516+ var package = PackageDescription . Package ( name: " pkg " , targets: [ Target ( name: " Random " ) ] )
517+ PackageBuilderTester ( package , in: fs) { result in
518+ result. checkDiagnostic ( " these referenced modules could not be found: Random fix: reference only valid modules " )
519+ }
520+
521+ // Reference an invalid dependency.
522+ package = PackageDescription . Package ( name: " pkg " , targets: [ Target ( name: " pkg " , dependencies: [ " Foo " ] ) ] )
523+ PackageBuilderTester ( package , in: fs) { result in
524+ result. checkDiagnostic ( " these referenced modules could not be found: Foo fix: reference only valid modules " )
525+ }
526+
527+ // Executable as dependency.
528+ // FIXME: maybe should support this and condiser it as build order dependency.
529+ fs = InMemoryFileSystem ( )
530+ try fs. createEmptyFiles ( " /Sources/exec/main.swift " ,
531+ " /Sources/lib/lib.swift " )
532+ package = PackageDescription . Package ( name: " pkg " , targets: [ Target ( name: " lib " , dependencies: [ " exec " ] ) ] )
533+ PackageBuilderTester ( package , in: fs) { result in
534+ result. checkDiagnostic ( " the target lib cannot have the executable exec as a dependency fix: move the shared logic inside a library, which can be referenced from both the target and the executable " )
535+ }
536+ }
537+
538+ func testProducts( ) throws {
539+ var fs = InMemoryFileSystem ( )
540+ try fs. createEmptyFiles ( " /Sources/Foo/Foo.swift " ,
541+ " /Sources/Bar/Bar.swift " )
542+ let products = [ Product ( name: " libpm " , type: . Library( . Dynamic) , modules: [ " Foo " , " Bar " ] ) ]
543+
544+ PackageBuilderTester ( " pkg " , in: fs, products: products) { result in
545+ result. checkModule ( " Foo " ) { moduleResult in
546+ moduleResult. check ( c99name: " Foo " , type: . library, isTest: false )
547+ moduleResult. checkSources ( root: " /Sources/Foo " , paths: " Foo.swift " )
548+ }
549+
550+ result. checkModule ( " Bar " ) { moduleResult in
551+ moduleResult. check ( c99name: " Bar " , type: . library, isTest: false )
552+ moduleResult. checkSources ( root: " /Sources/Bar " , paths: " Bar.swift " )
553+ }
554+
555+ result. checkProduct ( " libpm " ) { productResult in
556+ productResult. check ( type: . Library( . Dynamic) , modules: [ " Foo " , " Bar " ] )
557+ }
558+ }
559+ }
560+
561+ func testBadProducts( ) throws {
562+ var fs = InMemoryFileSystem ( )
563+ try fs. createEmptyFiles ( " /Foo.swift " )
564+ var products = [ Product ( name: " libpm " , type: . Library( . Dynamic) , modules: [ " Foo " , " Bar " ] ) ]
565+ PackageBuilderTester ( " Foo " , in: fs, products: products) { result in
566+ result. checkDiagnostic ( " the product named libpm references a module that could not be found: Bar fix: reference only valid modules from the product " )
567+ }
568+
569+ products = [ Product ( name: " libpm " , type: . Library( . Dynamic) , modules: [ ] ) ]
570+ PackageBuilderTester ( " Foo " , in: fs, products: products) { result in
571+ result. checkDiagnostic ( " the product named libpm doesn \' t reference any modules fix: reference one or more modules from the product " )
572+ }
573+ }
574+
443575 // MARK:- Invalid Layouts Tests
444576
445577 func testMultipleRoots( ) throws {
@@ -552,6 +684,36 @@ class ConventionTests: XCTestCase {
552684 }
553685 }
554686
687+ func testNoSourcesInModule( ) throws {
688+ let fs = InMemoryFileSystem ( )
689+ try fs. createDirectory ( AbsolutePath ( " /Sources/Module " ) , recursive: true )
690+
691+ PackageBuilderTester ( " MyPackage " , in: fs) { result in
692+ result. checkDiagnostic ( " the module at /Sources/Module does not contain any source files fix: either remove the module folder, or add a source file to the module " )
693+ }
694+ }
695+
696+ func testExcludes( ) throws {
697+ var fs = InMemoryFileSystem ( )
698+ try fs. createEmptyFiles ( " /Sources/A/main.swift " ,
699+ " /Sources/A/foo.swift " , // File will be excluded.
700+ " /Sources/B/main.swift " // Dir will be excluded.
701+ )
702+
703+ // Excluding everything.
704+ var package = PackageDescription . Package ( name: " pkg " , exclude: [ " . " ] )
705+ PackageBuilderTester ( package , in: fs) { _ in }
706+
707+ // Test excluding a file and a directory.
708+ package = PackageDescription . Package ( name: " pkg " , exclude: [ " Sources/A/foo.swift " , " Sources/B " ] )
709+ PackageBuilderTester ( package , in: fs) { result in
710+ result. checkModule ( " A " ) { moduleResult in
711+ moduleResult. check ( type: . executable, isTest: false )
712+ moduleResult. checkSources ( root: " /Sources/A " , paths: " main.swift " )
713+ }
714+ }
715+ }
716+
555717 static var allTests = [
556718 ( " testCInTests " , testCInTests) ,
557719 ( " testDotFilesAreIgnored " , testDotFilesAreIgnored) ,
@@ -573,6 +735,13 @@ class ConventionTests: XCTestCase {
573735 ( " testInvalidLayout3 " , testInvalidLayout3) ,
574736 ( " testInvalidLayout4 " , testInvalidLayout4) ,
575737 ( " testInvalidLayout5 " , testInvalidLayout5) ,
738+ ( " testNoSourcesInModule " , testNoSourcesInModule) ,
739+ ( " testValidSources " , testValidSources) ,
740+ ( " testExcludes " , testExcludes) ,
741+ ( " testCustomTargetDependencies " , testCustomTargetDependencies) ,
742+ ( " testManifestTargetDeclErrors " , testManifestTargetDeclErrors) ,
743+ ( " testProducts " , testProducts) ,
744+ ( " testBadProducts " , testBadProducts) ,
576745 ]
577746}
578747
@@ -613,11 +782,12 @@ private extension FileSystem {
613782/// - package: PackageDescription instance to use for loading this package.
614783/// - path: Directory where the package is located.
615784/// - in: FileSystem in which the package should be loaded from.
785+ /// - products: List of products in the package.
616786/// - warningStream: OutputByteStream to be passed to package builder.
617787///
618788/// - Throws: ModuleError, ProductError
619- private func loadPackage( _ package : PackageDescription . Package , path: AbsolutePath , in fs: FileSystem , warningStream: OutputByteStream ) throws -> PackageModel . Package {
620- let manifest = Manifest ( path: path. appending ( component: Manifest . filename) , url: " " , package : package , products: [ ] , version: nil )
789+ private func loadPackage( _ package : PackageDescription . Package , path: AbsolutePath , in fs: FileSystem , products : [ PackageDescription . Product ] , warningStream: OutputByteStream ) throws -> PackageModel . Package {
790+ let manifest = Manifest ( path: path. appending ( component: Manifest . filename) , url: " " , package : package , products: products , version: nil )
621791 let builder = PackageBuilder ( manifest: manifest, path: path, fileSystem: fs, warningStream: warningStream)
622792 return try builder. construct ( includingTestModules: true )
623793}
@@ -650,16 +820,16 @@ final class PackageBuilderTester {
650820 var ignoreOtherModules : Bool = false
651821
652822 @discardableResult
653- convenience init ( _ name: String , path: AbsolutePath = . root, in fs: FileSystem , file: StaticString = #file, line: UInt = #line, _ body: @noescape ( PackageBuilderTester ) -> Void ) {
823+ convenience init ( _ name: String , path: AbsolutePath = . root, in fs: FileSystem , products : [ PackageDescription . Product ] = [ ] , file: StaticString = #file, line: UInt = #line, _ body: @noescape ( PackageBuilderTester ) -> Void ) {
654824 let package = Package ( name: name)
655- self . init ( package , path: path, in: fs, file: file, line: line, body)
825+ self . init ( package , path: path, in: fs, products : products , file: file, line: line, body)
656826 }
657827
658828 @discardableResult
659- init ( _ package : PackageDescription . Package , path: AbsolutePath = . root, in fs: FileSystem , file: StaticString = #file, line: UInt = #line, _ body: @noescape ( PackageBuilderTester ) -> Void ) {
829+ init ( _ package : PackageDescription . Package , path: AbsolutePath = . root, in fs: FileSystem , products : [ PackageDescription . Product ] = [ ] , file: StaticString = #file, line: UInt = #line, _ body: @noescape ( PackageBuilderTester ) -> Void ) {
660830 do {
661831 let warningStream = BufferedOutputByteStream ( )
662- let loadedPackage = try loadPackage ( package , path: path, in: fs, warningStream: warningStream)
832+ let loadedPackage = try loadPackage ( package , path: path, in: fs, products : products , warningStream: warningStream)
663833 result = . package ( loadedPackage)
664834 uncheckedModules = Set ( loadedPackage. allModules)
665835 // FIXME: Find a better way. Maybe Package can keep array of warnings.
@@ -703,6 +873,29 @@ final class PackageBuilderTester {
703873 body ? ( ModuleResult ( module) )
704874 }
705875
876+ func checkProduct( _ name: String , file: StaticString = #file, line: UInt = #line, _ body: ( @noescape ( ProductResult ) -> Void ) ? = nil ) {
877+ guard case . package ( let package ) = result else {
878+ return XCTFail ( " Expected package did not load \( self ) " , file: file, line: line)
879+ }
880+ guard let product = package . products. first ( where: { $0. name == name} ) else {
881+ return XCTFail ( " Product: \( name) not found " , file: file, line: line)
882+ }
883+ body ? ( ProductResult ( product) )
884+ }
885+
886+ final class ProductResult {
887+ private let product : PackageModel . Product
888+
889+ init ( _ product: PackageModel . Product ) {
890+ self . product = product
891+ }
892+
893+ func check( type: PackageDescription . ProductType , modules: [ String ] , file: StaticString = #file, line: UInt = #line) {
894+ XCTAssertEqual ( product. type, type, file: file, line: line)
895+ XCTAssertEqual ( product. modules. map { $0. name} , modules, file: file, line: line)
896+ }
897+ }
898+
706899 final class ModuleResult {
707900 private let module : Module
708901
0 commit comments