Skip to content

Commit dc514d5

Browse files
Fix swiftc selection for the case no swiftc found in Swift SDK (#7296)
Fix `PATH` executable selection in `UserToolchain` ### Motivation: When `swiftc` is not in the toolchain bin directory of Swift SDK, system swiftc in PATH should be used. But the current implementation of `UserToolchain` always picks the last found entry in the PATH instead of the first one. This behavior was introduced by 22c2493 The causes actual issues when: 1. More than two `swiftc` are in `PATH` 2. The invoked `swift-build` is placed from outside of toolchain bin directory Then it uses the last found `swiftc` in `PATH` even though we expect the first one. ### Modifications: Fixed the order of `PATH` selection to prefer the first entry. ### Result: The first found `swiftc` is used as well as shell PATH selection.
1 parent e930796 commit dc514d5

File tree

2 files changed

+54
-14
lines changed

2 files changed

+54
-14
lines changed

Sources/PackageModel/UserToolchain.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,8 @@ public final class UserToolchain: Toolchain {
128128
continue
129129
}
130130
toolPath = path
131+
// Take the first match.
132+
break
131133
}
132134
guard let toolPath else {
133135
throw InvalidToolchainDiagnostic("could not find CLI tool `\(name)` at any of these directories: \(binDirectories)")

Tests/PackageModelTests/PackageModelTests.swift

Lines changed: 52 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -76,19 +76,20 @@ class PackageModelTests: XCTestCase {
7676
)
7777
}
7878

79+
// tiny PE binary from: https://archive.is/w01DO
80+
static let tinyPEBytes: [UInt8] = [
81+
0x4D, 0x5A, 0x00, 0x00, 0x50, 0x45, 0x00, 0x00, 0x4C, 0x01, 0x01, 0x00,
82+
0x6A, 0x2A, 0x58, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
83+
0x04, 0x00, 0x03, 0x01, 0x0B, 0x01, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00,
84+
0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00,
85+
0x04, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00,
86+
0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
87+
0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
88+
0x68, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
89+
0x02,
90+
]
91+
7992
func testWindowsLibrarianSelection() throws {
80-
// tiny PE binary from: https://archive.is/w01DO
81-
let contents: [UInt8] = [
82-
0x4D, 0x5A, 0x00, 0x00, 0x50, 0x45, 0x00, 0x00, 0x4C, 0x01, 0x01, 0x00,
83-
0x6A, 0x2A, 0x58, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
84-
0x04, 0x00, 0x03, 0x01, 0x0B, 0x01, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00,
85-
0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00,
86-
0x04, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00,
87-
0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
88-
0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
89-
0x68, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
90-
0x02,
91-
]
9293

9394
#if os(Windows)
9495
let suffix = ".exe"
@@ -99,8 +100,9 @@ class PackageModelTests: XCTestCase {
99100
let triple = try Triple("x86_64-unknown-windows-msvc")
100101
let fs = localFileSystem
101102

102-
try withTemporaryFile { [contents] _ in
103-
try withTemporaryDirectory(removeTreeOnDeinit: true) { [contents] tmp in
103+
try withTemporaryFile { _ in
104+
try withTemporaryDirectory(removeTreeOnDeinit: true) { tmp in
105+
let contents = Self.tinyPEBytes
104106
let bin = tmp.appending("bin")
105107
try fs.createDirectory(bin)
106108

@@ -140,4 +142,40 @@ class PackageModelTests: XCTestCase {
140142
}
141143
}
142144
}
145+
146+
func testDetermineSwiftCompilers() throws {
147+
let fs = localFileSystem
148+
try withTemporaryFile { _ in
149+
try withTemporaryDirectory(removeTreeOnDeinit: true) { tmp in
150+
// When swiftc is not in the toolchain bin directory, UserToolchain
151+
// should find it in the system PATH search paths in the order they
152+
// are specified.
153+
let toolchainPath = tmp.appending("swift.xctoolchain")
154+
let toolchainBinDir = toolchainPath.appending(components: "usr", "bin")
155+
// Create the toolchain bin directory, but don't put swiftc in it.
156+
try fs.createDirectory(toolchainBinDir, recursive: true)
157+
158+
// Create a directory with two swiftc binaries in it.
159+
let binDirs = ["bin1", "bin2"].map { tmp.appending($0) }
160+
for binDir in binDirs {
161+
try fs.createDirectory(binDir)
162+
let binFile = binDir.appending("swiftc")
163+
try fs.writeFileContents(binFile, bytes: ByteString(Self.tinyPEBytes))
164+
#if !os(Windows)
165+
try fs.chmod(.executable, path: binFile, options: [])
166+
#endif
167+
}
168+
169+
let compilers = try UserToolchain.determineSwiftCompilers(
170+
binDirectories: [toolchainBinDir],
171+
useXcrun: false,
172+
environment: [:],
173+
searchPaths: binDirs
174+
)
175+
176+
// The first swiftc in the search paths should be chosen.
177+
XCTAssertEqual(compilers.compile, binDirs.first?.appending("swiftc"))
178+
}
179+
}
180+
}
143181
}

0 commit comments

Comments
 (0)