Skip to content

Commit 7139eae

Browse files
authored
deprecate LinuxMain.swift (#3053)
motivation: Swift 5.1 introduced the --enable-test-discovery flag which instructs SwiftPM to discover tests instead of relying on a test manifest file named LinuxMain.swift. This functionality has been pretty well adopted and we are making it the default changes: * deprecate --enable-test-discovery flag * deprecate --generate-linuxmain flag * automatically detect tests on non-darwin platforms * support "XCTMain.swift", "LinuxMain.swift" as escape hatch when test discovery is not appropriate. This files take priority. * `package init` no longet generate LinuxMain.swift * add and adjust tests rdar://problem/64197288 rdar://problem/59534310 rdar://problem/62895076
1 parent 460eba1 commit 7139eae

File tree

30 files changed

+429
-574
lines changed

30 files changed

+429
-574
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// swift-tools-version:4.2
2+
import PackageDescription
3+
4+
let package = Package(
5+
name: "Simple",
6+
targets: [
7+
.target(name: "Simple"),
8+
.testTarget(name: "SimpleTests", dependencies: ["Simple"]),
9+
]
10+
)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
struct Simple {
2+
3+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import XCTest
2+
@testable import Simple
3+
4+
class SimpleTests: XCTestCase {
5+
6+
func testExample1() {
7+
}
8+
9+
func test_Example2() {
10+
}
11+
12+
func testExample3(arg: String) {
13+
}
14+
15+
func nontest() {
16+
}
17+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// swift-tools-version:5.3
2+
// The swift-tools-version declares the minimum version of Swift required to build this package.
3+
4+
import PackageDescription
5+
6+
let package = Package(
7+
name: "hello world",
8+
products: [
9+
// Products define the executables and libraries a package produces, and make them visible to other packages.
10+
.library(
11+
name: "hello world",
12+
targets: ["hello world"]),
13+
],
14+
dependencies: [
15+
// Dependencies declare other packages that this package depends on.
16+
// .package(url: /* package url */, from: "1.0.0"),
17+
],
18+
targets: [
19+
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
20+
// Targets can depend on other targets in this package, and on products in packages this package depends on.
21+
.target(
22+
name: "hello world",
23+
dependencies: []),
24+
.testTarget(
25+
name: "hello world tests",
26+
dependencies: ["hello world"]),
27+
]
28+
)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
struct hello_world {
2+
var text = "Hello, World!"
3+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import XCTest
2+
@testable import hello_world
3+
4+
final class hello_worldTests: XCTestCase {
5+
func testExample() {
6+
// This is an example of a functional test case.
7+
// Use XCTAssert and related functions to verify your tests produce the correct
8+
// results.
9+
XCTAssertEqual(hello_world().text, "Hello, World!")
10+
}
11+
}

Sources/Build/BuildDelegate.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ final class TestDiscoveryCommand: CustomLLBuildCommand {
9090
}
9191

9292
let outputs = tool.outputs.compactMap{ try? AbsolutePath(validating: $0.name) }
93-
let testsByModule = Dictionary(grouping: tests, by: { $0.module })
93+
let testsByModule = Dictionary(grouping: tests, by: { $0.module.spm_mangledToC99ExtendedIdentifier() })
9494

9595
func isMainFile(_ path: AbsolutePath) -> Bool {
9696
return path.basename == "main.swift"
@@ -109,7 +109,7 @@ final class TestDiscoveryCommand: CustomLLBuildCommand {
109109

110110
// FIXME: This is relying on implementation detail of the output but passing the
111111
// the context all the way through is not worth it right now.
112-
let module = file.basenameWithoutExt
112+
let module = file.basenameWithoutExt.spm_mangledToC99ExtendedIdentifier()
113113

114114
guard let tests = testsByModule[module] else {
115115
// This module has no tests so just write an empty file for it.

Sources/Build/BuildPlan.swift

Lines changed: 59 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,8 @@ extension BuildParameters {
9191
case .auto:
9292
if configuration == .debug {
9393
addIndexStoreArguments = true
94-
} else if enableTestDiscovery && target.type == .test {
95-
// Test discovery requires an index store for the test targets
96-
// to discover the tests
94+
} else if target.type == .test {
95+
// Test discovery requires an index store for the test target to discover the tests
9796
addIndexStoreArguments = true
9897
} else {
9998
addIndexStoreArguments = false
@@ -963,15 +962,10 @@ public final class SwiftTargetBuildDescription {
963962

964963
/// Testing arguments according to the build configuration.
965964
private var testingArguments: [String] {
966-
switch buildParameters.configuration {
967-
case .debug:
965+
if buildParameters.enableTestability {
968966
return ["-enable-testing"]
969-
case .release:
970-
if self.buildParameters.enableTestDiscovery {
971-
return ["-enable-testing"]
972-
} else {
973-
return []
974-
}
967+
} else {
968+
return []
975969
}
976970
}
977971

@@ -1100,10 +1094,11 @@ public final class ProductBuildDescription {
11001094
// No arguments for static libraries.
11011095
return []
11021096
case .test:
1103-
// Test products are bundle on macOS, executable on linux.
1104-
if buildParameters.triple.isDarwin() {
1097+
// Test products are bundle when using objectiveC, executable when using test manifests.
1098+
switch buildParameters.testDiscoveryStrategy {
1099+
case .objectiveC:
11051100
args += ["-Xlinker", "-bundle"]
1106-
} else {
1101+
case .manifest:
11071102
args += ["-emit-executable"]
11081103
}
11091104
case .library(.dynamic):
@@ -1224,16 +1219,11 @@ public final class ProductBuildDescription {
12241219
public class BuildPlan {
12251220

12261221
public enum Error: Swift.Error, CustomStringConvertible, Equatable {
1227-
/// The linux main file is missing.
1228-
case missingLinuxMain
1229-
12301222
/// There is no buildable target in the graph.
12311223
case noBuildableTarget
12321224

12331225
public var description: String {
12341226
switch self {
1235-
case .missingLinuxMain:
1236-
return "missing LinuxMain.swift file in the Tests directory"
12371227
case .noBuildableTarget:
12381228
return "the package does not contain a buildable target"
12391229
}
@@ -1273,64 +1263,60 @@ public class BuildPlan {
12731263
/// Diagnostics Engine for emitting diagnostics.
12741264
let diagnostics: DiagnosticsEngine
12751265

1276-
private static func planLinuxMain(
1266+
private static func makeTestManifestTargets(
12771267
_ buildParameters: BuildParameters,
12781268
_ graph: PackageGraph
1279-
) throws -> [(ResolvedProduct, SwiftTargetBuildDescription)] {
1280-
guard !buildParameters.triple.isDarwin() else {
1281-
return []
1269+
) throws -> [(product: ResolvedProduct, targetBuildDescription: SwiftTargetBuildDescription)] {
1270+
guard case .manifest(let generate) = buildParameters.testDiscoveryStrategy else {
1271+
preconditionFailure("makeTestManifestTargets should not be used for build plan with useTestManifest set to false")
12821272
}
12831273

12841274
var result: [(ResolvedProduct, SwiftTargetBuildDescription)] = []
1285-
12861275
for testProduct in graph.allProducts where testProduct.type == .test {
1287-
// Create the target description from the linux main if test discovery is off.
1288-
if !buildParameters.enableTestDiscovery {
1289-
guard let linuxMainTarget = testProduct.linuxMainTarget else {
1290-
throw Error.missingLinuxMain
1291-
}
1292-
1276+
// if test manifest exists, prefer that over test detection,
1277+
// this is designed as an escape hatch when test discovery is not appropriate
1278+
// and for backwards compatibility for projects that have existing test manifests (LinuxMain.swift)
1279+
if let testManifestTarget = testProduct.testManifestTarget, !generate {
12931280
let desc = try SwiftTargetBuildDescription(
1294-
target: linuxMainTarget,
1281+
target: testManifestTarget,
12951282
buildParameters: buildParameters,
12961283
isTestTarget: true
12971284
)
12981285

12991286
result.append((testProduct, desc))
1300-
continue
1301-
}
1302-
1303-
// We'll generate sources containing the test names as part of the build process.
1304-
let derivedTestListDir = buildParameters.buildPath.appending(components: "\(testProduct.name)Testlist.derived")
1305-
let mainFile = derivedTestListDir.appending(component: "main.swift")
1306-
1307-
var paths: [AbsolutePath] = []
1308-
paths.append(mainFile)
1309-
for testTarget in testProduct.targets {
1310-
let path = derivedTestListDir.appending(components: testTarget.name + ".swift")
1311-
paths.append(path)
1312-
}
1287+
} else {
1288+
// We'll generate sources containing the test names as part of the build process.
1289+
let derivedTestListDir = buildParameters.buildPath.appending(components: "\(testProduct.name).derived")
1290+
let mainFile = derivedTestListDir.appending(component: "main.swift")
1291+
1292+
var paths: [AbsolutePath] = []
1293+
paths.append(mainFile)
1294+
for testTarget in testProduct.targets {
1295+
let path = derivedTestListDir.appending(components: testTarget.name + ".swift")
1296+
paths.append(path)
1297+
}
13131298

1314-
let src = Sources(paths: paths, root: derivedTestListDir)
1299+
let src = Sources(paths: paths, root: derivedTestListDir)
13151300

1316-
let swiftTarget = SwiftTarget(
1317-
testDiscoverySrc: src,
1318-
name: testProduct.name,
1319-
dependencies: testProduct.underlyingProduct.targets.map { .target($0, conditions: []) }
1320-
)
1321-
let linuxMainTarget = ResolvedTarget(
1322-
target: swiftTarget,
1323-
dependencies: testProduct.targets.map { .target($0, conditions: []) }
1324-
)
1301+
let swiftTarget = SwiftTarget(
1302+
testDiscoverySrc: src,
1303+
name: testProduct.name,
1304+
dependencies: testProduct.underlyingProduct.targets.map { .target($0, conditions: []) }
1305+
)
1306+
let testManifestTarget = ResolvedTarget(
1307+
target: swiftTarget,
1308+
dependencies: testProduct.targets.map { .target($0, conditions: []) }
1309+
)
13251310

1326-
let target = try SwiftTargetBuildDescription(
1327-
target: linuxMainTarget,
1328-
buildParameters: buildParameters,
1329-
isTestTarget: true,
1330-
testDiscoveryTarget: true
1331-
)
1311+
let target = try SwiftTargetBuildDescription(
1312+
target: testManifestTarget,
1313+
buildParameters: buildParameters,
1314+
isTestTarget: true,
1315+
testDiscoveryTarget: true
1316+
)
13321317

1333-
result.append((testProduct, target))
1318+
result.append((testProduct, target))
1319+
}
13341320
}
13351321
return result
13361322
}
@@ -1389,11 +1375,13 @@ public class BuildPlan {
13891375
throw Diagnostics.fatalError
13901376
}
13911377

1392-
// Plan the linux main target.
1393-
let results = try Self.planLinuxMain(buildParameters, graph)
1394-
for result in results {
1395-
targetMap[result.1.target] = .swift(result.1)
1396-
linuxMainMap[result.0] = result.1.target
1378+
// Plan the test manifest target.
1379+
if case .manifest = buildParameters.testDiscoveryStrategy {
1380+
let testManifestTargets = try Self.makeTestManifestTargets(buildParameters, graph)
1381+
for item in testManifestTargets {
1382+
targetMap[item.targetBuildDescription.target] = .swift(item.targetBuildDescription)
1383+
testManifestTargetsMap[item.product] = item.targetBuildDescription.target
1384+
}
13971385
}
13981386

13991387
var productMap: [ResolvedProduct: ProductBuildDescription] = [:]
@@ -1413,7 +1401,7 @@ public class BuildPlan {
14131401
try plan()
14141402
}
14151403

1416-
private var linuxMainMap: [ResolvedProduct: ResolvedTarget] = [:]
1404+
private var testManifestTargetsMap: [ResolvedProduct: ResolvedTarget] = [:]
14171405

14181406
static func validateDeploymentVersionOfProductDependency(
14191407
_ product: ResolvedProduct,
@@ -1603,9 +1591,10 @@ public class BuildPlan {
16031591
}
16041592
}
16051593

1606-
if !buildParameters.triple.isDarwin() {
1607-
if product.type == .test {
1608-
linuxMainMap[product].map{ staticTargets.append($0) }
1594+
// add test manifest targets
1595+
if case .manifest = buildParameters.testDiscoveryStrategy {
1596+
if product.type == .test, let testManifestTarget = testManifestTargetsMap[product] {
1597+
staticTargets.append(testManifestTarget)
16091598
}
16101599
}
16111600

Sources/Build/ManifestBuilder.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ public class LLBuildManifestBuilder {
7474
}
7575
}
7676

77-
addTestFileGenerationCommand()
77+
addTestManifestGenerationCommand()
7878

7979
// Create command for all products in the plan.
8080
for (_, description) in plan.productMap {
@@ -727,7 +727,7 @@ extension LLBuildManifestBuilder {
727727
// MARK:- Test File Generation
728728

729729
extension LLBuildManifestBuilder {
730-
fileprivate func addTestFileGenerationCommand() {
730+
fileprivate func addTestManifestGenerationCommand() {
731731
for target in plan.targets {
732732
guard case .swift(let target) = target,
733733
target.isTestTarget,

Sources/Commands/CMakeLists.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ add_library(Commands
1010
APIDigester.swift
1111
Describe.swift
1212
Error.swift
13-
GenerateLinuxMain.swift
1413
MultiRootSupport.swift
1514
Options.swift
1615
show-dependencies.swift

0 commit comments

Comments
 (0)