Skip to content

Commit 17b13a9

Browse files
authored
Merge pull request swiftlang#1751 from ahoppen/windows-fixes
2 parents 5802aad + c16e33d commit 17b13a9

26 files changed

+265
-147
lines changed

Sources/BuildSystemIntegration/BuildSystemManager.swift

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ package import ToolchainRegistry
2323

2424
package import struct TSCBasic.AbsolutePath
2525
package import struct TSCBasic.RelativePath
26-
package import func TSCBasic.resolveSymlinks
2726
#else
2827
import BuildServerProtocol
2928
import Dispatch
@@ -37,7 +36,6 @@ import ToolchainRegistry
3736

3837
import struct TSCBasic.AbsolutePath
3938
import struct TSCBasic.RelativePath
40-
import func TSCBasic.resolveSymlinks
4139
#endif
4240

4341
fileprivate typealias RequestCache<Request: RequestType & Hashable> = Cache<Request, Request.Response>
@@ -96,18 +94,6 @@ fileprivate extension BuildTarget {
9694
}
9795
}
9896

99-
fileprivate extension DocumentURI {
100-
/// If this is a file URI pointing to a symlink, return the realpath of the URI, otherwise return `nil`.
101-
var symlinkTarget: DocumentURI? {
102-
guard let fileUrl = fileURL, let path = AbsolutePath(validatingOrNil: fileUrl.path),
103-
let symlinksResolved = try? TSCBasic.resolveSymlinks(path), symlinksResolved != path
104-
else {
105-
return nil
106-
}
107-
return DocumentURI(symlinksResolved.asURL)
108-
}
109-
}
110-
11197
fileprivate extension InitializeBuildResponse {
11298
var sourceKitData: SourceKitInitializeBuildResponseData? {
11399
guard dataKind == nil || dataKind == .sourceKit else {

Sources/BuildSystemIntegration/CompilationDatabase.swift

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ package import struct TSCBasic.AbsolutePath
2121
package import protocol TSCBasic.FileSystem
2222
package import struct TSCBasic.RelativePath
2323
package import var TSCBasic.localFileSystem
24-
package import func TSCBasic.resolveSymlinks
2524
#else
2625
import BuildServerProtocol
2726
import Foundation
@@ -33,7 +32,10 @@ import struct TSCBasic.AbsolutePath
3332
import protocol TSCBasic.FileSystem
3433
import struct TSCBasic.RelativePath
3534
import var TSCBasic.localFileSystem
36-
import func TSCBasic.resolveSymlinks
35+
#endif
36+
37+
#if os(Windows)
38+
import WinSDK
3739
#endif
3840

3941
/// A single compilation database command.
@@ -99,7 +101,7 @@ package struct CompilationDatabaseCompileCommand: Equatable, Codable {
99101
/// it falls back to `filename`, which is more likely to be the identifier
100102
/// that a caller will be looking for.
101103
package var uri: DocumentURI {
102-
if filename.hasPrefix("/") || !directory.hasPrefix("/") {
104+
if filename.isAbsolutePath || !directory.isAbsolutePath {
103105
return DocumentURI(filePath: filename, isDirectory: false)
104106
} else {
105107
return DocumentURI(URL(fileURLWithPath: directory).appendingPathComponent(filename, isDirectory: false))
@@ -262,7 +264,7 @@ package struct JSONCompilationDatabase: CompilationDatabase, Equatable, Codable
262264
if let indices = pathToCommands[uri] {
263265
return indices.map { commands[$0] }
264266
}
265-
if let fileURL = uri.fileURL, let indices = pathToCommands[DocumentURI(fileURL.resolvingSymlinksInPath())] {
267+
if let fileURL = uri.fileURL, let indices = pathToCommands[DocumentURI(fileURL.realpath)] {
266268
return indices.map { commands[$0] }
267269
}
268270
return []
@@ -278,13 +280,8 @@ package struct JSONCompilationDatabase: CompilationDatabase, Equatable, Codable
278280
let uri = command.uri
279281
pathToCommands[uri, default: []].append(commands.count)
280282

281-
if let fileURL = uri.fileURL,
282-
let symlinksResolved = try? resolveSymlinks(AbsolutePath(validating: fileURL.path))
283-
{
284-
let canonical = DocumentURI(filePath: symlinksResolved.pathString, isDirectory: false)
285-
if canonical != uri {
286-
pathToCommands[canonical, default: []].append(commands.count)
287-
}
283+
if let symlinkTarget = uri.symlinkTarget {
284+
pathToCommands[symlinkTarget, default: []].append(commands.count)
288285
}
289286

290287
commands.append(command)
@@ -295,3 +292,15 @@ enum CompilationDatabaseDecodingError: Error {
295292
case missingCommandOrArguments
296293
case fixedDatabaseDecodingError
297294
}
295+
296+
fileprivate extension String {
297+
var isAbsolutePath: Bool {
298+
#if os(Windows)
299+
Array(self.utf16).withUnsafeBufferPointer { buffer in
300+
return !PathIsRelativeW(buffer.baseAddress)
301+
}
302+
#else
303+
return self.hasPrefix("/")
304+
#endif
305+
}
306+
}

Sources/BuildSystemIntegration/DetermineBuildSystem.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,9 @@ package func determineBuildSystem(
5757
return .compilationDatabase(projectRoot: projectRoot)
5858
}
5959
case .swiftPM:
60-
if let projectRoot = SwiftPMBuildSystem.projectRoot(for: workspaceFolderPath, options: options) {
60+
if let projectRootURL = SwiftPMBuildSystem.projectRoot(for: workspaceFolderUrl, options: options),
61+
let projectRoot = try? AbsolutePath(validating: projectRootURL.path)
62+
{
6163
return .swiftPM(projectRoot: projectRoot)
6264
}
6365
}

Sources/BuildSystemIntegration/LegacyBuildServerBuildSystem.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ import struct TSCBasic.FileSystemError
2626
import func TSCBasic.getEnvSearchPaths
2727
import var TSCBasic.localFileSystem
2828
import func TSCBasic.lookupExecutablePath
29-
import func TSCBasic.resolveSymlinks
3029

3130
#if compiler(>=6.3)
3231
#warning("We have had a one year transition period to the pull based build server. Consider removing this build server")

Sources/BuildSystemIntegration/SwiftPMBuildSystem.swift

Lines changed: 13 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ package import Basics
1515
@preconcurrency import Build
1616
package import BuildServerProtocol
1717
import Dispatch
18-
import Foundation
18+
package import Foundation
1919
package import LanguageServerProtocol
2020
@preconcurrency import PackageGraph
2121
import PackageLoading
@@ -33,12 +33,10 @@ package import ToolchainRegistry
3333
package import struct Basics.AbsolutePath
3434
package import struct Basics.IdentifiableSet
3535
package import struct Basics.TSCAbsolutePath
36-
import struct Foundation.URL
37-
package import struct TSCBasic.AbsolutePath
38-
package import protocol TSCBasic.FileSystem
39-
package import class TSCBasic.Process
40-
package import var TSCBasic.localFileSystem
41-
package import func TSCBasic.resolveSymlinks
36+
import struct TSCBasic.AbsolutePath
37+
import protocol TSCBasic.FileSystem
38+
import class TSCBasic.Process
39+
import var TSCBasic.localFileSystem
4240
package import class ToolchainRegistry.Toolchain
4341
#else
4442
import Basics
@@ -68,7 +66,6 @@ import struct TSCBasic.AbsolutePath
6866
import protocol TSCBasic.FileSystem
6967
import class TSCBasic.Process
7068
import var TSCBasic.localFileSystem
71-
import func TSCBasic.resolveSymlinks
7269
import class ToolchainRegistry.Toolchain
7370
#endif
7471

@@ -240,26 +237,18 @@ package actor SwiftPMBuildSystem: BuiltInBuildSystem {
240237

241238
private var targetDependencies: [BuildTargetIdentifier: Set<BuildTargetIdentifier>] = [:]
242239

243-
static package func projectRoot(
244-
for path: TSCBasic.AbsolutePath,
245-
options: SourceKitLSPOptions
246-
) -> TSCBasic.AbsolutePath? {
247-
guard var path = try? resolveSymlinks(path) else {
248-
return nil
249-
}
240+
static package func projectRoot(for path: URL, options: SourceKitLSPOptions) -> URL? {
241+
var path = path.realpath
250242
while true {
251243
let packagePath = path.appending(component: "Package.swift")
252-
if localFileSystem.isFile(packagePath) {
253-
let contents = try? localFileSystem.readFileContents(packagePath)
254-
if contents?.cString.contains("PackageDescription") == true {
255-
return path
256-
}
244+
if (try? String(contentsOf: packagePath, encoding: .utf8))?.contains("PackageDescription") ?? false {
245+
return path
257246
}
258247

259-
if path.isRoot {
260-
return nil
248+
if (try? AbsolutePath(validating: path.path))?.isRoot ?? true {
249+
break
261250
}
262-
path = path.parentDirectory
251+
path.deleteLastPathComponent()
263252
}
264253
return nil
265254
}
@@ -611,7 +600,7 @@ package actor SwiftPMBuildSystem: BuiltInBuildSystem {
611600
let buildSettings = FileBuildSettings(
612601
compilerArguments: try await compilerArguments(for: DocumentURI(substituteFile), in: swiftPMTarget),
613602
workingDirectory: projectRoot.pathString
614-
).patching(newFile: DocumentURI(try resolveSymlinks(path).asURL), originalFile: DocumentURI(substituteFile))
603+
).patching(newFile: DocumentURI(path.asURL.realpath), originalFile: DocumentURI(substituteFile))
615604
return TextDocumentSourceKitOptionsResponse(
616605
compilerArguments: buildSettings.compilerArguments,
617606
workingDirectory: buildSettings.workingDirectory

Sources/Diagnose/ReproducerBundle.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ func makeReproducerBundle(for requestInfo: RequestInfo, toolchain: Toolchain, bu
2626
encoding: .utf8
2727
)
2828
if let toolchainPath = toolchain.path {
29-
try toolchainPath.asURL.resolvingSymlinksInPath().path
29+
try toolchainPath.asURL.realpath.path
3030
.write(
3131
to: bundlePath.appendingPathComponent("toolchain.txt"),
3232
atomically: true,

Sources/SKSupport/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ add_library(SKSupport STATIC
66
Debouncer.swift
77
Dictionary+InitWithElementsKeyedBy.swift
88
DocumentURI+CustomLogStringConvertible.swift
9+
DocumentURI+symlinkTarget.swift
910
FileSystem.swift
1011
LineTable.swift
1112
LocalConnection.swift
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#if compiler(>=6)
14+
import Foundation
15+
package import LanguageServerProtocol
16+
import struct TSCBasic.AbsolutePath
17+
#else
18+
import Foundation
19+
import LanguageServerProtocol
20+
21+
import struct TSCBasic.RelativePath
22+
#endif
23+
24+
extension DocumentURI {
25+
/// If this is a file URI pointing to a symlink, return the realpath of the URI, otherwise return `nil`.
26+
package var symlinkTarget: DocumentURI? {
27+
guard let fileUrl = fileURL else {
28+
return nil
29+
}
30+
let realpath = DocumentURI(fileUrl.realpath)
31+
if realpath == self {
32+
return nil
33+
}
34+
return realpath
35+
}
36+
}

Sources/SKTestSupport/BuildServerTestProject.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import XCTest
1717
fileprivate let sdkArgs =
1818
if let defaultSDKPath {
1919
"""
20-
"-sdk", "\(defaultSDKPath)",
20+
"-sdk", "\(defaultSDKPath.replacing(#"\"#, with: #"\\"#))",
2121
"""
2222
} else {
2323
""

Sources/SKTestSupport/FindTool.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,14 @@ package func findTool(name: String) async -> URL? {
4848
return nil
4949
}
5050
#if os(Windows)
51-
path = String((path.split { $0.isNewline })[0])
51+
// where.exe returns all files that match the name. We only care about the first one.
52+
if let newlineIndex = path.firstIndex(where: \.isNewline) {
53+
path = String(path[..<newlineIndex])
54+
}
5255
#endif
5356
path = path.trimmingCharacters(in: .whitespacesAndNewlines)
57+
if path.isEmpty {
58+
return nil
59+
}
5460
return URL(fileURLWithPath: path, isDirectory: false)
5561
}

Sources/SKTestSupport/IndexedSingleSwiftFileTestProject.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,19 @@ package struct IndexedSingleSwiftFileTestProject {
8787

8888
// The following are needed so we can import XCTest
8989
let sdkUrl = URL(fileURLWithPath: sdk)
90+
#if os(Windows)
91+
let xctestModuleDir =
92+
sdkUrl
93+
.deletingLastPathComponent()
94+
.deletingLastPathComponent()
95+
.appendingPathComponent("Library")
96+
.appendingPathComponent("XCTest-development")
97+
.appendingPathComponent("usr")
98+
.appendingPathComponent("lib")
99+
.appendingPathComponent("swift")
100+
.appendingPathComponent("windows")
101+
compilerArguments += ["-I", xctestModuleDir.path]
102+
#else
90103
let usrLibDir =
91104
sdkUrl
92105
.deletingLastPathComponent()
@@ -103,6 +116,7 @@ package struct IndexedSingleSwiftFileTestProject {
103116
"-I", usrLibDir.path,
104117
"-F", frameworksDir.path,
105118
]
119+
#endif
106120
}
107121

108122
let compilationDatabase = JSONCompilationDatabase(

Sources/SKTestSupport/MultiFileTestProject.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,10 @@ package class MultiFileTestProject {
102102

103103
var fileData: [String: FileData] = [:]
104104
for (fileLocation, markedText) in files {
105-
let markedText = markedText.replacingOccurrences(of: "$TEST_DIR", with: scratchDirectory.path)
105+
let markedText =
106+
markedText
107+
.replacingOccurrences(of: "$TEST_DIR_URL", with: scratchDirectory.absoluteString)
108+
.replacingOccurrences(of: "$TEST_DIR", with: scratchDirectory.path)
106109
let fileURL = fileLocation.url(relativeTo: scratchDirectory)
107110
try FileManager.default.createDirectory(
108111
at: fileURL.deletingLastPathComponent(),

Sources/SKTestSupport/Utils.swift

Lines changed: 11 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@
1313
#if compiler(>=6)
1414
package import Foundation
1515
package import LanguageServerProtocol
16+
import SwiftExtensions
1617
package import struct TSCBasic.AbsolutePath
1718
#else
1819
import Foundation
1920
import LanguageServerProtocol
21+
import SwiftExtensions
2022
import struct TSCBasic.AbsolutePath
2123
#endif
2224

@@ -66,10 +68,17 @@ package func testScratchDir(testName: String = #function) throws -> URL {
6668
if let firstDash = uuid.firstIndex(of: "-") {
6769
uuid = uuid[..<firstDash]
6870
}
69-
let url = FileManager.default.temporaryDirectory
70-
.realpath
71+
// Including the test name in the directory frequently makes path lengths of test files exceed the maximum path length
72+
// on Windows. Choose shorter directory names on that platform to avoid that issue.
73+
#if os(Windows)
74+
let url = FileManager.default.temporaryDirectory.realpath
75+
.appendingPathComponent("lsp-test")
76+
.appendingPathComponent("\(uuid)")
77+
#else
78+
let url = FileManager.default.temporaryDirectory.realpath
7179
.appendingPathComponent("sourcekit-lsp-test-scratch")
7280
.appendingPathComponent("\(testBaseName)-\(uuid)")
81+
#endif
7382
try? FileManager.default.removeItem(at: url)
7483
try FileManager.default.createDirectory(at: url, withIntermediateDirectories: true)
7584
return url
@@ -94,31 +103,6 @@ package func withTestScratchDir<T>(
94103
return try await body(try AbsolutePath(validating: scratchDirectory.path))
95104
}
96105

97-
fileprivate extension URL {
98-
/// Assuming this is a file URL, resolves all symlinks in the path.
99-
///
100-
/// - Note: We need this because `URL.resolvingSymlinksInPath()` not only resolves symlinks but also standardizes the
101-
/// path by stripping away `private` prefixes. Since sourcekitd is not performing this standardization, using
102-
/// `resolvingSymlinksInPath` can lead to slightly mismatched URLs between the sourcekit-lsp response and the test
103-
/// assertion.
104-
var realpath: URL {
105-
#if canImport(Darwin)
106-
return self.path.withCString { path in
107-
guard let realpath = Darwin.realpath(path, nil) else {
108-
return self
109-
}
110-
let result = URL(fileURLWithPath: String(cString: realpath))
111-
free(realpath)
112-
return result
113-
}
114-
#else
115-
// Non-Darwin platforms don't have the `/private` stripping issue, so we can just use `self.resolvingSymlinksInPath`
116-
// here.
117-
return self.resolvingSymlinksInPath()
118-
#endif
119-
}
120-
}
121-
122106
let globalModuleCache: URL? = {
123107
if let customModuleCache = ProcessInfo.processInfo.environment["SOURCEKIT_LSP_TEST_MODULE_CACHE"] {
124108
if customModuleCache.isEmpty {

Sources/SourceKitLSP/Clang/ClangLanguageService.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,7 @@ actor ClangLanguageService: LanguageService, MessageHandler {
326326
// safer, can also indefinitely hang as `CreateRemoteThread` may not be serviced depending on the state of
327327
// the process. This just attempts to terminate the process, risking a deadlock and resource leaks, which is fine
328328
// since we only use `crash` from tests.
329-
_ = TerminateProcess(self.hClangd, 0)
329+
_ = TerminateProcess(self.hClangd, 255)
330330
}
331331
#else
332332
if let pid = self.clangdPid {

0 commit comments

Comments
 (0)