@@ -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.
@@ -469,13 +474,22 @@ public final class SwiftTargetBuildDescription {
469474 /// If this target is a test target.
470475 public let isTestTarget : Bool
471476
477+ /// True if this is the test discovery target.
478+ public let testDiscoveryTarget : Bool
479+
472480 /// Create a new target description with target and build parameters.
473- init ( target: ResolvedTarget , buildParameters: BuildParameters , isTestTarget: Bool ? = nil ) {
481+ init (
482+ target: ResolvedTarget ,
483+ buildParameters: BuildParameters ,
484+ isTestTarget: Bool ? = nil ,
485+ testDiscoveryTarget: Bool = false
486+ ) {
474487 assert ( target. underlyingTarget is SwiftTarget , " underlying target type mismatch \( target) " )
475488 self . target = target
476489 self . buildParameters = buildParameters
477490 // Unless mentioned explicitly, use the target type to determine if this is a test target.
478491 self . isTestTarget = isTestTarget ?? ( target. type == . test)
492+ self . testDiscoveryTarget = testDiscoveryTarget
479493 }
480494
481495 /// The arguments needed to compile this target.
@@ -868,6 +882,65 @@ public class BuildPlan {
868882 /// Diagnostics Engine for emitting diagnostics.
869883 let diagnostics : DiagnosticsEngine
870884
885+ private static func planLinuxMain(
886+ _ buildParameters: BuildParameters ,
887+ _ graph: PackageGraph
888+ ) throws -> ( ResolvedTarget , SwiftTargetBuildDescription ) ? {
889+ guard buildParameters. triple. isLinux ( ) else {
890+ return nil
891+ }
892+
893+ // Currently, there can be only one test product in a package graph.
894+ guard let testProduct = graph. allProducts. first ( where: { $0. type == . test } ) else {
895+ return nil
896+ }
897+
898+ if !buildParameters. enableTestDiscovery {
899+ guard let linuxMainTarget = testProduct. linuxMainTarget else {
900+ throw Error . missingLinuxMain
901+ }
902+
903+ let desc = SwiftTargetBuildDescription (
904+ target: linuxMainTarget,
905+ buildParameters: buildParameters,
906+ isTestTarget: true
907+ )
908+ return ( linuxMainTarget, desc)
909+ }
910+
911+ // We'll generate sources containing the test names as part of the build process.
912+ let derivedTestListDir = buildParameters. buildPath. appending ( components: " testlist.derived " )
913+ let mainFile = derivedTestListDir. appending ( component: " main.swift " )
914+
915+ var paths : [ AbsolutePath ] = [ ]
916+ paths. append ( mainFile)
917+ let testTargets = graph. rootPackages. flatMap { $0. targets } . filter { $0. type == . test }
918+ for testTarget in testTargets {
919+ let path = derivedTestListDir. appending ( components: testTarget. name + " .swift " )
920+ paths. append ( path)
921+ }
922+
923+ let src = Sources ( paths: paths, root: derivedTestListDir)
924+
925+ let swiftTarget = SwiftTarget (
926+ testDiscoverySrc: src,
927+ name: testProduct. name,
928+ dependencies: testProduct. underlyingProduct. targets)
929+ let linuxMainTarget = ResolvedTarget (
930+ target: swiftTarget,
931+ dependencies: testProduct. targets. map ( ResolvedTarget . Dependency. target)
932+ )
933+
934+ let target = SwiftTargetBuildDescription (
935+ target: linuxMainTarget,
936+ buildParameters: buildParameters,
937+ isTestTarget: true ,
938+ testDiscoveryTarget: true
939+ )
940+
941+ return ( linuxMainTarget, target)
942+ }
943+
871944 /// Create a build plan with build parameters and a package graph.
872945 public init (
873946 buildParameters: BuildParameters ,
@@ -921,19 +994,10 @@ public class BuildPlan {
921994 throw Diagnostics . fatalError
922995 }
923996
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- }
997+ // Plan the linux main target.
998+ if let result = try Self . planLinuxMain ( buildParameters, graph) {
999+ targetMap [ result. 0 ] = . swift( result. 1 )
1000+ self . linuxMainTarget = result. 0
9371001 }
9381002
9391003 var productMap : [ ResolvedProduct : ProductBuildDescription ] = [ : ]
@@ -953,6 +1017,8 @@ public class BuildPlan {
9531017 try plan ( )
9541018 }
9551019
1020+ private var linuxMainTarget : ResolvedTarget ?
1021+
9561022 static func validateDeploymentVersionOfProductDependency(
9571023 _ product: ResolvedProduct ,
9581024 forTarget target: ResolvedTarget ,
@@ -1094,7 +1160,7 @@ public class BuildPlan {
10941160
10951161 if buildParameters. triple. isLinux ( ) {
10961162 if product. type == . test {
1097- product . linuxMainTarget. map ( { staticTargets. append ( $0) } )
1163+ linuxMainTarget. map ( { staticTargets. append ( $0) } )
10981164 }
10991165 }
11001166
0 commit comments