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")) + } + } + } }