From 529cd7886a631360e95533849401ffa0316cfdee Mon Sep 17 00:00:00 2001 From: Ben Barham Date: Mon, 8 Sep 2025 14:45:06 -0700 Subject: [PATCH 1/2] Use native root check on Windows Not sure exactly what paths are triggering this on Windows, as all the logs in #2174 are within `C:\`. But the stacktrace seems to strongly imply that we have an infinite loop here, so let's see if switching to the native check fixes this now that https://github.com/swiftlang/swift-foundation/issues/976 is in. Resolves #2174 (cherry picked from commit ad69881000a928ecd14fb1525c6a10d6a6f066f2) --- Sources/SwiftExtensions/URLExtensions.swift | 20 ++++++++++++-------- Sources/ToolchainRegistry/Toolchain.swift | 2 +- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/Sources/SwiftExtensions/URLExtensions.swift b/Sources/SwiftExtensions/URLExtensions.swift index 71cdee8a9..d14d26d74 100644 --- a/Sources/SwiftExtensions/URLExtensions.swift +++ b/Sources/SwiftExtensions/URLExtensions.swift @@ -12,6 +12,10 @@ package import Foundation +#if os(Windows) +import WinSDK +#endif + enum FilePathError: Error, CustomStringConvertible { case noFileSystemRepresentation(URL) case noFileURL(URL) @@ -83,14 +87,14 @@ extension URL { } package var isRoot: Bool { - #if os(Windows) - // FIXME: We should call into Windows' native check to check if this path is a root once https://github.com/swiftlang/swift-foundation/issues/976 is fixed. - return self.pathComponents.count <= 1 - #else - // On Linux, we may end up with an string for the path due to https://github.com/swiftlang/swift-foundation/issues/980 - // TODO: Remove the check for "" once https://github.com/swiftlang/swift-foundation/issues/980 is fixed. - return self.path == "/" || self.path == "" - #endif + get throws { + let checkPath = try filePath + #if os(Windows) + return checkPath.withCString(encodedAs: UTF16.self, PathCchIsRoot) + #else + return checkPath == "/" + #endif + } } /// Returns true if the path of `self` starts with the path in `other`. diff --git a/Sources/ToolchainRegistry/Toolchain.swift b/Sources/ToolchainRegistry/Toolchain.swift index 13dd0dfe3..673c9ea05 100644 --- a/Sources/ToolchainRegistry/Toolchain.swift +++ b/Sources/ToolchainRegistry/Toolchain.swift @@ -370,7 +370,7 @@ func containingXCToolchain( _ path: URL ) -> (XCToolchainPlist, URL)? { var path = path - while !path.isRoot { + while !((try? path.isRoot) ?? true) { if path.pathExtension == "xctoolchain" { if let infoPlist = orLog("Loading information from xctoolchain", { try XCToolchainPlist(fromDirectory: path) }) { return (infoPlist, path) From 53215eb3c0d59b4e0ab22d667b076f321ac89779 Mon Sep 17 00:00:00 2001 From: Ben Barham Date: Thu, 11 Sep 2025 16:25:39 -0700 Subject: [PATCH 2/2] Standardize file paths when attempting to find toolchains `containingXCToolchain` loops infinitely when given eg. `C:\foo\..\bar`. The underlying cause is that `deletingLastPathComponent` does not remove the `..` on Windows. On macOS it's potentially worse - it *adds* `..`. We don't see the infinite loop on macOS/Linux though because `AbsolutePath` already removes them (which is not the case on Windows). Resolves #2174. (cherry picked from commit a082d41a3cd4069600fc7094a82731aff54d9030) --- Sources/ToolchainRegistry/Toolchain.swift | 2 +- Tests/ToolchainRegistryTests/ToolchainRegistryTests.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/ToolchainRegistry/Toolchain.swift b/Sources/ToolchainRegistry/Toolchain.swift index 673c9ea05..7c3c957af 100644 --- a/Sources/ToolchainRegistry/Toolchain.swift +++ b/Sources/ToolchainRegistry/Toolchain.swift @@ -369,7 +369,7 @@ public final class Toolchain: Sendable { func containingXCToolchain( _ path: URL ) -> (XCToolchainPlist, URL)? { - var path = path + var path = path.standardizedFileURL while !((try? path.isRoot) ?? true) { if path.pathExtension == "xctoolchain" { if let infoPlist = orLog("Loading information from xctoolchain", { try XCToolchainPlist(fromDirectory: path) }) { diff --git a/Tests/ToolchainRegistryTests/ToolchainRegistryTests.swift b/Tests/ToolchainRegistryTests/ToolchainRegistryTests.swift index 929add581..f834e605e 100644 --- a/Tests/ToolchainRegistryTests/ToolchainRegistryTests.swift +++ b/Tests/ToolchainRegistryTests/ToolchainRegistryTests.swift @@ -336,7 +336,7 @@ final class ToolchainRegistryTests: XCTestCase { try ProcessEnv.setVar( "SOURCEKIT_PATH_FOR_TEST", - value: ["/bogus", binPath.filePath, "/bogus2"].joined(separator: separator) + value: ["/bogus/../parent", "/bogus", binPath.filePath, "/bogus2"].joined(separator: separator) ) defer { try! ProcessEnv.setVar("SOURCEKIT_PATH_FOR_TEST", value: "") }