Skip to content

Commit 9580360

Browse files
committed
Filter out xcode toolchain from the output on non-macOS
Add a little documentation for the xcode toolchain selector Add tests for the xcode toolchain selector and version
1 parent 2cd4cad commit 9580360

File tree

10 files changed

+84
-6
lines changed

10 files changed

+84
-6
lines changed

Sources/Swiftly/Config.swift

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,14 @@ public struct Config: Codable, Equatable {
5151
}
5252

5353
public func listInstalledToolchains(selector: ToolchainSelector?) -> [ToolchainVersion] {
54+
#if os(macOS)
55+
let systemToolchains: [ToolchainVersion] = [.xcodeVersion]
56+
#else
57+
let systemToolchains = []
58+
#endif
59+
5460
guard let selector else {
55-
return Array(self.installedToolchains) + [.xcodeVersion]
61+
return Array(self.installedToolchains) + systemToolchains
5662
}
5763

5864
if case .latest = selector {
@@ -63,7 +69,7 @@ public struct Config: Codable, Equatable {
6369
return ts
6470
}
6571

66-
return (self.installedToolchains + [.xcodeVersion]).filter { toolchain in
72+
return (self.installedToolchains + systemToolchains).filter { toolchain in
6773
selector.matches(toolchain: toolchain)
6874
}
6975
}

Sources/Swiftly/List.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,10 @@ struct List: SwiftlyCommand {
9898
await printToolchain(toolchain)
9999
}
100100

101+
#if os(macOS)
101102
await ctx.print("")
102103
await printToolchain(ToolchainVersion.xcode)
104+
#endif
103105
}
104106
}
105107
}

Sources/Swiftly/Uninstall.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,11 @@ struct Uninstall: SwiftlyCommand {
6868
toolchains = installedToolchains
6969
}
7070

71+
// Filter out the xcode toolchain here since it is not uninstallable
7172
toolchains.removeAll(where: { $0 == .xcodeVersion })
7273

7374
guard !toolchains.isEmpty else {
74-
await ctx.print("No toolchains matched \"\(self.toolchain)\"")
75+
await ctx.print("No toolchains can be uninstalled that match \"\(self.toolchain)\"")
7576
return
7677
}
7778

Sources/Swiftly/Use.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,12 @@ struct Use: SwiftlyCommand {
5050
5151
$ swiftly use 5.7-snapshot
5252
$ swiftly use main-snapshot
53+
54+
macOS ONLY: There is a special selector for swiftly to use your Xcode toolchain. \
55+
If there are multiple versions of Xcode then swiftly will use the currently selected \
56+
toolchain from xcode-select.
57+
58+
$ swiftly use xcode
5359
"""
5460
))
5561
var toolchain: String?
@@ -240,7 +246,7 @@ public func selectToolchain(_ ctx: SwiftlyCoreContext, config: inout Config, glo
240246

241247
// Check to ensure that the global default in use toolchain matches one of the installed toolchains, and return
242248
// no selected toolchain if it doesn't.
243-
guard let defaultInUse = config.inUse, config.installedToolchains.contains(defaultInUse) else {
249+
guard let defaultInUse = config.inUse, config.listInstalledToolchains(selector: nil).contains(defaultInUse) else {
244250
return (nil, .globalDefault)
245251
}
246252

Sources/SwiftlyCore/ToolchainVersion.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,10 @@ extension ToolchainVersion: Comparable {
243243
case (.stable, .snapshot):
244244
return !(rhs < lhs)
245245
case (.xcode, .xcode):
246+
return false
247+
case (.xcode, _):
248+
return false
249+
case (_, .xcode):
246250
return true
247251
default:
248252
return false

Tests/SwiftlyTests/InstallTests.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,4 +262,11 @@ import Testing
262262
try await SwiftlyTests.installMockedToolchain(selector: ToolchainVersion.newStable.name, args: ["--use"])
263263
try await SwiftlyTests.validateInUse(expected: .newStable)
264264
}
265+
266+
/// Verify that xcode can't be installed like regular toolchains
267+
@Test(.testHomeMockedToolchain()) func installXcode() async throws {
268+
try await #expect(throws: SwiftlyError.self) {
269+
try await SwiftlyTests.runCommand(Install.self, ["install", "xcode", "--post-install-file=\(Swiftly.currentPlatform.getTempFilePath().path)"])
270+
}
271+
}
265272
}

Tests/SwiftlyTests/ListTests.swift

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,19 @@ import Testing
4444
let output = try await SwiftlyTests.runWithMockedIO(List.self, args)
4545

4646
let parsedToolchains = output.compactMap { outputLine in
47+
#if !os(macOS)
4748
Set<ToolchainVersion>.allToolchains().first {
4849
outputLine.contains(String(describing: $0))
4950
}
51+
#else
52+
(Set<ToolchainVersion>.allToolchains() + [.xcodeVersion]).first {
53+
outputLine.contains(String(describing: $0))
54+
}
55+
#endif
5056
}
5157

5258
// Ensure extra toolchains weren't accidentally included in the output.
53-
guard parsedToolchains.count == output.filter({ $0.hasPrefix("Swift") || $0.contains("-snapshot") }).count else {
59+
guard parsedToolchains.count == output.filter({ $0.hasPrefix("Swift") || $0.contains("-snapshot") || $0.contains("xcode") }).count else {
5460
throw SwiftlyTestError(message: "unexpected listed toolchains in \(output)")
5561
}
5662

@@ -62,7 +68,11 @@ import Testing
6268
@Test func list() async throws {
6369
try await self.runListTest {
6470
let toolchains = try await self.runList(selector: nil)
71+
#if !os(macOS)
6572
#expect(toolchains == Self.sortedReleaseToolchains + Self.sortedSnapshotToolchains)
73+
#else
74+
#expect(toolchains == Self.sortedReleaseToolchains + Self.sortedSnapshotToolchains + [.xcodeVersion])
75+
#endif
6676
}
6777
}
6878

@@ -155,8 +165,14 @@ import Testing
155165

156166
/// Tests that `list` properly handles the case where no toolchains have been installed yet.
157167
@Test(.testHome(Self.homeName)) func listEmpty() async throws {
168+
#if !os(macOS)
169+
let systemToolchains: [ToolchainVersion] = []
170+
#else
171+
let systemToolchains: [ToolchainVersion] = [.xcodeVersion]
172+
#endif
173+
158174
var toolchains = try await self.runList(selector: nil)
159-
#expect(toolchains == [])
175+
#expect(toolchains == systemToolchains)
160176

161177
toolchains = try await self.runList(selector: "5")
162178
#expect(toolchains == [])

Tests/SwiftlyTests/PlatformTests.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,4 +85,19 @@ import Testing
8585
toolchains = try FileManager.default.contentsOfDirectory(at: Swiftly.currentPlatform.swiftlyToolchainsDir(SwiftlyTests.ctx), includingPropertiesForKeys: nil)
8686
#expect(0 == toolchains.count)
8787
}
88+
89+
#if os(macOS)
90+
@Test(.testHome()) func findXcodeToolchainLocation() async throws {
91+
// GIVEN: the xcode toolchain
92+
// AND there is xcode installed
93+
guard let xcodeLocation = try? await Swiftly.currentPlatform.runProgramOutput("xcode-select", "-p"), xcodeLocation != "" else {
94+
return
95+
}
96+
97+
// WHEN: the location of the xcode toolchain can be found
98+
let toolchainLocation = try await Swiftly.currentPlatform.findToolchainLocation(SwiftlyTests.ctx, .xcodeVersion)
99+
100+
#expect(toolchainLocation.path.hasPrefix(xcodeLocation.replacingOccurrences(of: "\n", with: "")))
101+
}
102+
#endif
88103
}

Tests/SwiftlyTests/UninstallTests.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,4 +280,9 @@ import Testing
280280
description: "uninstall did not uninstall all toolchains"
281281
)
282282
}
283+
284+
@Test(.mockHomeToolchains(Self.homeName, toolchains: [])) func uninstallXcode() async throws {
285+
let output = try await SwiftlyTests.runWithMockedIO(Uninstall.self, ["uninstall", "-y", ToolchainVersion.xcodeVersion.name])
286+
#expect(!output.filter { $0.contains("No toolchains can be uninstalled that match \"xcode\"") }.isEmpty)
287+
}
283288
}

Tests/SwiftlyTests/UseTests.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,13 @@ import Testing
148148
)
149149
}
150150

151+
#if os(macOS)
152+
/// Tests that the xcode toolchain can be used on macOS
153+
@Test(.mockHomeToolchains()) func useXcode() async throws {
154+
try await self.useAndValidate(argument: ToolchainVersion.xcodeVersion.name, expectedVersion: .xcode)
155+
}
156+
#endif
157+
151158
/// Tests that the `use` command gracefully exits when executed before any toolchains have been installed.
152159
@Test(.mockHomeToolchains(toolchains: [])) func useNoInstalledToolchains() async throws {
153160
try await SwiftlyTests.runCommand(Use.self, ["use", "-g", "latest"])
@@ -209,6 +216,15 @@ import Testing
209216
}
210217
}
211218

219+
#if os(macOS)
220+
/// Tests that running a use command without an argument prints the xcode toolchain when it is in use.
221+
@Test(.mockHomeToolchains()) func printInUseXcode() async throws {
222+
try await SwiftlyTests.runCommand(Use.self, ["use", "-g", "xcode"])
223+
var output = try await SwiftlyTests.runWithMockedIO(Use.self, ["use", "-g"])
224+
#expect(output.contains(where: { $0.contains("xcode") }))
225+
}
226+
#endif
227+
212228
/// Tests in-use toolchain selected by the .swift-version file.
213229
@Test func swiftVersionFile() async throws {
214230
let toolchains = [

0 commit comments

Comments
 (0)