Skip to content

Commit d120823

Browse files
committed
swift-package-migrate: Add more clarity to manifest update error message
(cherry picked from commit e1deb23)
1 parent 6bb409c commit d120823

File tree

10 files changed

+257
-11
lines changed

10 files changed

+257
-11
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// swift-tools-version:5.8
2+
3+
import PackageDescription
4+
5+
var swiftSettings: [SwiftSetting] = []
6+
7+
let package = Package(
8+
name: "WithErrors",
9+
targets: [
10+
.target(
11+
name: "CannotFindSettings",
12+
swiftSettings: swiftSettings
13+
),
14+
.target(name: "A"),
15+
.target(name: "B"),
16+
]
17+
)
18+
19+
package.targets.append(
20+
.target(
21+
name: "CannotFindTarget",
22+
swiftSettings: swiftSettings
23+
),
24+
)
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// swift-tools-version:5.8
2+
3+
import PackageDescription
4+
5+
var swiftSettings: [SwiftSetting] = []
6+
7+
let package = Package(
8+
name: "WithErrors",
9+
targets: [
10+
.target(
11+
name: "CannotFindSettings",
12+
swiftSettings: swiftSettings
13+
),
14+
.target(name: "A",swiftSettings: [
15+
.enableUpcomingFeature("ExistentialAny"),
16+
.enableUpcomingFeature("InferIsolatedConformances"),]),
17+
.target(name: "B",swiftSettings: [
18+
.enableUpcomingFeature("ExistentialAny"),
19+
.enableUpcomingFeature("InferIsolatedConformances"),]),
20+
]
21+
)
22+
23+
package.targets.append(
24+
.target(
25+
name: "CannotFindTarget",
26+
swiftSettings: swiftSettings
27+
),
28+
)
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// swift-tools-version:5.8
2+
3+
import PackageDescription
4+
5+
var swiftSettings: [SwiftSetting] = []
6+
7+
let package = Package(
8+
name: "WithErrors",
9+
targets: [
10+
.target(
11+
name: "CannotFindSettings",
12+
swiftSettings: swiftSettings
13+
),
14+
.target(name: "A",swiftSettings: [
15+
.enableUpcomingFeature("ExistentialAny"),
16+
.enableUpcomingFeature("InferIsolatedConformances"),]),
17+
.target(name: "B"),
18+
]
19+
)
20+
21+
package.targets.append(
22+
.target(
23+
name: "CannotFindTarget",
24+
swiftSettings: swiftSettings
25+
),
26+
)
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// swift-tools-version:5.8
2+
3+
import PackageDescription
4+
5+
var swiftSettings: [SwiftSetting] = []
6+
7+
let package = Package(
8+
name: "WithErrors",
9+
targets: [
10+
.target(
11+
name: "CannotFindSettings",
12+
swiftSettings: swiftSettings
13+
),
14+
.target(name: "A",swiftSettings: [
15+
.enableUpcomingFeature("ExistentialAny"),
16+
.enableUpcomingFeature("InferIsolatedConformances"),]),
17+
.target(name: "B",swiftSettings: [
18+
.enableUpcomingFeature("ExistentialAny"),
19+
.enableUpcomingFeature("InferIsolatedConformances"),]),
20+
]
21+
)
22+
23+
package.targets.append(
24+
.target(
25+
name: "CannotFindTarget",
26+
swiftSettings: swiftSettings
27+
),
28+
)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
public func foo() {}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
public func foo() {}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
public func foo() {}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
public func foo() {}

Sources/Commands/PackageCommands/Migrate.swift

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import OrderedCollections
2323

2424
import PackageGraph
2525
import PackageModel
26+
import enum PackageModelSyntax.ManifestEditError
2627

2728
import SPMBuildCore
2829
import SwiftFixIt
@@ -153,9 +154,12 @@ extension SwiftPackageCommand {
153154

154155
// Once the fix-its were applied, it's time to update the
155156
// manifest with newly adopted feature settings.
157+
//
158+
// Loop over a sorted array to produce deterministic results and
159+
// order of diagnostics.
156160

157161
print("> Updating manifest")
158-
for (name, _) in modules {
162+
for name in modules.keys.sorted() {
159163
swiftCommandState.observabilityScope.emit(debug: "Adding feature(s) to '\(name)'")
160164
try self.updateManifest(
161165
for: name,
@@ -166,6 +170,8 @@ extension SwiftPackageCommand {
166170
}
167171

168172
/// Resolves the requested feature names.
173+
///
174+
/// - Returns: An array of resolved features, sorted by name.
169175
private func resolveRequestedFeatures(
170176
_ swiftCommandState: SwiftCommandState
171177
) throws -> [SwiftCompilerFeature] {
@@ -200,7 +206,9 @@ extension SwiftPackageCommand {
200206
resolvedFeatures.append(feature)
201207
}
202208

203-
return resolvedFeatures
209+
return resolvedFeatures.sorted { lhs, rhs in
210+
lhs.name < rhs.name
211+
}
204212
}
205213

206214
private func createBuildSystem(
@@ -266,15 +274,37 @@ extension SwiftPackageCommand {
266274
verbose: !self.globalOptions.logging.quiet
267275
)
268276
} catch {
269-
try swiftCommandState.observabilityScope.emit(
270-
error: """
271-
Could not update manifest for '\(target)' (\(error)). \
272-
Please enable '\(
273-
features.map { try $0.swiftSettingDescription }
274-
.joined(separator: ", ")
275-
)' features manually
276-
"""
277-
)
277+
var message =
278+
"Could not update manifest to enable requested features for target '\(target)' (\(error))"
279+
280+
// Do not suggest manual addition if something else is wrong or
281+
// if the error implies that it cannot be done.
282+
if let error = error as? ManifestEditError {
283+
switch error {
284+
case .cannotFindPackage,
285+
.cannotAddSettingsToPluginTarget,
286+
.existingDependency:
287+
break
288+
case .cannotFindArrayLiteralArgument,
289+
// This means the target could not be found
290+
// syntactically, not that it does not exist.
291+
.cannotFindTargets,
292+
.cannotFindTarget,
293+
// This means the swift-tools-version is lower than
294+
// the version where one of the setting was introduced.
295+
.oldManifest:
296+
let settings = try features.map {
297+
try $0.swiftSettingDescription
298+
}.joined(separator: ", ")
299+
300+
message += """
301+
. Please enable them manually by adding the following Swift settings to the target: \
302+
'\(settings)'
303+
"""
304+
}
305+
}
306+
307+
swiftCommandState.observabilityScope.emit(error: message)
278308
}
279309
}
280310

Tests/CommandsTests/PackageCommandTests.swift

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2248,6 +2248,100 @@ class PackageCommandTestCase: CommandsBuildProviderTestCase {
22482248
}
22492249
}
22502250

2251+
func testMigrateCommandUpdateManifestSingleTarget() async throws {
2252+
try XCTSkipIf(
2253+
!UserToolchain.default.supportesSupportedFeatures,
2254+
"skipping because test environment compiler doesn't support `-print-supported-features`"
2255+
)
2256+
2257+
try await fixture(name: "SwiftMigrate/UpdateManifest") { fixturePath in
2258+
_ = try await self.execute(
2259+
[
2260+
"migrate",
2261+
"--to-feature",
2262+
"ExistentialAny,InferIsolatedConformances",
2263+
"--target",
2264+
"A",
2265+
],
2266+
packagePath: fixturePath
2267+
)
2268+
2269+
let updatedManifest = try localFileSystem.readFileContents(
2270+
fixturePath.appending(components: "Package.swift")
2271+
)
2272+
let expectedManifest = try localFileSystem.readFileContents(
2273+
fixturePath.appending(components: "Package.updated.targets-A.swift")
2274+
)
2275+
XCTAssertEqual(updatedManifest, expectedManifest)
2276+
}
2277+
2278+
}
2279+
2280+
func testMigrateCommandUpdateManifest2Targets() async throws {
2281+
try XCTSkipIf(
2282+
!UserToolchain.default.supportesSupportedFeatures,
2283+
"skipping because test environment compiler doesn't support `-print-supported-features`"
2284+
)
2285+
2286+
try await fixture(name: "SwiftMigrate/UpdateManifest") { fixturePath in
2287+
_ = try await self.execute(
2288+
[
2289+
"migrate",
2290+
"--to-feature",
2291+
"ExistentialAny,InferIsolatedConformances",
2292+
"--target",
2293+
"A,B",
2294+
],
2295+
packagePath: fixturePath
2296+
)
2297+
2298+
let updatedManifest = try localFileSystem.readFileContents(
2299+
fixturePath.appending(components: "Package.swift")
2300+
)
2301+
let expectedManifest = try localFileSystem.readFileContents(
2302+
fixturePath.appending(components: "Package.updated.targets-A-B.swift")
2303+
)
2304+
XCTAssertEqual(updatedManifest, expectedManifest)
2305+
}
2306+
}
2307+
2308+
func testMigrateCommandUpdateManifestWithErrors() async throws {
2309+
try XCTSkipIf(
2310+
!UserToolchain.default.supportesSupportedFeatures,
2311+
"skipping because test environment compiler doesn't support `-print-supported-features`"
2312+
)
2313+
2314+
try await fixture(name: "SwiftMigrate/UpdateManifest") { fixturePath in
2315+
try await XCTAssertThrowsCommandExecutionError(
2316+
await self.execute(
2317+
["migrate", "--to-feature", "ExistentialAny,InferIsolatedConformances,StrictMemorySafety"],
2318+
packagePath: fixturePath
2319+
)
2320+
) { error in
2321+
// 'SwiftMemorySafety.strictMemorySafety' was introduced in 6.2.
2322+
XCTAssertMatch(
2323+
error.stderr,
2324+
.contains(
2325+
"""
2326+
error: Could not update manifest to enable requested features for target 'A' (package manifest version 5.8.0 is too old: please update to manifest version 6.2.0 or newer). Please enable them manually by adding the following Swift settings to the target: '.enableUpcomingFeature("ExistentialAny"), .enableUpcomingFeature("InferIsolatedConformances"), .strictMemorySafety()'
2327+
error: Could not update manifest to enable requested features for target 'B' (package manifest version 5.8.0 is too old: please update to manifest version 6.2.0 or newer). Please enable them manually by adding the following Swift settings to the target: '.enableUpcomingFeature("ExistentialAny"), .enableUpcomingFeature("InferIsolatedConformances"), .strictMemorySafety()'
2328+
error: Could not update manifest to enable requested features for target 'CannotFindSettings' (unable to find array literal for 'swiftSettings' argument). Please enable them manually by adding the following Swift settings to the target: '.enableUpcomingFeature("ExistentialAny"), .enableUpcomingFeature("InferIsolatedConformances"), .strictMemorySafety()'
2329+
error: Could not update manifest to enable requested features for target 'CannotFindTarget' (unable to find target named 'CannotFindTarget' in package). Please enable them manually by adding the following Swift settings to the target: '.enableUpcomingFeature("ExistentialAny"), .enableUpcomingFeature("InferIsolatedConformances"), .strictMemorySafety()'
2330+
"""
2331+
)
2332+
)
2333+
}
2334+
2335+
let updatedManifest = try localFileSystem.readFileContents(
2336+
fixturePath.appending(components: "Package.swift")
2337+
)
2338+
let expectedManifest = try localFileSystem.readFileContents(
2339+
fixturePath.appending(components: "Package.updated.targets-all.swift")
2340+
)
2341+
XCTAssertEqual(updatedManifest, expectedManifest)
2342+
}
2343+
}
2344+
22512345
func testBuildToolPlugin() async throws {
22522346
try await testBuildToolPlugin(staticStdlib: false)
22532347
}
@@ -4206,6 +4300,18 @@ class PackageCommandSwiftBuildTests: PackageCommandTestCase {
42064300
throw XCTSkip("SWBINTTODO: Build plan is not currently supported")
42074301
}
42084302

4303+
override func testMigrateCommandUpdateManifestSingleTarget() async throws {
4304+
throw XCTSkip("SWBINTTODO: Build plan is not currently supported")
4305+
}
4306+
4307+
override func testMigrateCommandUpdateManifest2Targets() async throws {
4308+
throw XCTSkip("SWBINTTODO: Build plan is not currently supported")
4309+
}
4310+
4311+
override func testMigrateCommandUpdateManifestWithErrors() async throws {
4312+
throw XCTSkip("SWBINTTODO: Build plan is not currently supported")
4313+
}
4314+
42094315
override func testCommandPluginTestingCallbacks() async throws {
42104316
throw XCTSkip("SWBINTTODO: Requires PIF generation to adopt new test runner product type")
42114317
try XCTSkipOnWindows(because: "TSCBasic/Path.swift:969: Assertion failed, https://github.com/swiftlang/swift-package-manager/issues/8602")

0 commit comments

Comments
 (0)