@@ -128,6 +128,9 @@ public struct BuildParameters {
128128 /// Whether to enable code coverage.
129129 public let enableCodeCoverage : Bool
130130
131+ /// Whether to enable test discovery on platforms without Objective-C runtime.
132+ public let enableTestDiscovery : Bool
133+
131134 /// Whether to enable generation of `.swiftinterface` files alongside
132135 /// `.swiftmodule`s.
133136 public let enableParseableModuleInterfaces : Bool
@@ -156,7 +159,8 @@ public struct BuildParameters {
156159 sanitizers: EnabledSanitizers = EnabledSanitizers ( ) ,
157160 enableCodeCoverage: Bool = false ,
158161 indexStoreMode: IndexStoreMode = . auto,
159- enableParseableModuleInterfaces: Bool = false
162+ enableParseableModuleInterfaces: Bool = false ,
163+ enableTestDiscovery: Bool = false
160164 ) {
161165 self . dataPath = dataPath
162166 self . configuration = configuration
@@ -170,6 +174,7 @@ public struct BuildParameters {
170174 self . enableCodeCoverage = enableCodeCoverage
171175 self . indexStoreMode = indexStoreMode
172176 self . enableParseableModuleInterfaces = enableParseableModuleInterfaces
177+ self . enableTestDiscovery = enableTestDiscovery
173178 }
174179
175180 /// Returns the compiler arguments for the index store, if enabled.
@@ -445,6 +450,7 @@ public final class SwiftTargetBuildDescription {
445450
446451 /// The objects in this target.
447452 var objects : [ AbsolutePath ] {
453+ // Otherwise, use sources from the target.
448454 return target. sources. relativePaths. map ( { tempsPath. appending ( RelativePath ( " \( $0. pathString) .o " ) ) } )
449455 }
450456
@@ -469,13 +475,22 @@ public final class SwiftTargetBuildDescription {
469475 /// If this target is a test target.
470476 public let isTestTarget : Bool
471477
478+ /// True if this is the test discovery target.
479+ public let testDiscoveryTarget : Bool
480+
472481 /// Create a new target description with target and build parameters.
473- init ( target: ResolvedTarget , buildParameters: BuildParameters , isTestTarget: Bool ? = nil ) {
482+ init (
483+ target: ResolvedTarget ,
484+ buildParameters: BuildParameters ,
485+ isTestTarget: Bool ? = nil ,
486+ testDiscoveryTarget: Bool = false
487+ ) {
474488 assert ( target. underlyingTarget is SwiftTarget , " underlying target type mismatch \( target) " )
475489 self . target = target
476490 self . buildParameters = buildParameters
477491 // Unless mentioned explicitly, use the target type to determine if this is a test target.
478492 self . isTestTarget = isTestTarget ?? ( target. type == . test)
493+ self . testDiscoveryTarget = testDiscoveryTarget
479494 }
480495
481496 /// The arguments needed to compile this target.
@@ -868,6 +883,65 @@ public class BuildPlan {
868883 /// Diagnostics Engine for emitting diagnostics.
869884 let diagnostics : DiagnosticsEngine
870885
886+ private static func planLinuxMain(
887+ _ buildParameters: BuildParameters ,
888+ _ graph: PackageGraph
889+ ) throws -> ( ResolvedTarget , SwiftTargetBuildDescription ) ? {
890+ guard buildParameters. triple. isLinux ( ) else {
891+ return nil
892+ }
893+
894+ // Currently, there can be only one test product in a package graph.
895+ guard let testProduct = graph. allProducts. first ( where: { $0. type == . test } ) else {
896+ return nil
897+ }
898+
899+ if !buildParameters. enableTestDiscovery {
900+ guard let linuxMainTarget = testProduct. linuxMainTarget else {
901+ throw Error . missingLinuxMain
902+ }
903+
904+ let desc = SwiftTargetBuildDescription (
905+ target: linuxMainTarget,
906+ buildParameters: buildParameters,
907+ isTestTarget: true
908+ )
909+ return ( linuxMainTarget, desc)
910+ }
911+
912+ // We'll generate sources containing the test names as part of the build process.
913+ let derivedTestListDir = buildParameters. buildPath. appending ( components: " testlist.derived " )
914+ let mainFile = derivedTestListDir. appending ( component: " main.swift " )
915+
916+ var paths : [ AbsolutePath ] = [ ]
917+ paths. append ( mainFile)
918+ let testTargets = graph. rootPackages. flatMap { $0. targets } . filter { $0. type == . test }
919+ for testTarget in testTargets {
920+ let path = derivedTestListDir. appending ( components: testTarget. name + " .swift " )
921+ paths. append ( path)
922+ }
923+
924+ let src = Sources ( paths: paths, root: derivedTestListDir)
925+
926+ let swiftTarget = SwiftTarget (
927+ testDiscoverySrc: src,
928+ name: testProduct. name,
929+ dependencies: testProduct. underlyingProduct. targets)
930+ let linuxMainTarget = ResolvedTarget (
931+ target: swiftTarget,
932+ dependencies: testProduct. targets. map ( ResolvedTarget . Dependency. target)
933+ )
934+
935+ let target = SwiftTargetBuildDescription (
936+ target: linuxMainTarget,
937+ buildParameters: buildParameters,
938+ isTestTarget: true ,
939+ testDiscoveryTarget: true
940+ )
941+
942+ return ( linuxMainTarget, target)
943+ }
944+
871945 /// Create a build plan with build parameters and a package graph.
872946 public init (
873947 buildParameters: BuildParameters ,
@@ -921,19 +995,10 @@ public class BuildPlan {
921995 throw Diagnostics . fatalError
922996 }
923997
924- if buildParameters. triple. isLinux ( ) {
925- // FIXME: Create a target for LinuxMain file on linux.
926- // This will go away once it is possible to auto detect tests.
927- let testProducts = graph. allProducts. filter ( { $0. type == . test } )
928-
929- for product in testProducts {
930- guard let linuxMainTarget = product. linuxMainTarget else {
931- throw Error . missingLinuxMain
932- }
933- let target = SwiftTargetBuildDescription (
934- target: linuxMainTarget, buildParameters: buildParameters, isTestTarget: true )
935- targetMap [ linuxMainTarget] = . swift( target)
936- }
998+ // Plan the linux main target.
999+ if let result = try Self . planLinuxMain ( buildParameters, graph) {
1000+ targetMap [ result. 0 ] = . swift( result. 1 )
1001+ self . linuxMainTarget = result. 0
9371002 }
9381003
9391004 var productMap : [ ResolvedProduct : ProductBuildDescription ] = [ : ]
@@ -953,6 +1018,8 @@ public class BuildPlan {
9531018 try plan ( )
9541019 }
9551020
1021+ private var linuxMainTarget : ResolvedTarget ?
1022+
9561023 static func validateDeploymentVersionOfProductDependency(
9571024 _ product: ResolvedProduct ,
9581025 forTarget target: ResolvedTarget ,
@@ -1094,7 +1161,7 @@ public class BuildPlan {
10941161
10951162 if buildParameters. triple. isLinux ( ) {
10961163 if product. type == . test {
1097- product . linuxMainTarget. map ( { staticTargets. append ( $0) } )
1164+ linuxMainTarget. map ( { staticTargets. append ( $0) } )
10981165 }
10991166 }
11001167
0 commit comments