Skip to content

Commit 7549b5d

Browse files
committed
Enforce use of throwing AbsolutePath initializer
In support of swiftlang/swift-package-manager#5797
1 parent 79f2aac commit 7549b5d

27 files changed

+395
-340
lines changed

Sources/TSCBasic/FileSystem.swift

Lines changed: 62 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -187,13 +187,13 @@ public protocol FileSystem: AnyObject {
187187
func changeCurrentWorkingDirectory(to path: AbsolutePath) throws
188188

189189
/// Get the home directory of current user
190-
var homeDirectory: AbsolutePath { get }
190+
var homeDirectory: AbsolutePath { get throws }
191191

192192
/// Get the caches directory of current user
193193
var cachesDirectory: AbsolutePath? { get }
194194

195195
/// Get the temp directory
196-
var tempDirectory: AbsolutePath { get }
196+
var tempDirectory: AbsolutePath { get throws }
197197

198198
/// Create the given directory.
199199
func createDirectory(_ path: AbsolutePath) throws
@@ -317,7 +317,9 @@ private class LocalFileSystem: FileSystem {
317317
}
318318

319319
func isFile(_ path: AbsolutePath) -> Bool {
320-
let path = resolveSymlinks(path)
320+
guard let path = try? resolveSymlinks(path) else {
321+
return false
322+
}
321323
let attrs = try? FileManager.default.attributesOfItem(atPath: path.pathString)
322324
return attrs?[.type] as? FileAttributeType == .typeRegular
323325
}
@@ -353,7 +355,7 @@ private class LocalFileSystem: FileSystem {
353355
let fsr: UnsafePointer<Int8> = cwdStr.fileSystemRepresentation
354356
defer { fsr.deallocate() }
355357

356-
return try? AbsolutePath(validating: String(cString: fsr))
358+
return try? AbsolutePath(String(cString: fsr))
357359
#endif
358360
}
359361

@@ -368,19 +370,23 @@ private class LocalFileSystem: FileSystem {
368370
}
369371

370372
var homeDirectory: AbsolutePath {
371-
return AbsolutePath(NSHomeDirectory())
373+
get throws {
374+
return try AbsolutePath(validating: NSHomeDirectory())
375+
}
372376
}
373377

374378
var cachesDirectory: AbsolutePath? {
375-
return FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first.flatMap { AbsolutePath($0.path) }
379+
return FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first.flatMap { try? AbsolutePath(validating: $0.path) }
376380
}
377381

378382
var tempDirectory: AbsolutePath {
379-
let override = ProcessEnv.vars["TMPDIR"] ?? ProcessEnv.vars["TEMP"] ?? ProcessEnv.vars["TMP"]
380-
if let path = override.flatMap({ try? AbsolutePath(validating: $0) }) {
381-
return path
383+
get throws {
384+
let override = ProcessEnv.vars["TMPDIR"] ?? ProcessEnv.vars["TEMP"] ?? ProcessEnv.vars["TMP"]
385+
if let path = override.flatMap({ try? AbsolutePath(validating: $0) }) {
386+
return path
387+
}
388+
return try AbsolutePath(validating: NSTemporaryDirectory())
382389
}
383-
return AbsolutePath(NSTemporaryDirectory())
384390
}
385391

386392
func getDirectoryContents(_ path: AbsolutePath) throws -> [String] {
@@ -664,7 +670,7 @@ public class InMemoryFileSystem: FileSystem {
664670
case .directory, .file:
665671
return node
666672
case .symlink(let destination):
667-
let destination = AbsolutePath(destination, relativeTo: path.parentDirectory)
673+
let destination = try AbsolutePath(validating: destination, relativeTo: path.parentDirectory)
668674
return followSymlink ? try getNodeInternal(destination) : node
669675
case .none:
670676
return nil
@@ -745,24 +751,28 @@ public class InMemoryFileSystem: FileSystem {
745751

746752
/// Virtualized current working directory.
747753
public var currentWorkingDirectory: AbsolutePath? {
748-
return AbsolutePath("/")
754+
return try? AbsolutePath(validating: "/")
749755
}
750756

751757
public func changeCurrentWorkingDirectory(to path: AbsolutePath) throws {
752758
throw FileSystemError(.unsupported, path)
753759
}
754760

755761
public var homeDirectory: AbsolutePath {
756-
// FIXME: Maybe we should allow setting this when creating the fs.
757-
return AbsolutePath("/home/user")
762+
get throws {
763+
// FIXME: Maybe we should allow setting this when creating the fs.
764+
return try AbsolutePath(validating: "/home/user")
765+
}
758766
}
759767

760768
public var cachesDirectory: AbsolutePath? {
761-
return self.homeDirectory.appending(component: "caches")
769+
return try? self.homeDirectory.appending(component: "caches")
762770
}
763771

764772
public var tempDirectory: AbsolutePath {
765-
return AbsolutePath("/tmp")
773+
get throws {
774+
return try AbsolutePath(validating: "/tmp")
775+
}
766776
}
767777

768778
public func getDirectoryContents(_ path: AbsolutePath) throws -> [String] {
@@ -978,7 +988,7 @@ public class InMemoryFileSystem: FileSystem {
978988
public func withLock<T>(on path: AbsolutePath, type: FileLock.LockType = .exclusive, _ body: () throws -> T) throws -> T {
979989
let resolvedPath: AbsolutePath = try lock.withLock {
980990
if case let .symlink(destination) = try getNode(path)?.contents {
981-
return AbsolutePath(destination, relativeTo: path.parentDirectory)
991+
return try AbsolutePath(validating: destination, relativeTo: path.parentDirectory)
982992
} else {
983993
return path
984994
}
@@ -1023,48 +1033,69 @@ public class RerootedFileSystemView: FileSystem {
10231033
}
10241034

10251035
/// Adjust the input path for the underlying file system.
1026-
private func formUnderlyingPath(_ path: AbsolutePath) -> AbsolutePath {
1036+
private func formUnderlyingPath(_ path: AbsolutePath) throws -> AbsolutePath {
10271037
if path == AbsolutePath.root {
10281038
return root
10291039
} else {
10301040
// FIXME: Optimize?
1031-
return AbsolutePath(String(path.pathString.dropFirst(1)), relativeTo: root)
1041+
return try AbsolutePath(validating: String(path.pathString.dropFirst(1)), relativeTo: root)
10321042
}
10331043
}
10341044

10351045
// MARK: FileSystem Implementation
10361046

10371047
public func exists(_ path: AbsolutePath, followSymlink: Bool) -> Bool {
1038-
return underlyingFileSystem.exists(formUnderlyingPath(path), followSymlink: followSymlink)
1048+
guard let underlying = try? formUnderlyingPath(path) else {
1049+
return false
1050+
}
1051+
return underlyingFileSystem.exists(underlying, followSymlink: followSymlink)
10391052
}
10401053

10411054
public func isDirectory(_ path: AbsolutePath) -> Bool {
1042-
return underlyingFileSystem.isDirectory(formUnderlyingPath(path))
1055+
guard let underlying = try? formUnderlyingPath(path) else {
1056+
return false
1057+
}
1058+
return underlyingFileSystem.isDirectory(underlying)
10431059
}
10441060

10451061
public func isFile(_ path: AbsolutePath) -> Bool {
1046-
return underlyingFileSystem.isFile(formUnderlyingPath(path))
1062+
guard let underlying = try? formUnderlyingPath(path) else {
1063+
return false
1064+
}
1065+
return underlyingFileSystem.isFile(underlying)
10471066
}
10481067

10491068
public func isSymlink(_ path: AbsolutePath) -> Bool {
1050-
return underlyingFileSystem.isSymlink(formUnderlyingPath(path))
1069+
guard let underlying = try? formUnderlyingPath(path) else {
1070+
return false
1071+
}
1072+
return underlyingFileSystem.isSymlink(underlying)
10511073
}
10521074

10531075
public func isReadable(_ path: AbsolutePath) -> Bool {
1054-
return underlyingFileSystem.isReadable(formUnderlyingPath(path))
1076+
guard let underlying = try? formUnderlyingPath(path) else {
1077+
return false
1078+
}
1079+
return underlyingFileSystem.isReadable(underlying)
10551080
}
10561081

10571082
public func isWritable(_ path: AbsolutePath) -> Bool {
1058-
return underlyingFileSystem.isWritable(formUnderlyingPath(path))
1083+
guard let underlying = try? formUnderlyingPath(path) else {
1084+
return false
1085+
}
1086+
return underlyingFileSystem.isWritable(underlying)
10591087
}
10601088

10611089
public func isExecutableFile(_ path: AbsolutePath) -> Bool {
1062-
return underlyingFileSystem.isExecutableFile(formUnderlyingPath(path))
1090+
guard let underlying = try? formUnderlyingPath(path) else {
1091+
return false
1092+
}
1093+
return underlyingFileSystem.isExecutableFile(underlying)
10631094
}
10641095

10651096
/// Virtualized current working directory.
10661097
public var currentWorkingDirectory: AbsolutePath? {
1067-
return AbsolutePath("/")
1098+
return try? AbsolutePath(validating: "/")
10681099
}
10691100

10701101
public func changeCurrentWorkingDirectory(to path: AbsolutePath) throws {
@@ -1088,13 +1119,13 @@ public class RerootedFileSystemView: FileSystem {
10881119
}
10891120

10901121
public func createDirectory(_ path: AbsolutePath, recursive: Bool) throws {
1091-
let path = formUnderlyingPath(path)
1122+
let path = try formUnderlyingPath(path)
10921123
return try underlyingFileSystem.createDirectory(path, recursive: recursive)
10931124
}
10941125

10951126
public func createSymbolicLink(_ path: AbsolutePath, pointingAt destination: AbsolutePath, relative: Bool) throws {
1096-
let path = formUnderlyingPath(path)
1097-
let destination = formUnderlyingPath(destination)
1127+
let path = try formUnderlyingPath(path)
1128+
let destination = try formUnderlyingPath(destination)
10981129
return try underlyingFileSystem.createSymbolicLink(path, pointingAt: destination, relative: relative)
10991130
}
11001131

@@ -1103,7 +1134,7 @@ public class RerootedFileSystemView: FileSystem {
11031134
}
11041135

11051136
public func writeFileContents(_ path: AbsolutePath, bytes: ByteString) throws {
1106-
let path = formUnderlyingPath(path)
1137+
let path = try formUnderlyingPath(path)
11071138
return try underlyingFileSystem.writeFileContents(path, bytes: bytes)
11081139
}
11091140

Sources/TSCBasic/Lock.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,15 +180,15 @@ public final class FileLock {
180180

181181
public static func withLock<T>(fileToLock: AbsolutePath, lockFilesDirectory: AbsolutePath? = nil, type: LockType = .exclusive, body: () throws -> T) throws -> T {
182182
// unless specified, we use the tempDirectory to store lock files
183-
let lockFilesDirectory = lockFilesDirectory ?? localFileSystem.tempDirectory
183+
let lockFilesDirectory = try lockFilesDirectory ?? localFileSystem.tempDirectory
184184
if !localFileSystem.exists(lockFilesDirectory) {
185185
throw FileSystemError(.noEntry, lockFilesDirectory)
186186
}
187187
if !localFileSystem.isDirectory(lockFilesDirectory) {
188188
throw FileSystemError(.notDirectory, lockFilesDirectory)
189189
}
190190
// use the parent path to generate unique filename in temp
191-
var lockFileName = (resolveSymlinks(fileToLock.parentDirectory)
191+
var lockFileName = try (resolveSymlinks(fileToLock.parentDirectory)
192192
.appending(component: fileToLock.basename))
193193
.components.joined(separator: "_")
194194
.replacingOccurrences(of: ":", with: "_") + ".lock"

Sources/TSCBasic/Path.swift

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -59,21 +59,12 @@ public struct AbsolutePath: Hashable {
5959
_impl = impl
6060
}
6161

62-
/// Initializes the AbsolutePath from `absStr`, which must be an absolute
63-
/// path (i.e. it must begin with a path separator; this initializer does
64-
/// not interpret leading `~` characters as home directory specifiers).
65-
/// The input string will be normalized if needed, as described in the
66-
/// documentation for AbsolutePath.
67-
public init(_ absStr: String) {
68-
self.init(PathImpl(normalizingAbsolutePath: absStr))
69-
}
70-
7162
/// Initializes an AbsolutePath from a string that may be either absolute
7263
/// or relative; if relative, `basePath` is used as the anchor; if absolute,
7364
/// it is used as is, and in this case `basePath` is ignored.
74-
public init(_ str: String, relativeTo basePath: AbsolutePath) {
65+
public init(validating str: String, relativeTo basePath: AbsolutePath) throws {
7566
if PathImpl(string: str).isAbsolute {
76-
self.init(str)
67+
try self.init(validating: str)
7768
} else {
7869
#if os(Windows)
7970
assert(!basePath.pathString.isEmpty)
@@ -115,7 +106,11 @@ public struct AbsolutePath: Hashable {
115106
self.init(absPath, RelativePath(relStr))
116107
}
117108

118-
/// Convenience initializer that verifies that the path is absolute.
109+
/// Initializes the AbsolutePath from `absStr`, which must be an absolute
110+
/// path (i.e. it must begin with a path separator; this initializer does
111+
/// not interpret leading `~` characters as home directory specifiers).
112+
/// The input string will be normalized if needed, as described in the
113+
/// documentation for AbsolutePath.
119114
public init(validating path: String) throws {
120115
try self.init(PathImpl(validatingAbsolutePath: path))
121116
}
@@ -1065,3 +1060,17 @@ private func mayNeedNormalization(absolute string: String) -> Bool {
10651060
}
10661061
return false
10671062
}
1063+
1064+
// MARK: - `AbsolutePath` backwards compatibility, delete after deprecation period.
1065+
1066+
extension AbsolutePath {
1067+
@available(*, deprecated, message: "use throwing variant instead")
1068+
public init(_ absStr: String) {
1069+
try! self.init(validating: absStr)
1070+
}
1071+
1072+
@available(*, deprecated, message: "use throwing variant instead")
1073+
public init(_ str: String, relativeTo basePath: AbsolutePath) {
1074+
try! self.init(validating: str, relativeTo: basePath)
1075+
}
1076+
}

Sources/TSCBasic/PathShims.swift

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import TSCLibc
2121
import Foundation
2222

2323
/// Returns the "real path" corresponding to `path` by resolving any symbolic links.
24-
public func resolveSymlinks(_ path: AbsolutePath) -> AbsolutePath {
24+
public func resolveSymlinks(_ path: AbsolutePath) throws -> AbsolutePath {
2525
#if os(Windows)
2626
let handle: HANDLE = path.pathString.withCString(encodedAs: UTF16.self) {
2727
CreateFileW($0, GENERIC_READ, DWORD(FILE_SHARE_READ), nil,
@@ -34,7 +34,7 @@ public func resolveSymlinks(_ path: AbsolutePath) -> AbsolutePath {
3434
GetFinalPathNameByHandleW(handle, $0.baseAddress!, DWORD($0.count),
3535
DWORD(FILE_NAME_NORMALIZED))
3636
let path = String(decodingCString: $0.baseAddress!, as: UTF16.self)
37-
return try! AbsolutePath(validating: path)
37+
return try AbsolutePath(path)
3838
}
3939
#else
4040
let pathStr = path.pathString
@@ -51,7 +51,7 @@ public func resolveSymlinks(_ path: AbsolutePath) -> AbsolutePath {
5151
// null-terminated UTF-8 data referenced by the given pointer.
5252
resultPtr.deallocate()
5353
// FIXME: We should measure if it's really more efficient to compare the strings first.
54-
return result == pathStr ? path : AbsolutePath(result)
54+
return result == pathStr ? path : try AbsolutePath(validating: result)
5555
}
5656

5757
return path
@@ -72,13 +72,6 @@ public func createSymlink(_ path: AbsolutePath, pointingAt dest: AbsolutePath, r
7272
try FileManager.default.createSymbolicLink(atPath: path.pathString, withDestinationPath: destString)
7373
}
7474

75-
/// The current working directory of the processs.
76-
@available(*, deprecated, renamed: "localFileSystem.currentWorkingDirectory")
77-
public var currentWorkingDirectory: AbsolutePath {
78-
let cwdStr = FileManager.default.currentDirectoryPath
79-
return AbsolutePath(cwdStr)
80-
}
81-
8275
/**
8376
- Returns: a generator that walks the specified directory producing all
8477
files therein. If recursively is true will enter any directories

Sources/TSCBasic/TemporaryFile.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ private extension TempFileError {
5353
///
5454
/// - Returns: Path to directory in which temporary file should be created.
5555
public func determineTempDirectory(_ dir: AbsolutePath? = nil) throws -> AbsolutePath {
56-
let tmpDir = dir ?? localFileSystem.tempDirectory
56+
let tmpDir = try dir ?? localFileSystem.tempDirectory
5757
guard localFileSystem.isDirectory(tmpDir) else {
5858
throw TempFileError.couldNotFindTmpDir(tmpDir.pathString)
5959
}
@@ -87,7 +87,7 @@ public struct TemporaryFile {
8787
// Determine in which directory to create the temporary file.
8888
self.dir = try determineTempDirectory(dir)
8989
// Construct path to the temporary file.
90-
let path = AbsolutePath(prefix + ".XXXXXX" + suffix, relativeTo: self.dir)
90+
let path = try AbsolutePath(validating: prefix + ".XXXXXX" + suffix, relativeTo: self.dir)
9191

9292
// Convert path to a C style string terminating with null char to be an valid input
9393
// to mkstemps method. The XXXXXX in this string will be replaced by a random string
@@ -98,7 +98,7 @@ public struct TemporaryFile {
9898
// If mkstemps failed then throw error.
9999
if fd == -1 { throw TempFileError(errno: errno) }
100100

101-
self.path = AbsolutePath(String(cString: template))
101+
self.path = try AbsolutePath(validating: String(cString: template))
102102
fileHandle = FileHandle(fileDescriptor: fd, closeOnDealloc: true)
103103
}
104104
}
@@ -236,7 +236,7 @@ public func withTemporaryDirectory<Result>(
236236
dir: AbsolutePath? = nil, prefix: String = "TemporaryDirectory" , _ body: (AbsolutePath, @escaping (AbsolutePath) -> Void) throws -> Result
237237
) throws -> Result {
238238
// Construct path to the temporary directory.
239-
let templatePath = try AbsolutePath(prefix + ".XXXXXX", relativeTo: determineTempDirectory(dir))
239+
let templatePath = try AbsolutePath(validating: prefix + ".XXXXXX", relativeTo: determineTempDirectory(dir))
240240

241241
// Convert templatePath to a C style string terminating with null char to be an valid input
242242
// to mkdtemp method. The XXXXXX in this string will be replaced by a random string
@@ -247,7 +247,7 @@ public func withTemporaryDirectory<Result>(
247247
throw MakeDirectoryError(errno: errno)
248248
}
249249

250-
return try body(AbsolutePath(String(cString: template))) { path in
250+
return try body(AbsolutePath(validating: String(cString: template))) { path in
251251
_ = try? FileManager.default.removeItem(atPath: path.pathString)
252252
}
253253
}

0 commit comments

Comments
 (0)