Skip to content

Commit dbdc714

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

27 files changed

+375
-342
lines changed

Sources/TSCBasic/FileSystem.swift

Lines changed: 56 additions & 33 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+
func homeDirectory() throws -> AbsolutePath
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+
func tempDirectory() throws -> AbsolutePath
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

@@ -367,20 +369,20 @@ private class LocalFileSystem: FileSystem {
367369
}
368370
}
369371

370-
var homeDirectory: AbsolutePath {
371-
return AbsolutePath(NSHomeDirectory())
372+
func homeDirectory() throws -> AbsolutePath {
373+
return try AbsolutePath(validating: NSHomeDirectory())
372374
}
373375

374376
var cachesDirectory: AbsolutePath? {
375-
return FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first.flatMap { AbsolutePath($0.path) }
377+
return FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first.flatMap { try? AbsolutePath(validating: $0.path) }
376378
}
377379

378-
var tempDirectory: AbsolutePath {
380+
func tempDirectory() throws -> AbsolutePath {
379381
let override = ProcessEnv.vars["TMPDIR"] ?? ProcessEnv.vars["TEMP"] ?? ProcessEnv.vars["TMP"]
380382
if let path = override.flatMap({ try? AbsolutePath(validating: $0) }) {
381383
return path
382384
}
383-
return AbsolutePath(NSTemporaryDirectory())
385+
return try AbsolutePath(validating: NSTemporaryDirectory())
384386
}
385387

386388
func getDirectoryContents(_ path: AbsolutePath) throws -> [String] {
@@ -664,7 +666,7 @@ public class InMemoryFileSystem: FileSystem {
664666
case .directory, .file:
665667
return node
666668
case .symlink(let destination):
667-
let destination = AbsolutePath(destination, relativeTo: path.parentDirectory)
669+
let destination = try AbsolutePath(validating: destination, relativeTo: path.parentDirectory)
668670
return followSymlink ? try getNodeInternal(destination) : node
669671
case .none:
670672
return nil
@@ -745,24 +747,24 @@ public class InMemoryFileSystem: FileSystem {
745747

746748
/// Virtualized current working directory.
747749
public var currentWorkingDirectory: AbsolutePath? {
748-
return AbsolutePath("/")
750+
return try? AbsolutePath(validating: "/")
749751
}
750752

751753
public func changeCurrentWorkingDirectory(to path: AbsolutePath) throws {
752754
throw FileSystemError(.unsupported, path)
753755
}
754756

755-
public var homeDirectory: AbsolutePath {
757+
public func homeDirectory() throws -> AbsolutePath {
756758
// FIXME: Maybe we should allow setting this when creating the fs.
757-
return AbsolutePath("/home/user")
759+
return try AbsolutePath(validating: "/home/user")
758760
}
759761

760762
public var cachesDirectory: AbsolutePath? {
761-
return self.homeDirectory.appending(component: "caches")
763+
return try? self.homeDirectory().appending(component: "caches")
762764
}
763765

764-
public var tempDirectory: AbsolutePath {
765-
return AbsolutePath("/tmp")
766+
public func tempDirectory() throws -> AbsolutePath {
767+
return try AbsolutePath(validating: "/tmp")
766768
}
767769

768770
public func getDirectoryContents(_ path: AbsolutePath) throws -> [String] {
@@ -978,7 +980,7 @@ public class InMemoryFileSystem: FileSystem {
978980
public func withLock<T>(on path: AbsolutePath, type: FileLock.LockType = .exclusive, _ body: () throws -> T) throws -> T {
979981
let resolvedPath: AbsolutePath = try lock.withLock {
980982
if case let .symlink(destination) = try getNode(path)?.contents {
981-
return AbsolutePath(destination, relativeTo: path.parentDirectory)
983+
return try AbsolutePath(validating: destination, relativeTo: path.parentDirectory)
982984
} else {
983985
return path
984986
}
@@ -1023,63 +1025,84 @@ public class RerootedFileSystemView: FileSystem {
10231025
}
10241026

10251027
/// Adjust the input path for the underlying file system.
1026-
private func formUnderlyingPath(_ path: AbsolutePath) -> AbsolutePath {
1028+
private func formUnderlyingPath(_ path: AbsolutePath) throws -> AbsolutePath {
10271029
if path == AbsolutePath.root {
10281030
return root
10291031
} else {
10301032
// FIXME: Optimize?
1031-
return AbsolutePath(String(path.pathString.dropFirst(1)), relativeTo: root)
1033+
return try AbsolutePath(validating: String(path.pathString.dropFirst(1)), relativeTo: root)
10321034
}
10331035
}
10341036

10351037
// MARK: FileSystem Implementation
10361038

10371039
public func exists(_ path: AbsolutePath, followSymlink: Bool) -> Bool {
1038-
return underlyingFileSystem.exists(formUnderlyingPath(path), followSymlink: followSymlink)
1040+
guard let underlying = try? formUnderlyingPath(path) else {
1041+
return false
1042+
}
1043+
return underlyingFileSystem.exists(underlying, followSymlink: followSymlink)
10391044
}
10401045

10411046
public func isDirectory(_ path: AbsolutePath) -> Bool {
1042-
return underlyingFileSystem.isDirectory(formUnderlyingPath(path))
1047+
guard let underlying = try? formUnderlyingPath(path) else {
1048+
return false
1049+
}
1050+
return underlyingFileSystem.isDirectory(underlying)
10431051
}
10441052

10451053
public func isFile(_ path: AbsolutePath) -> Bool {
1046-
return underlyingFileSystem.isFile(formUnderlyingPath(path))
1054+
guard let underlying = try? formUnderlyingPath(path) else {
1055+
return false
1056+
}
1057+
return underlyingFileSystem.isFile(underlying)
10471058
}
10481059

10491060
public func isSymlink(_ path: AbsolutePath) -> Bool {
1050-
return underlyingFileSystem.isSymlink(formUnderlyingPath(path))
1061+
guard let underlying = try? formUnderlyingPath(path) else {
1062+
return false
1063+
}
1064+
return underlyingFileSystem.isSymlink(underlying)
10511065
}
10521066

10531067
public func isReadable(_ path: AbsolutePath) -> Bool {
1054-
return underlyingFileSystem.isReadable(formUnderlyingPath(path))
1068+
guard let underlying = try? formUnderlyingPath(path) else {
1069+
return false
1070+
}
1071+
return underlyingFileSystem.isReadable(underlying)
10551072
}
10561073

10571074
public func isWritable(_ path: AbsolutePath) -> Bool {
1058-
return underlyingFileSystem.isWritable(formUnderlyingPath(path))
1075+
guard let underlying = try? formUnderlyingPath(path) else {
1076+
return false
1077+
}
1078+
return underlyingFileSystem.isWritable(underlying)
10591079
}
10601080

10611081
public func isExecutableFile(_ path: AbsolutePath) -> Bool {
1062-
return underlyingFileSystem.isExecutableFile(formUnderlyingPath(path))
1082+
guard let underlying = try? formUnderlyingPath(path) else {
1083+
return false
1084+
}
1085+
return underlyingFileSystem.isExecutableFile(underlying)
10631086
}
10641087

10651088
/// Virtualized current working directory.
10661089
public var currentWorkingDirectory: AbsolutePath? {
1067-
return AbsolutePath("/")
1090+
return try? AbsolutePath(validating: "/")
10681091
}
10691092

10701093
public func changeCurrentWorkingDirectory(to path: AbsolutePath) throws {
10711094
throw FileSystemError(.unsupported, path)
10721095
}
10731096

1074-
public var homeDirectory: AbsolutePath {
1097+
public func homeDirectory() throws -> AbsolutePath {
10751098
fatalError("homeDirectory on RerootedFileSystemView is not supported.")
10761099
}
10771100

10781101
public var cachesDirectory: AbsolutePath? {
10791102
fatalError("cachesDirectory on RerootedFileSystemView is not supported.")
10801103
}
10811104

1082-
public var tempDirectory: AbsolutePath {
1105+
public func tempDirectory() throws -> AbsolutePath {
10831106
fatalError("tempDirectory on RerootedFileSystemView is not supported.")
10841107
}
10851108

@@ -1088,13 +1111,13 @@ public class RerootedFileSystemView: FileSystem {
10881111
}
10891112

10901113
public func createDirectory(_ path: AbsolutePath, recursive: Bool) throws {
1091-
let path = formUnderlyingPath(path)
1114+
let path = try formUnderlyingPath(path)
10921115
return try underlyingFileSystem.createDirectory(path, recursive: recursive)
10931116
}
10941117

10951118
public func createSymbolicLink(_ path: AbsolutePath, pointingAt destination: AbsolutePath, relative: Bool) throws {
1096-
let path = formUnderlyingPath(path)
1097-
let destination = formUnderlyingPath(destination)
1119+
let path = try formUnderlyingPath(path)
1120+
let destination = try formUnderlyingPath(destination)
10981121
return try underlyingFileSystem.createSymbolicLink(path, pointingAt: destination, relative: relative)
10991122
}
11001123

@@ -1103,7 +1126,7 @@ public class RerootedFileSystemView: FileSystem {
11031126
}
11041127

11051128
public func writeFileContents(_ path: AbsolutePath, bytes: ByteString) throws {
1106-
let path = formUnderlyingPath(path)
1129+
let path = try formUnderlyingPath(path)
11071130
return try underlyingFileSystem.writeFileContents(path, bytes: bytes)
11081131
}
11091132

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: 7 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
}

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
}

Sources/TSCBasic/misc.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ public func getEnvSearchPaths(
195195
#endif
196196
return (pathString ?? "").split(separator: pathSeparator).map(String.init).compactMap({ pathString in
197197
if let cwd = currentWorkingDirectory {
198-
return AbsolutePath(pathString, relativeTo: cwd)
198+
return try? AbsolutePath(validating: pathString, relativeTo: cwd)
199199
}
200200
return try? AbsolutePath(validating: pathString)
201201
})
@@ -227,9 +227,9 @@ public func lookupExecutablePath(
227227

228228
var paths: [AbsolutePath] = []
229229

230-
if let cwd = currentWorkingDirectory {
230+
if let cwd = currentWorkingDirectory, let path = try? AbsolutePath(validating: value, relativeTo: cwd) {
231231
// We have a value, but it could be an absolute or a relative path.
232-
paths.append(AbsolutePath(value, relativeTo: cwd))
232+
paths.append(path)
233233
} else if let absPath = try? AbsolutePath(validating: value) {
234234
// Current directory not being available is not a problem
235235
// for the absolute-specified paths.

0 commit comments

Comments
 (0)