Skip to content

Commit

Permalink
Use withUnsafeFileSystemRepresentation to get the path of a URL on …
Browse files Browse the repository at this point in the history
…disk

`URL.path` returns forward slashes in the path on Windows (swiftlang/swift-foundation#973) where we expect backslashes. Work around that by defining our own `filePath` property that is backed by `withUnsafeFileSystemRepresentation`, which produces backslashes.

rdar://137963660
  • Loading branch information
ahoppen committed Oct 21, 2024
1 parent c71471a commit bba2a18
Show file tree
Hide file tree
Showing 40 changed files with 251 additions and 157 deletions.
6 changes: 3 additions & 3 deletions Sources/BuildSystemIntegration/BuildSystemManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -587,7 +587,7 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
logger.error("Toolchain is not a file URL")
return nil
}
return try AbsolutePath(validating: toolchainUrl.path)
return try AbsolutePath(validating: toolchainUrl.filePath)
}
if let toolchainPath {
if let toolchain = await self.toolchainRegistry.toolchain(withPath: toolchainPath) {
Expand Down Expand Up @@ -645,10 +645,10 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
result.formUnion(targets)
}
if !filesAndDirectories.directories.isEmpty,
let documentPath = AbsolutePath(validatingOrNil: document.fileURL?.path)
let documentPath = AbsolutePath(validatingOrNil: try? document.fileURL?.filePath)
{
for (directory, info) in filesAndDirectories.directories {
guard let directoryPath = AbsolutePath(validatingOrNil: directory.fileURL?.path) else {
guard let directoryPath = AbsolutePath(validatingOrNil: try? directory.fileURL?.filePath) else {
continue
}
if documentPath.isDescendant(of: directoryPath) {
Expand Down
2 changes: 1 addition & 1 deletion Sources/BuildSystemIntegration/CompilationDatabase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ package struct JSONCompilationDatabase: CompilationDatabase, Equatable, Codable
if let indices = pathToCommands[uri] {
return indices.map { commands[$0] }
}
if let fileURL = uri.fileURL, let indices = pathToCommands[DocumentURI(fileURL.realpath)] {
if let fileURL = try? uri.fileURL?.realpath, let indices = pathToCommands[DocumentURI(fileURL)] {
return indices.map { commands[$0] }
}
return []
Expand Down
6 changes: 4 additions & 2 deletions Sources/BuildSystemIntegration/DetermineBuildSystem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@
package import LanguageServerProtocol
import SKLogging
package import SKOptions
import SwiftExtensions
import ToolchainRegistry

import struct TSCBasic.AbsolutePath
#else
import LanguageServerProtocol
import SKLogging
import SKOptions
import SwiftExtensions
import ToolchainRegistry

import struct TSCBasic.AbsolutePath
Expand All @@ -42,7 +44,7 @@ package func determineBuildSystem(
buildSystemPreference.insert(defaultBuildSystem, at: 0)
}
guard let workspaceFolderUrl = workspaceFolder.fileURL,
let workspaceFolderPath = try? AbsolutePath(validating: workspaceFolderUrl.path)
let workspaceFolderPath = try? AbsolutePath(validating: workspaceFolderUrl.filePath)
else {
return nil
}
Expand All @@ -58,7 +60,7 @@ package func determineBuildSystem(
}
case .swiftPM:
if let projectRootURL = SwiftPMBuildSystem.projectRoot(for: workspaceFolderUrl, options: options),
let projectRoot = try? AbsolutePath(validating: projectRootURL.path)
let projectRoot = try? AbsolutePath(validating: projectRootURL.filePath)
{
return .swiftPM(projectRoot: projectRoot)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import LanguageServerProtocol
import LanguageServerProtocolJSONRPC
import SKLogging
import SKOptions
import SwiftExtensions

import struct TSCBasic.AbsolutePath
import func TSCBasic.getEnvSearchPaths
Expand Down Expand Up @@ -224,7 +225,7 @@ actor ExternalBuildSystemAdapter {
.filter { $0.pathExtension == "json" }

if let configFileURL = jsonFiles?.sorted(by: { $0.lastPathComponent < $1.lastPathComponent }).first,
let configFilePath = AbsolutePath(validatingOrNil: configFileURL.path)
let configFilePath = AbsolutePath(validatingOrNil: try? configFileURL.filePath)
{
return configFilePath
}
Expand Down
12 changes: 7 additions & 5 deletions Sources/BuildSystemIntegration/SwiftPMBuildSystem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -238,14 +238,16 @@ package actor SwiftPMBuildSystem: BuiltInBuildSystem {
private var targetDependencies: [BuildTargetIdentifier: Set<BuildTargetIdentifier>] = [:]

static package func projectRoot(for path: URL, options: SourceKitLSPOptions) -> URL? {
var path = path.realpath
guard var path = orLog("Getting realpath for project root", { try path.realpath }) else {
return nil
}
while true {
let packagePath = path.appending(component: "Package.swift")
if (try? String(contentsOf: packagePath, encoding: .utf8))?.contains("PackageDescription") ?? false {
return path
}

if (try? AbsolutePath(validating: path.path))?.isRoot ?? true {
if (try? AbsolutePath(validating: path.filePath))?.isRoot ?? true {
break
}
path.deleteLastPathComponent()
Expand Down Expand Up @@ -572,7 +574,7 @@ package actor SwiftPMBuildSystem: BuiltInBuildSystem {
package func sourceKitOptions(
request: TextDocumentSourceKitOptionsRequest
) async throws -> TextDocumentSourceKitOptionsResponse? {
guard let url = request.textDocument.uri.fileURL, let path = try? AbsolutePath(validating: url.path) else {
guard let url = request.textDocument.uri.fileURL, let path = try? AbsolutePath(validating: url.filePath) else {
// We can't determine build settings for non-file URIs.
return nil
}
Expand All @@ -587,7 +589,7 @@ package actor SwiftPMBuildSystem: BuiltInBuildSystem {
}

if !swiftPMTarget.sources.lazy.map(DocumentURI.init).contains(request.textDocument.uri),
let substituteFile = swiftPMTarget.sources.sorted(by: { $0.path < $1.path }).first
let substituteFile = swiftPMTarget.sources.sorted(by: { $0.description < $1.description }).first
{
logger.info("Getting compiler arguments for \(url) using substitute file \(substituteFile)")
// If `url` is not part of the target's source, it's most likely a header file. Fake compiler arguments for it
Expand All @@ -600,7 +602,7 @@ package actor SwiftPMBuildSystem: BuiltInBuildSystem {
let buildSettings = FileBuildSettings(
compilerArguments: try await compilerArguments(for: DocumentURI(substituteFile), in: swiftPMTarget),
workingDirectory: projectRoot.pathString
).patching(newFile: DocumentURI(path.asURL.realpath), originalFile: DocumentURI(substituteFile))
).patching(newFile: DocumentURI(try path.asURL.realpath), originalFile: DocumentURI(substituteFile))
return TextDocumentSourceKitOptionsResponse(
compilerArguments: buildSettings.compilerArguments,
workingDirectory: buildSettings.workingDirectory
Expand Down
16 changes: 9 additions & 7 deletions Sources/Diagnose/DiagnoseCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
package import ArgumentParser
import Foundation
import ToolchainRegistry
import SwiftExtensions

import struct TSCBasic.AbsolutePath
import class TSCBasic.Process
Expand All @@ -22,6 +23,7 @@ import class TSCUtility.PercentProgressAnimation
import ArgumentParser
import Foundation
import ToolchainRegistry
import SwiftExtensions

import struct TSCBasic.AbsolutePath
import class TSCBasic.Process
Expand Down Expand Up @@ -172,7 +174,7 @@ package struct DiagnoseCommand: AsyncParsableCommand {
.deletingLastPathComponent()
.deletingLastPathComponent()

guard let toolchain = try Toolchain(AbsolutePath(validating: toolchainPath.path)),
guard let toolchain = try Toolchain(AbsolutePath(validating: toolchainPath.filePath)),
let sourcekitd = toolchain.sourcekitd
else {
continue
Expand Down Expand Up @@ -223,7 +225,7 @@ package struct DiagnoseCommand: AsyncParsableCommand {
#if os(macOS)
reportProgress(.collectingLogMessages(progress: 0), message: "Collecting log messages")
let outputFileUrl = bundlePath.appendingPathComponent("log.txt")
guard FileManager.default.createFile(atPath: outputFileUrl.path, contents: nil) else {
guard FileManager.default.createFile(atPath: try outputFileUrl.filePath, contents: nil) else {
throw GenericError("Failed to create log.txt")
}
let fileHandle = try FileHandle(forWritingTo: outputFileUrl)
Expand Down Expand Up @@ -316,7 +318,7 @@ package struct DiagnoseCommand: AsyncParsableCommand {
@MainActor
private func addSwiftVersion(toBundle bundlePath: URL) async throws {
let outputFileUrl = bundlePath.appendingPathComponent("swift-versions.txt")
guard FileManager.default.createFile(atPath: outputFileUrl.path, contents: nil) else {
guard FileManager.default.createFile(atPath: try outputFileUrl.filePath, contents: nil) else {
throw GenericError("Failed to create file at \(outputFileUrl)")
}
let fileHandle = try FileHandle(forWritingTo: outputFileUrl)
Expand All @@ -333,9 +335,9 @@ package struct DiagnoseCommand: AsyncParsableCommand {
continue
}

try fileHandle.write(contentsOf: "\(swiftUrl.path) --version\n".data(using: .utf8)!)
try fileHandle.write(contentsOf: "\(swiftUrl.filePath) --version\n".data(using: .utf8)!)
let process = Process(
arguments: [swiftUrl.path, "--version"],
arguments: [try swiftUrl.filePath, "--version"],
outputRedirection: .stream(
stdout: { try? fileHandle.write(contentsOf: $0) },
stderr: { _ in }
Expand Down Expand Up @@ -417,7 +419,7 @@ package struct DiagnoseCommand: AsyncParsableCommand {
Bundle created.
When filing an issue at https://github.com/swiftlang/sourcekit-lsp/issues/new,
please attach the bundle located at
\(bundlePath.path)
\(try bundlePath.filePath)
"""
)

Expand All @@ -428,7 +430,7 @@ package struct DiagnoseCommand: AsyncParsableCommand {
// is responsible for showing the diagnose bundle location to the user
if self.bundleOutputPath == nil {
do {
_ = try await Process.run(arguments: ["open", "-R", bundlePath.path], workingDirectory: nil)
_ = try await Process.run(arguments: ["open", "-R", bundlePath.filePath], workingDirectory: nil)
} catch {
// If revealing the bundle in Finder should fail, we don't care. We still printed the bundle path to stdout.
}
Expand Down
5 changes: 3 additions & 2 deletions Sources/Diagnose/ReproducerBundle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
//===----------------------------------------------------------------------===//

import Foundation
import SwiftExtensions
import ToolchainRegistry

/// Create a folder that contains all files that should be necessary to reproduce a sourcekitd crash.
Expand All @@ -26,7 +27,7 @@ func makeReproducerBundle(for requestInfo: RequestInfo, toolchain: Toolchain, bu
encoding: .utf8
)
if let toolchainPath = toolchain.path {
try toolchainPath.asURL.realpath.path
try toolchainPath.asURL.realpath.filePath
.write(
to: bundlePath.appendingPathComponent("toolchain.txt"),
atomically: true,
Expand Down Expand Up @@ -59,7 +60,7 @@ func makeReproducerBundle(for requestInfo: RequestInfo, toolchain: Toolchain, bu
// aren't user specific and would bloat the reproducer bundle.
continue
}
let dest = URL(fileURLWithPath: bundlePath.path + path)
let dest = URL(fileURLWithPath: try bundlePath.filePath + path)
try? FileManager.default.createDirectory(at: dest.deletingLastPathComponent(), withIntermediateDirectories: true)
try? FileManager.default.copyItem(at: URL(fileURLWithPath: String(path)), to: dest)
}
Expand Down
4 changes: 3 additions & 1 deletion Sources/Diagnose/RequestInfo.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@
#if compiler(>=6)
package import Foundation
import RegexBuilder
import SwiftExtensions
#else
import Foundation
import RegexBuilder
import SwiftExtensions
#endif

/// All the information necessary to replay a sourcektid request.
Expand Down Expand Up @@ -48,7 +50,7 @@ package struct RequestInfo: Sendable {
requestTemplate
.replacingOccurrences(of: "$OFFSET", with: String(offset))
.replacingOccurrences(of: "$COMPILER_ARGS", with: compilerArgs)
.replacingOccurrences(of: "$FILE", with: file.path)
.replacingOccurrences(of: "$FILE", with: try file.filePath)
}

/// A fake value that is used to indicate that we are reducing a `swift-frontend` issue instead of a sourcekitd issue.
Expand Down
10 changes: 6 additions & 4 deletions Sources/Diagnose/SourceKitDRequestExecutor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@
#if compiler(>=6)
package import Foundation
import SourceKitD
import SwiftExtensions

import struct TSCBasic.AbsolutePath
import class TSCBasic.Process
import struct TSCBasic.ProcessResult
#else
import Foundation
import SourceKitD
import SwiftExtensions

import struct TSCBasic.AbsolutePath
import class TSCBasic.Process
Expand Down Expand Up @@ -147,9 +149,9 @@ package class OutOfProcessSourceKitRequestExecutor: SourceKitRequestExecutor {
package func runSwiftFrontend(request: RequestInfo) async throws -> SourceKitDRequestResult {
try request.fileContents.write(to: temporarySourceFile, atomically: true, encoding: .utf8)

let arguments = request.compilerArgs.replacing(["$FILE"], with: [temporarySourceFile.path])
let arguments = request.compilerArgs.replacing(["$FILE"], with: [try temporarySourceFile.filePath])

let process = Process(arguments: [swiftFrontend.path] + arguments)
let process = Process(arguments: [try swiftFrontend.filePath] + arguments)
try process.launch()
let result = try await process.waitUntilExit()

Expand All @@ -167,9 +169,9 @@ package class OutOfProcessSourceKitRequestExecutor: SourceKitRequestExecutor {
"debug",
"run-sourcekitd-request",
"--sourcekitd",
sourcekitd.path,
try sourcekitd.filePath,
"--request-file",
temporaryRequestFile.path,
try temporaryRequestFile.filePath,
]
)
try process.launch()
Expand Down
2 changes: 1 addition & 1 deletion Sources/InProcessClient/InProcessSourceKitLSPClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public final class InProcessSourceKitLSPClient: Sendable {
let serverToClientConnection = LocalConnection(receiverName: "client")
self.server = SourceKitLSPServer(
client: serverToClientConnection,
toolchainRegistry: ToolchainRegistry(installPath: AbsolutePath(validatingOrNil: toolchainPath?.path)),
toolchainRegistry: ToolchainRegistry(installPath: AbsolutePath(validatingOrNil: toolchainPath?.filePath)),
options: options,
testHooks: TestHooks(),
onExit: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ public final class JSONRPCConnection: Connection {
}
let process = Foundation.Process()
logger.log(
"Launching build server at \(executable.path) with options [\(arguments.joined(separator: " "))]"
"Launching build server at \(executable.description) with options [\(arguments.joined(separator: " "))]"
)
process.executableURL = executable
process.arguments = arguments
Expand Down
8 changes: 4 additions & 4 deletions Sources/SKLogging/SetGlobalLogFileHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ func getOrCreateLogFileHandle(logDirectory: URL, logRotateCount: Int) -> FileHan

do {
try FileManager.default.createDirectory(at: logDirectory, withIntermediateDirectories: true)
if !FileManager.default.fileExists(atPath: logFileUrl.path) {
guard FileManager.default.createFile(atPath: logFileUrl.path, contents: nil) else {
if !FileManager.default.fileExists(at: logFileUrl) {
guard FileManager.default.createFile(atPath: try logFileUrl.filePath, contents: nil) else {
throw FailedToCreateFileError(logFile: logFileUrl)
}
}
Expand All @@ -79,7 +79,7 @@ func getOrCreateLogFileHandle(logDirectory: URL, logRotateCount: Int) -> FileHan
// We will try creating a log file again once this section of the log reaches `maxLogFileSize` but that means that
// we'll only log this error every `maxLogFileSize` bytes, which is a lot less spammy than logging it on every log
// call.
fputs("Failed to open file handle for log file at \(logFileUrl.path): \(error)", stderr)
fputs("Failed to open file handle for log file at \(logFileUrl): \(error)", stderr)
logFileHandle = FileHandle.standardError
return FileHandle.standardError
}
Expand Down Expand Up @@ -176,7 +176,7 @@ private func cleanOldLogFilesImpl(logFileDirectory: URL, maxAge: TimeInterval) {
guard
let modificationDate = orLog(
"Getting mtime of old log file",
{ try FileManager.default.attributesOfItem(atPath: url.path)[.modificationDate] }
{ try FileManager.default.attributesOfItem(atPath: url.filePath)[.modificationDate] }
) as? Date,
Date().timeIntervalSince(modificationDate) > maxAge
else {
Expand Down
4 changes: 3 additions & 1 deletion Sources/SKSupport/DocumentURI+symlinkTarget.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ extension DocumentURI {
guard let fileUrl = fileURL else {
return nil
}
let realpath = DocumentURI(fileUrl.realpath)
guard let realpath = try? DocumentURI(fileUrl.realpath) else {
return nil
}
if realpath == self {
return nil
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/SKSupport/FileSystem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ extension AbsolutePath {
package init(expandingTilde path: String) throws {
if path.first == "~" {
try self.init(
AbsolutePath(validating: FileManager.default.homeDirectoryForCurrentUser.path),
AbsolutePath(validating: FileManager.default.homeDirectoryForCurrentUser.filePath),
validating: String(path.dropFirst(2))
)
} else {
Expand Down
8 changes: 4 additions & 4 deletions Sources/SKTestSupport/BuildServerTestProject.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ private let skTestSupportInputsDirectory: URL = {
.appendingPathComponent("SourceKitLSP_SKTestSupport.bundle")
.appendingPathComponent("Contents")
.appendingPathComponent("Resources")
if !FileManager.default.fileExists(atPath: resources.path) {
if !FileManager.default.fileExists(at: resources) {
// Xcode and command-line swiftpm differ about the path.
resources.deleteLastPathComponent()
resources.deleteLastPathComponent()
Expand All @@ -40,8 +40,8 @@ private let skTestSupportInputsDirectory: URL = {
productsDirectory
.appendingPathComponent("SourceKitLSP_SKTestSupport.resources")
#endif
guard FileManager.default.fileExists(atPath: resources.path) else {
fatalError("missing resources \(resources.path)")
guard FileManager.default.fileExists(at: resources) else {
fatalError("missing resources \(resources)")
}
return resources.appendingPathComponent("INPUTS", isDirectory: true).standardizedFileURL
}()
Expand Down Expand Up @@ -73,7 +73,7 @@ package class BuildServerTestProject: MultiFileTestProject {
import sys
from typing import Dict, List, Optional
sys.path.append("\(skTestSupportInputsDirectory.path)")
sys.path.append("\(try skTestSupportInputsDirectory.filePath)")
from AbstractBuildServer import AbstractBuildServer, LegacyBuildServer
Expand Down
Loading

0 comments on commit bba2a18

Please sign in to comment.