From 9f3d98250db7307f461ecc176651f0030db47916 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 24 Jan 2024 19:16:34 +0000 Subject: [PATCH] Fix swiftc selection for the case no swiftc found in Swift SDK 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 22c249317fa5171f17a0e41efe81e69e054a3d75 --- Sources/PackageModel/UserToolchain.swift | 2 + .../PackageModelTests/PackageModelTests.swift | 66 +++++++++++++++---- 2 files changed, 54 insertions(+), 14 deletions(-) diff --git a/Sources/PackageModel/UserToolchain.swift b/Sources/PackageModel/UserToolchain.swift index 920558b47df..fbb9013c3f4 100644 --- a/Sources/PackageModel/UserToolchain.swift +++ b/Sources/PackageModel/UserToolchain.swift @@ -128,6 +128,8 @@ public final class UserToolchain: Toolchain { continue } toolPath = path + // Take the first match. + break } guard let toolPath else { throw InvalidToolchainDiagnostic("could not find CLI tool `\(name)` at any of these directories: \(binDirectories)") diff --git a/Tests/PackageModelTests/PackageModelTests.swift b/Tests/PackageModelTests/PackageModelTests.swift index 3fbfc13e8c7..54c32fa276e 100644 --- a/Tests/PackageModelTests/PackageModelTests.swift +++ b/Tests/PackageModelTests/PackageModelTests.swift @@ -76,19 +76,20 @@ class PackageModelTests: XCTestCase { ) } + // tiny PE binary from: https://archive.is/w01DO + static let tinyPEBytes: [UInt8] = [ + 0x4D, 0x5A, 0x00, 0x00, 0x50, 0x45, 0x00, 0x00, 0x4C, 0x01, 0x01, 0x00, + 0x6A, 0x2A, 0x58, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x03, 0x01, 0x0B, 0x01, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x68, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, + ] + func testWindowsLibrarianSelection() throws { - // tiny PE binary from: https://archive.is/w01DO - let contents: [UInt8] = [ - 0x4D, 0x5A, 0x00, 0x00, 0x50, 0x45, 0x00, 0x00, 0x4C, 0x01, 0x01, 0x00, - 0x6A, 0x2A, 0x58, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x03, 0x01, 0x0B, 0x01, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x68, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x02, - ] #if os(Windows) let suffix = ".exe" @@ -99,8 +100,9 @@ class PackageModelTests: XCTestCase { let triple = try Triple("x86_64-unknown-windows-msvc") let fs = localFileSystem - try withTemporaryFile { [contents] _ in - try withTemporaryDirectory(removeTreeOnDeinit: true) { [contents] tmp in + try withTemporaryFile { _ in + try withTemporaryDirectory(removeTreeOnDeinit: true) { tmp in + let contents = Self.tinyPEBytes let bin = tmp.appending("bin") try fs.createDirectory(bin) @@ -140,4 +142,40 @@ class PackageModelTests: XCTestCase { } } } + + func testDetermineSwiftCompilers() throws { + let fs = localFileSystem + try withTemporaryFile { _ in + try withTemporaryDirectory(removeTreeOnDeinit: true) { tmp in + // When swiftc is not in the toolchain bin directory, UserToolchain + // should find it in the system PATH search paths in the order they + // are specified. + let toolchainPath = tmp.appending("swift.xctoolchain") + let toolchainBinDir = toolchainPath.appending(components: "usr", "bin") + // Create the toolchain bin directory, but don't put swiftc in it. + try fs.createDirectory(toolchainBinDir, recursive: true) + + // Create a directory with two swiftc binaries in it. + let binDirs = ["bin1", "bin2"].map { tmp.appending($0) } + for binDir in binDirs { + try fs.createDirectory(binDir) + let binFile = binDir.appending("swiftc") + try fs.writeFileContents(binFile, bytes: ByteString(Self.tinyPEBytes)) + #if !os(Windows) + try fs.chmod(.executable, path: binFile, options: []) + #endif + } + + let compilers = try UserToolchain.determineSwiftCompilers( + binDirectories: [toolchainBinDir], + useXcrun: false, + environment: [:], + searchPaths: binDirs + ) + + // The first swiftc in the search paths should be chosen. + XCTAssertEqual(compilers.compile, binDirs.first?.appending("swiftc")) + } + } + } }