diff --git a/Package.swift b/Package.swift index 15be17bef..2de888e77 100644 --- a/Package.swift +++ b/Package.swift @@ -31,6 +31,7 @@ let package = Package( "SKOptions", "SKSupport", "SourceKitLSP", + "SwiftExtensions", "ToolchainRegistry", .product(name: "ArgumentParser", package: "swift-argument-parser"), .product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"), diff --git a/Sources/Diagnose/DiagnoseCommand.swift b/Sources/Diagnose/DiagnoseCommand.swift index 10f2fa4bc..723633dfe 100644 --- a/Sources/Diagnose/DiagnoseCommand.swift +++ b/Sources/Diagnose/DiagnoseCommand.swift @@ -254,7 +254,9 @@ package struct DiagnoseCommand: AsyncParsableCommand { let destinationDir = bundlePath.appendingPathComponent("logs") try FileManager.default.createDirectory(at: destinationDir, withIntermediateDirectories: true) - let logFileDirectoryURL = URL(fileURLWithPath: ("~/.sourcekit-lsp/logs" as NSString).expandingTildeInPath) + let logFileDirectoryURL = FileManager.default.sanitizedHomeDirectoryForCurrentUser + .appendingPathComponent(".sourcekit-lsp") + .appendingPathComponent("logs") let enumerator = FileManager.default.enumerator(at: logFileDirectoryURL, includingPropertiesForKeys: nil) while let fileUrl = enumerator?.nextObject() as? URL { guard fileUrl.lastPathComponent.hasPrefix("sourcekit-lsp") else { diff --git a/Sources/SKSupport/FileSystem.swift b/Sources/SKSupport/FileSystem.swift index 9935b0e51..e9ebde9e4 100644 --- a/Sources/SKSupport/FileSystem.swift +++ b/Sources/SKSupport/FileSystem.swift @@ -11,20 +11,19 @@ //===----------------------------------------------------------------------===// import Foundation +import SwiftExtensions import struct TSCBasic.AbsolutePath -/// The home directory of the current user (same as returned by Foundation's `NSHomeDirectory` method). -package var homeDirectoryForCurrentUser: AbsolutePath { - try! AbsolutePath(validating: NSHomeDirectory()) -} - extension AbsolutePath { /// Inititializes an absolute path from a string, expanding a leading `~` to `homeDirectoryForCurrentUser` first. package init(expandingTilde path: String) throws { if path.first == "~" { - try self.init(homeDirectoryForCurrentUser, validating: String(path.dropFirst(2))) + try self.init( + AbsolutePath(validating: FileManager.default.sanitizedHomeDirectoryForCurrentUser.path), + validating: String(path.dropFirst(2)) + ) } else { try self.init(validating: path) } diff --git a/Sources/SwiftExtensions/CMakeLists.txt b/Sources/SwiftExtensions/CMakeLists.txt index 7b539d1e5..59992e5cc 100644 --- a/Sources/SwiftExtensions/CMakeLists.txt +++ b/Sources/SwiftExtensions/CMakeLists.txt @@ -5,6 +5,7 @@ add_library(SwiftExtensions STATIC AsyncUtils.swift Collection+Only.swift Collection+PartitionIntoBatches.swift + FileManager+SanitizedHomeDirectoryOfCurrentUser.swift NSLock+WithLock.swift PipeAsStringHandler.swift ResultExtensions.swift diff --git a/Sources/SwiftExtensions/FileManager+SanitizedHomeDirectoryOfCurrentUser.swift b/Sources/SwiftExtensions/FileManager+SanitizedHomeDirectoryOfCurrentUser.swift new file mode 100644 index 000000000..4c015d411 --- /dev/null +++ b/Sources/SwiftExtensions/FileManager+SanitizedHomeDirectoryOfCurrentUser.swift @@ -0,0 +1,28 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import Foundation + +extension FileManager { + /// Same as `homeDirectoryForCurrentUser` but works around + /// https://github.com/apple/swift-corelibs-foundation/issues/5041, which causes a null byte + package var sanitizedHomeDirectoryForCurrentUser: URL { + let homeDirectory = FileManager.default.homeDirectoryForCurrentUser + #if os(Windows) + if homeDirectory.lastPathComponent.hasSuffix("\0") { + let newLastPathComponent = String(homeDirectory.lastPathComponent.dropLast()) + return homeDirectory.deletingLastPathComponent().appendingPathComponent(newLastPathComponent) + } + #endif + return homeDirectory + } +} diff --git a/Sources/sourcekit-lsp/CMakeLists.txt b/Sources/sourcekit-lsp/CMakeLists.txt index 344104e8f..141e79dd6 100644 --- a/Sources/sourcekit-lsp/CMakeLists.txt +++ b/Sources/sourcekit-lsp/CMakeLists.txt @@ -10,6 +10,7 @@ target_link_libraries(sourcekit-lsp PRIVATE SKOptions SKSupport SourceKitLSP + SwiftExtensions ToolchainRegistry ArgumentParser TSCBasic) diff --git a/Sources/sourcekit-lsp/SourceKitLSP.swift b/Sources/sourcekit-lsp/SourceKitLSP.swift index 8ad28cbcc..538b22410 100644 --- a/Sources/sourcekit-lsp/SourceKitLSP.swift +++ b/Sources/sourcekit-lsp/SourceKitLSP.swift @@ -21,6 +21,7 @@ import SKLogging import SKOptions import SKSupport import SourceKitLSP +import SwiftExtensions import ToolchainRegistry import struct TSCBasic.AbsolutePath @@ -238,7 +239,9 @@ struct SourceKitLSP: AsyncParsableCommand { var options = SourceKitLSPOptions.merging( base: commandLineOptions(), override: SourceKitLSPOptions( - path: URL(fileURLWithPath: ("~/.sourcekit-lsp/config.json" as NSString).expandingTildeInPath) + path: FileManager.default.sanitizedHomeDirectoryForCurrentUser + .appendingPathComponent(".sourcekit-lsp") + .appendingPathComponent("config.json") ) ) #if canImport(Darwin) @@ -282,7 +285,9 @@ struct SourceKitLSP: AsyncParsableCommand { let realStdoutHandle = FileHandle(fileDescriptor: realStdout, closeOnDealloc: false) // Directory should match the directory we are searching for logs in `DiagnoseCommand.addNonDarwinLogs`. - let logFileDirectoryURL = URL(fileURLWithPath: ("~/.sourcekit-lsp/logs" as NSString).expandingTildeInPath) + let logFileDirectoryURL = FileManager.default.sanitizedHomeDirectoryForCurrentUser + .appendingPathComponent(".sourcekit-lsp") + .appendingPathComponent("logs") await setUpGlobalLogFileHandler( logFileDirectory: logFileDirectoryURL, logFileMaxBytes: 5_000_000,