Skip to content

Commit 0220fc3

Browse files
authored
TSCBasic: correct resolvingSymlinks on Windows (#347)
The function is not meant to resolve a symbolic link in the terminal arc but rather anywhere along the path. More specifically, it is meant to behave similar to `realpath` on Unix. The moral equivalent operation on Windows is `GetFinalPathNameByHandle` which takes a handle to the file and retrieves the actual location resolving any junctions in the path. This repairs the behaviour of the function which is important for SourceKit-LSP's test suite.
1 parent 0b77e67 commit 0220fc3

File tree

1 file changed

+11
-6
lines changed

1 file changed

+11
-6
lines changed

Sources/TSCBasic/PathShims.swift

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,18 @@ import Foundation
2323
/// Returns the "real path" corresponding to `path` by resolving any symbolic links.
2424
public func resolveSymlinks(_ path: AbsolutePath) -> AbsolutePath {
2525
#if os(Windows)
26-
var resolved: URL = URL(fileURLWithPath: path.pathString)
27-
if let destination = try? FileManager.default.destinationOfSymbolicLink(atPath: path.pathString) {
28-
resolved = URL(fileURLWithPath: destination, relativeTo: URL(fileURLWithPath: path.pathString))
26+
let handle: HANDLE = path.pathString.withCString(encodedAs: UTF16.self) {
27+
CreateFileW($0, GENERIC_READ, DWORD(FILE_SHARE_READ), nil,
28+
DWORD(OPEN_EXISTING), DWORD(FILE_FLAG_BACKUP_SEMANTICS), nil)
2929
}
30-
31-
return resolved.standardized.withUnsafeFileSystemRepresentation {
32-
try! AbsolutePath(validating: String(cString: $0!))
30+
if handle == INVALID_HANDLE_VALUE { return path }
31+
defer { CloseHandle(handle) }
32+
return withUnsafeTemporaryAllocation(of: WCHAR.self, capacity: 261) {
33+
let dwLength: DWORD =
34+
GetFinalPathNameByHandleW(handle, $0.baseAddress!, DWORD($0.count),
35+
DWORD(FILE_NAME_NORMALIZED))
36+
let path = String(decodingCString: $0.baseAddress!, as: UTF16.self)
37+
return try! AbsolutePath(validating: path)
3338
}
3439
#else
3540
let pathStr = path.pathString

0 commit comments

Comments
 (0)