diff --git a/CODEOWNERS b/CODEOWNERS index ddaf4ce8..6fc335a0 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -7,11 +7,7 @@ # beautification by scripts. The fields are: name (N), email (E), web-address # (W), PGP key ID and fingerprint (P), description (D), and snail-mail address # (S). - -# N: Ankit Aggarwal -# E: ankit_aggarwal@apple.com -# D: Package Manager -# + # N: Anders Bertelrud # E: anders@apple.com # D: Package Manager @@ -20,8 +16,20 @@ # E: daniel_dunbar@apple.com # D: Package Manager +# N: Boris Buegling +# E: bbuegling@apple.com +# D: Package Manager + +# N: Tomer Doron +# E: tomer@apple.com +# D: Package Manager + +# N: Max Desiatov +# E: m_desiatov@apple.com +# D: Package Manager + ### # The following lines are used by GitHub to automatically recommend reviewers. -* @aciidb0mb3r @abertelrud @neonichu @friedbunny @tomerd +* @abertelrud @neonichu @tomerd @MaxDesiatov diff --git a/Package.swift b/Package.swift index 467c41a5..ab2774c8 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.5 +// swift-tools-version:5.7 /* This source file is part of the Swift.org open source project @@ -18,7 +18,7 @@ let macOSPlatform: SupportedPlatform if let deploymentTarget = ProcessInfo.processInfo.environment["SWIFTTSC_MACOS_DEPLOYMENT_TARGET"] { macOSPlatform = .macOS(deploymentTarget) } else { - macOSPlatform = .macOS(.v10_13) + macOSPlatform = .macOS(.v10_15) } let CMakeFiles = ["CMakeLists.txt"] @@ -27,6 +27,7 @@ let package = Package( name: "swift-tools-support-core", platforms: [ macOSPlatform, + .iOS(.v15) ], products: [ .library( @@ -53,7 +54,10 @@ let package = Package( /** Shim target to import missing C headers in Darwin and Glibc modulemap. */ name: "TSCclibc", dependencies: [], - exclude: CMakeFiles), + exclude: CMakeFiles, + cSettings: [ + .define("_GNU_SOURCE", .when(platforms: [.linux])), + ]), .target( /** Cross-platform access to bare `libc` functionality. */ name: "TSCLibc", @@ -67,7 +71,13 @@ let package = Package( "TSCclibc", .product(name: "SystemPackage", package: "swift-system"), ], - exclude: CMakeFiles + ["README.md"]), + exclude: CMakeFiles + ["README.md"], + cxxSettings: [ + .define("_CRT_SECURE_NO_WARNINGS", .when(platforms: [.windows])), + ], + linkerSettings: [ + .linkedLibrary("Pathcch", .when(platforms: [.windows])), + ]), .target( /** Abstractions for common operations, should migrate to TSCBasic */ name: "TSCUtility", @@ -103,29 +113,10 @@ let package = Package( if ProcessInfo.processInfo.environment["SWIFTCI_USE_LOCAL_DEPS"] == nil { package.dependencies += [ - .package(url: "https://github.com/apple/swift-system.git", .upToNextMinor(from: "1.1.1")), + .package(url: "https://github.com/apple/swift-system.git", from: "1.1.1"), ] } else { package.dependencies += [ .package(path: "../swift-system"), ] } - -// FIXME: conditionalise these flags since SwiftPM 5.3 and earlier will crash -// for platforms they don't know about. -#if os(Windows) - if let TSCBasic = package.targets.first(where: { $0.name == "TSCBasic" }) { - TSCBasic.cxxSettings = [ - .define("_CRT_SECURE_NO_WARNINGS", .when(platforms: [.windows])), - ] - TSCBasic.linkerSettings = [ - .linkedLibrary("Pathcch", .when(platforms: [.windows])), - ] - } -#elseif os(Linux) - if let TSCclibc = package.targets.first(where: { $0.name == "TSCclibc" }) { - TSCclibc.cSettings = [ - .define("_GNU_SOURCE", .when(platforms: [.linux])), - ] - } -#endif diff --git a/README.md b/README.md index 476d74e3..3a79aab0 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,13 @@ swift-tools-support-core Contains common infrastructural code for both [SwiftPM](https://github.com/apple/swift-package-manager) and [llbuild](https://github.com/apple/swift-llbuild). +## ⚠️ This package is deprecated + +As this package with time has become a collection of unrelated utilities, that made it much harder to version. +Primary users of TSC such as SwiftPM and Swift Driver came up with specialized alternatives to APIs provided +in TSC. Moving forward, we don't recommend adding TSC as a dependency to your project. More and more types +and functions here will be deprecated, with minimal modifications to ease the migration off TSC. + License ------- diff --git a/Sources/TSCBasic/DiagnosticsEngine.swift b/Sources/TSCBasic/DiagnosticsEngine.swift index 2f3df235..8bdc4580 100644 --- a/Sources/TSCBasic/DiagnosticsEngine.swift +++ b/Sources/TSCBasic/DiagnosticsEngine.swift @@ -148,11 +148,11 @@ public final class DiagnosticsEngine: CustomStringConvertible { public var description: String { let stream = BufferedOutputByteStream() - stream <<< "[" + stream.send("[") for diag in diagnostics { - stream <<< diag.description <<< ", " + stream.send(diag.description).send(", ") } - stream <<< "]" + stream.send("]") return stream.bytes.description } } diff --git a/Sources/TSCBasic/FileSystem.swift b/Sources/TSCBasic/FileSystem.swift index 741aa93e..b33b1c61 100644 --- a/Sources/TSCBasic/FileSystem.swift +++ b/Sources/TSCBasic/FileSystem.swift @@ -137,6 +137,33 @@ public enum FileMode: Sendable { } } +/// Extended file system attributes that can applied to a given file path. See also ``FileSystem/hasAttribute(_:_:)``. +public enum FileSystemAttribute: RawRepresentable { + #if canImport(Darwin) + case quarantine + #endif + + public init?(rawValue: String) { + switch rawValue { + #if canImport(Darwin) + case "com.apple.quarantine": + self = .quarantine + #endif + default: + return nil + } + } + + public var rawValue: String { + switch self { + #if canImport(Darwin) + case .quarantine: + return "com.apple.quarantine" + #endif + } + } +} + // FIXME: Design an asynchronous story? // /// Abstracted access to file system operations. @@ -146,8 +173,9 @@ public enum FileMode: Sendable { /// substitute a virtual file system or redirect file system operations. /// /// - Note: All of these APIs are synchronous and can block. -public protocol FileSystem: AnyObject { +public protocol FileSystem: Sendable { /// Check whether the given path exists and is accessible. + @_disfavoredOverload func exists(_ path: AbsolutePath, followSymlink: Bool) -> Bool /// Check whether the given path is accessible and a directory. @@ -168,6 +196,17 @@ public protocol FileSystem: AnyObject { /// Check whether the given path is accessible and writable. func isWritable(_ path: AbsolutePath) -> Bool + /// Returns any known item replacement directories for a given path. These may be used by platform-specific + /// libraries to handle atomic file system operations, such as deletion. + func itemReplacementDirectories(for path: AbsolutePath) throws -> [AbsolutePath] + + @available(*, deprecated, message: "use `hasAttribute(_:_:)` instead") + func hasQuarantineAttribute(_ path: AbsolutePath) -> Bool + + /// Returns `true` if a given path has an attribute with a given name applied when file system supports this + /// attribute. Returns `false` if such attribute is not applied or it isn't supported. + func hasAttribute(_ name: FileSystemAttribute, _ path: AbsolutePath) -> Bool + // FIXME: Actual file system interfaces will allow more efficient access to // more data than just the name here. // @@ -179,6 +218,7 @@ public protocol FileSystem: AnyObject { /// The current working directory can be empty if e.g. the directory became /// unavailable while the current process was still working in it. /// This follows the POSIX `getcwd(3)` semantics. + @_disfavoredOverload var currentWorkingDirectory: AbsolutePath? { get } /// Change the current working directory. @@ -187,12 +227,15 @@ public protocol FileSystem: AnyObject { func changeCurrentWorkingDirectory(to path: AbsolutePath) throws /// Get the home directory of current user + @_disfavoredOverload var homeDirectory: AbsolutePath { get throws } /// Get the caches directory of current user + @_disfavoredOverload var cachesDirectory: AbsolutePath? { get } /// Get the temp directory + @_disfavoredOverload var tempDirectory: AbsolutePath { get throws } /// Create the given directory. @@ -249,12 +292,16 @@ public protocol FileSystem: AnyObject { /// Execute the given block while holding the lock. func withLock(on path: AbsolutePath, type: FileLock.LockType, _ body: () throws -> T) throws -> T + + /// Execute the given block while holding the lock. + func withLock(on path: AbsolutePath, type: FileLock.LockType, _ body: () async throws -> T) async throws -> T } /// Convenience implementations (default arguments aren't permitted in protocol /// methods). public extension FileSystem { /// exists override with default value. + @_disfavoredOverload func exists(_ path: AbsolutePath) -> Bool { return exists(path, followSymlink: true) } @@ -271,6 +318,7 @@ public extension FileSystem { // Unless the file system type provides an override for this method, throw // if `atomically` is `true`, otherwise fall back to whatever implementation already exists. + @_disfavoredOverload func writeFileContents(_ path: AbsolutePath, bytes: ByteString, atomically: Bool) throws { guard !atomically else { throw FileSystemError(.unsupported, path) @@ -279,6 +327,7 @@ public extension FileSystem { } /// Write to a file from a stream producer. + @_disfavoredOverload func writeFileContents(_ path: AbsolutePath, body: (WritableByteStream) -> Void) throws { let contents = BufferedOutputByteStream() body(contents) @@ -293,11 +342,20 @@ public extension FileSystem { func withLock(on path: AbsolutePath, type: FileLock.LockType, _ body: () throws -> T) throws -> T { throw FileSystemError(.unsupported, path) } + + func withLock(on path: AbsolutePath, type: FileLock.LockType, _ body: () async throws -> T) async throws -> T { + throw FileSystemError(.unsupported, path) + } + + func hasQuarantineAttribute(_ path: AbsolutePath) -> Bool { false } + + func hasAttribute(_ name: FileSystemAttribute, _ path: AbsolutePath) -> Bool { false } + + func itemReplacementDirectories(for path: AbsolutePath) throws -> [AbsolutePath] { [] } } /// Concrete FileSystem implementation which communicates with the local file system. -private class LocalFileSystem: FileSystem { - +private struct LocalFileSystem: FileSystem { func isExecutableFile(_ path: AbsolutePath) -> Bool { // Our semantics doesn't consider directories. return (self.isFile(path) || self.isSymlink(path)) && FileManager.default.isExecutableFile(atPath: path.pathString) @@ -325,8 +383,10 @@ private class LocalFileSystem: FileSystem { } func isSymlink(_ path: AbsolutePath) -> Bool { - let attrs = try? FileManager.default.attributesOfItem(atPath: path.pathString) - return attrs?[.type] as? FileAttributeType == .typeSymbolicLink + let url = NSURL(fileURLWithPath: path.pathString) + // We are intentionally using `NSURL.resourceValues(forKeys:)` here since it improves performance on Darwin platforms. + let result = try? url.resourceValues(forKeys: [.isSymbolicLinkKey]) + return (result?[.isSymbolicLinkKey] as? Bool) == true } func isReadable(_ path: AbsolutePath) -> Bool { @@ -342,6 +402,16 @@ private class LocalFileSystem: FileSystem { return FileInfo(attrs) } + func hasAttribute(_ name: FileSystemAttribute, _ path: AbsolutePath) -> Bool { +#if canImport(Darwin) + let bufLength = getxattr(path.pathString, name.rawValue, nil, 0, 0, 0) + + return bufLength > 0 +#else + return false +#endif + } + var currentWorkingDirectory: AbsolutePath? { let cwdStr = FileManager.default.currentDirectoryPath @@ -443,7 +513,7 @@ private class LocalFileSystem: FileSystem { } break } - data <<< tmpBuffer[0..(on path: AbsolutePath, type: FileLock.LockType = .exclusive, _ body: () throws -> T) throws -> T { try FileLock.withLock(fileToLock: path, type: type, body: body) } + + func withLock( + on path: AbsolutePath, + type: FileLock.LockType = .exclusive, + _ body: () async throws -> T + ) async throws -> T { + try await FileLock.withLock(fileToLock: path, type: type, body: body) + } + + func itemReplacementDirectories(for path: AbsolutePath) throws -> [AbsolutePath] { + let result = try FileManager.default.url(for: .itemReplacementDirectory, in: .userDomainMask, appropriateFor: path.asURL, create: false) + let path = try AbsolutePath(validating: result.path) + // Foundation returns a path that is unique every time, so we return both that path, as well as its parent. + return [path, path.parentDirectory] + } } -// FIXME: This class does not yet support concurrent mutation safely. -// /// Concrete FileSystem implementation which simulates an empty disk. -public class InMemoryFileSystem: FileSystem { - +public final class InMemoryFileSystem: FileSystem { /// Private internal representation of a file system node. - /// Not threadsafe. + /// Not thread-safe. private class Node { /// The actual node data. let contents: NodeContents @@ -570,7 +652,7 @@ public class InMemoryFileSystem: FileSystem { } /// Private internal representation the contents of a file system node. - /// Not threadsafe. + /// Not thread-safe. private enum NodeContents { case file(ByteString) case directory(DirectoryContents) @@ -590,8 +672,8 @@ public class InMemoryFileSystem: FileSystem { } /// Private internal representation the contents of a directory. - /// Not threadsafe. - private class DirectoryContents { + /// Not thread-safe. + private final class DirectoryContents { var entries: [String: Node] init(entries: [String: Node] = [:]) { @@ -645,7 +727,7 @@ public class InMemoryFileSystem: FileSystem { } /// Private function to look up the node corresponding to a path. - /// Not threadsafe. + /// Not thread-safe. private func getNode(_ path: AbsolutePath, followSymlink: Bool = true) throws -> Node? { func getNodeInternal(_ path: AbsolutePath) throws -> Node? { // If this is the root node, return it. @@ -789,7 +871,7 @@ public class InMemoryFileSystem: FileSystem { } } - /// Not threadsafe. + /// Not thread-safe. private func _createDirectory(_ path: AbsolutePath, recursive: Bool) throws { // Ignore if client passes root. guard !path.isRoot else { @@ -937,7 +1019,7 @@ public class InMemoryFileSystem: FileSystem { } /// Private implementation of core copying function. - /// Not threadsafe. + /// Not thread-safe. private func _copy(from sourcePath: AbsolutePath, to destinationPath: AbsolutePath) throws { // Get the source node. guard let source = try getNode(sourcePath) else { @@ -1008,6 +1090,13 @@ public class InMemoryFileSystem: FileSystem { } } +// Internal state of `InMemoryFileSystem` is protected with a lock in all of its `public` methods. +#if compiler(>=5.7) +extension InMemoryFileSystem: @unchecked Sendable {} +#else +extension InMemoryFileSystem: UnsafeSendable {} +#endif + /// A rerooted view on an existing FileSystem. /// /// This is a simple wrapper which creates a new FileSystem view into a subtree @@ -1020,7 +1109,7 @@ public class InMemoryFileSystem: FileSystem { /// is designed for situations where a client is only interested in the contents /// *visible* within a subpath and is agnostic to the actual location of those /// contents. -public class RerootedFileSystemView: FileSystem { +public final class RerootedFileSystemView: FileSystem { /// The underlying file system. private var underlyingFileSystem: FileSystem @@ -1159,8 +1248,27 @@ public class RerootedFileSystemView: FileSystem { } } +// `RerootedFileSystemView` doesn't hold any internal state and can be considered `Sendable` since +// `underlyingFileSystem` is required to be `Sendable`. +#if compiler(>=5.7) +extension RerootedFileSystemView: @unchecked Sendable {} +#else +extension RerootedFileSystemView: UnsafeSendable {} +#endif + +private var _localFileSystem: FileSystem = LocalFileSystem() + /// Public access to the local FS proxy. -public var localFileSystem: FileSystem = LocalFileSystem() +public var localFileSystem: FileSystem { + get { + return _localFileSystem + } + + @available(*, deprecated, message: "This global should never be mutable and is supposed to be read-only. Deprecated in Apr 2023.") + set { + _localFileSystem = newValue + } +} extension FileSystem { /// Print the filesystem tree of the given path. diff --git a/Sources/TSCBasic/JSON.swift b/Sources/TSCBasic/JSON.swift index a1001640..8138b414 100644 --- a/Sources/TSCBasic/JSON.swift +++ b/Sources/TSCBasic/JSON.swift @@ -126,41 +126,41 @@ extension JSON: ByteStreamable { let shouldIndent = indent != nil switch self { case .null: - stream <<< "null" + stream.send("null") case .bool(let value): - stream <<< Format.asJSON(value) + stream.send(Format.asJSON(value)) case .int(let value): - stream <<< Format.asJSON(value) + stream.send(Format.asJSON(value)) case .double(let value): // FIXME: What happens for NaN, etc.? - stream <<< Format.asJSON(value) + stream.send(Format.asJSON(value)) case .string(let value): - stream <<< Format.asJSON(value) + stream.send(Format.asJSON(value)) case .array(let contents): - stream <<< "[" <<< (shouldIndent ? "\n" : "") + stream.send("[").send(shouldIndent ? "\n" : "") for (i, item) in contents.enumerated() { - if i != 0 { stream <<< "," <<< (shouldIndent ? "\n" : " ") } - stream <<< indentStreamable(offset: 2) + if i != 0 { stream.send(",").send(shouldIndent ? "\n" : " ") } + stream.send(indentStreamable(offset: 2)) item.write(to: stream, indent: indent.flatMap({ $0 + 2 })) } - stream <<< (shouldIndent ? "\n" : "") <<< indentStreamable() <<< "]" + stream.send(shouldIndent ? "\n" : "").send(indentStreamable()).send("]") case .dictionary(let contents): // We always output in a deterministic order. - stream <<< "{" <<< (shouldIndent ? "\n" : "") + stream.send("{").send(shouldIndent ? "\n" : "") for (i, key) in contents.keys.sorted().enumerated() { - if i != 0 { stream <<< "," <<< (shouldIndent ? "\n" : " ") } - stream <<< indentStreamable(offset: 2) <<< Format.asJSON(key) <<< ": " + if i != 0 { stream.send(",").send(shouldIndent ? "\n" : " ") } + stream.send(indentStreamable(offset: 2)).send(Format.asJSON(key)).send(": ") contents[key]!.write(to: stream, indent: indent.flatMap({ $0 + 2 })) } - stream <<< (shouldIndent ? "\n" : "") <<< indentStreamable() <<< "}" + stream.send(shouldIndent ? "\n" : "").send(indentStreamable()).send("}") case .orderedDictionary(let contents): - stream <<< "{" <<< (shouldIndent ? "\n" : "") + stream.send("{").send(shouldIndent ? "\n" : "") for (i, item) in contents.enumerated() { - if i != 0 { stream <<< "," <<< (shouldIndent ? "\n" : " ") } - stream <<< indentStreamable(offset: 2) <<< Format.asJSON(item.key) <<< ": " + if i != 0 { stream.send(",").send(shouldIndent ? "\n" : " ") } + stream.send(indentStreamable(offset: 2)).send(Format.asJSON(item.key)).send(": ") item.value.write(to: stream, indent: indent.flatMap({ $0 + 2 })) } - stream <<< (shouldIndent ? "\n" : "") <<< indentStreamable() <<< "}" + stream.send(shouldIndent ? "\n" : "").send(indentStreamable()).send("}") } } } @@ -171,7 +171,7 @@ import Foundation enum JSONDecodingError: Error { /// The input byte string is malformed. - case malformed + case malformed(json: String, underlyingError: Error) } extension JSONDecodingError: CustomNSError { @@ -262,7 +262,7 @@ extension JSON { // times. self = JSON.convertToJSON(result) } catch { - throw JSONDecodingError.malformed + throw JSONDecodingError.malformed(json: String(data: data, encoding: .utf8) ?? data.description, underlyingError: error) } } diff --git a/Sources/TSCBasic/Lock.swift b/Sources/TSCBasic/Lock.swift index 278d1086..d87186d8 100644 --- a/Sources/TSCBasic/Lock.swift +++ b/Sources/TSCBasic/Lock.swift @@ -177,8 +177,19 @@ public final class FileLock { defer { unlock() } return try body() } - - public static func withLock(fileToLock: AbsolutePath, lockFilesDirectory: AbsolutePath? = nil, type: LockType = .exclusive, body: () throws -> T) throws -> T { + + /// Execute the given block while holding the lock. + public func withLock(type: LockType = .exclusive, _ body: () async throws -> T) async throws -> T { + try lock(type: type) + defer { unlock() } + return try await body() + } + + private static func prepareLock( + fileToLock: AbsolutePath, + at lockFilesDirectory: AbsolutePath? = nil, + _ type: LockType = .exclusive + ) throws -> FileLock { // unless specified, we use the tempDirectory to store lock files let lockFilesDirectory = try lockFilesDirectory ?? localFileSystem.tempDirectory if !localFileSystem.exists(lockFilesDirectory) { @@ -215,7 +226,26 @@ public final class FileLock { #endif let lockFilePath = lockFilesDirectory.appending(component: lockFileName) - let lock = FileLock(at: lockFilePath) + return FileLock(at: lockFilePath) + } + + public static func withLock( + fileToLock: AbsolutePath, + lockFilesDirectory: AbsolutePath? = nil, + type: LockType = .exclusive, + body: () throws -> T + ) throws -> T { + let lock = try Self.prepareLock(fileToLock: fileToLock, at: lockFilesDirectory, type) return try lock.withLock(type: type, body) } + + public static func withLock( + fileToLock: AbsolutePath, + lockFilesDirectory: AbsolutePath? = nil, + type: LockType = .exclusive, + body: () async throws -> T + ) async throws -> T { + let lock = try Self.prepareLock(fileToLock: fileToLock, at: lockFilesDirectory, type) + return try await lock.withLock(type: type, body) + } } diff --git a/Sources/TSCBasic/Path.swift b/Sources/TSCBasic/Path.swift index 9fed3e1b..3ceb2252 100644 --- a/Sources/TSCBasic/Path.swift +++ b/Sources/TSCBasic/Path.swift @@ -90,7 +90,7 @@ public struct AbsolutePath: Hashable, Sendable { self.init(String(decodingCString: pwszResult, as: UTF16.self)) #else - self.init(basePath, RelativePath(str)) + try self.init(basePath, RelativePath(validating: str)) #endif } } @@ -102,8 +102,8 @@ public struct AbsolutePath: Hashable, Sendable { } /// Convenience initializer that appends a string to a relative path. - public init(_ absPath: AbsolutePath, _ relStr: String) { - self.init(absPath, RelativePath(relStr)) + public init(_ absPath: AbsolutePath, validating relStr: String) throws { + try self.init(absPath, RelativePath(validating: relStr)) } /// Initializes the AbsolutePath from `absStr`, which must be an absolute @@ -240,23 +240,6 @@ public struct RelativePath: Hashable, Sendable { _impl = impl } - /// Private initializer for constructing a relative path without performing - /// normalization or canonicalization. This will construct a path without - /// an anchor and thus may be invalid. - fileprivate init(unsafeUncheckedPath string: String) { - self.init(PathImpl(string: string)) - } - - /// Initializes the RelativePath from `str`, which must be a relative path - /// (which means that it must not begin with a path separator or a tilde). - /// An empty input path is allowed, but will be normalized to a single `.` - /// character. The input string will be normalized if needed, as described - /// in the documentation for RelativePath. - public init(_ string: String) { - // Normalize the relative string and store it as our Path. - self.init(PathImpl(normalizingRelativePath: string)) - } - /// Convenience initializer that verifies that the path is relative. public init(validating path: String) throws { try self.init(PathImpl(validatingRelativePath: path)) @@ -429,12 +412,6 @@ protocol Path: Hashable { /// Creates a path from its normalized string representation. init(string: String) - /// Creates a path from an absolute string representation and normalizes it. - init(normalizingAbsolutePath: String) - - /// Creates a path from an relative string representation and normalizes it. - init(normalizingRelativePath: String) - /// Creates a path from a string representation, validates that it is a valid absolute path and normalizes it. init(validatingAbsolutePath: String) throws @@ -524,57 +501,41 @@ private struct WindowsPath: Path, Sendable { } init(string: String) { - self.string = string - } - - init(normalizingAbsolutePath path: String) { - let normalized: UnsafePointer = path.fileSystemRepresentation - defer { normalized.deallocate() } - - self.init(string: String(cString: normalized) - .withCString(encodedAs: UTF16.self) { pwszPath in - var canonical: PWSTR! - _ = PathAllocCanonicalize(pwszPath, - ULONG(PATHCCH_ALLOW_LONG_PATHS.rawValue), - &canonical) - return String(decodingCString: canonical, as: UTF16.self) - }) - } - - init(normalizingRelativePath path: String) { - if path.isEmpty || path == "." { - self.init(string: ".") + if string.first?.isASCII ?? false, string.first?.isLetter ?? false, string.first?.isLowercase ?? false, + string.count > 1, string[string.index(string.startIndex, offsetBy: 1)] == ":" + { + self.string = "\(string.first!.uppercased())\(string.dropFirst(1))" } else { - var buffer: [WCHAR] = Array(repeating: 0, count: Int(MAX_PATH + 1)) - _ = path.replacingOccurrences(of: "/", with: "\\").withCString(encodedAs: UTF16.self) { - PathCanonicalizeW(&buffer, $0) - } - self.init(string: String(decodingCString: buffer, as: UTF16.self)) + self.string = string } } - init(validatingAbsolutePath path: String) throws { - let fsr: UnsafePointer = path.fileSystemRepresentation - defer { fsr.deallocate() } + private static func repr(_ path: String) -> String { + guard !path.isEmpty else { return "" } + let representation: UnsafePointer = path.fileSystemRepresentation + defer { representation.deallocate() } + return String(cString: representation) + } - let realpath = String(cString: fsr) + init(validatingAbsolutePath path: String) throws { + let realpath = Self.repr(path) if !Self.isAbsolutePath(realpath) { throw PathValidationError.invalidAbsolutePath(path) } - self.init(normalizingAbsolutePath: path) + self.init(string: realpath) } init(validatingRelativePath path: String) throws { - let fsr: UnsafePointer = path.fileSystemRepresentation - defer { fsr.deallocate() } - - let realpath: String = String(cString: fsr) - // Treat a relative path as an invalid relative path... - if Self.isAbsolutePath(realpath) || - realpath.first == "~" || realpath.first == "\\" { - throw PathValidationError.invalidRelativePath(path) + if path.isEmpty || path == "." { + self.init(string: ".") + } else { + let realpath: String = Self.repr(path) + // Treat a relative path as an invalid relative path... + if Self.isAbsolutePath(realpath) || realpath.first == "\\" { + throw PathValidationError.invalidRelativePath(path) + } + self.init(string: realpath) } - self.init(normalizingRelativePath: path) } func suffix(withDot: Bool) -> String? { @@ -596,7 +557,7 @@ private struct WindowsPath: Path, Sendable { } } defer { LocalFree(result) } - return PathImpl(string: String(decodingCString: result!, as: UTF16.self)) + return Self(string: String(decodingCString: result!, as: UTF16.self)) } func appending(relativePath: Self) -> Self { @@ -607,7 +568,7 @@ private struct WindowsPath: Path, Sendable { } } defer { LocalFree(result) } - return PathImpl(string: String(decodingCString: result!, as: UTF16.self)) + return Self(string: String(decodingCString: result!, as: UTF16.self)) } } #else @@ -829,7 +790,7 @@ private struct UNIXPath: Path, Sendable { init(validatingRelativePath path: String) throws { switch path.first { - case "/", "~": + case "/": throw PathValidationError.invalidRelativePath(path) default: self.init(normalizingRelativePath: path) @@ -874,9 +835,9 @@ private struct UNIXPath: Path, Sendable { } if self == Self.root { - return PathImpl(string: "/" + name) + return Self(string: "/" + name) } else { - return PathImpl(string: string + "/" + name) + return Self(string: string + "/" + name) } } @@ -899,12 +860,12 @@ private struct UNIXPath: Path, Sendable { // the beginning of the path only. if relativePathString.hasPrefix(".") { if newPathString.hasPrefix("/") { - return PathImpl(normalizingAbsolutePath: newPathString) + return Self(normalizingAbsolutePath: newPathString) } else { - return PathImpl(normalizingRelativePath: newPathString) + return Self(normalizingRelativePath: newPathString) } } else { - return PathImpl(string: newPathString) + return Self(string: newPathString) } } } @@ -925,7 +886,7 @@ extension PathValidationError: CustomStringConvertible { case .invalidAbsolutePath(let path): return "invalid absolute path '\(path)'" case .invalidRelativePath(let path): - return "invalid relative path '\(path)'; relative path should not begin with '\(AbsolutePath.root.pathString)' or '~'" + return "invalid relative path '\(path)'; relative path should not begin with '\(AbsolutePath.root.pathString)'" } } } @@ -955,10 +916,16 @@ extension AbsolutePath { // might be an empty path (when self and the base are equal). let relComps = pathComps.dropFirst(baseComps.count) #if os(Windows) - result = RelativePath(unsafeUncheckedPath: relComps.joined(separator: "\\")) + let pathString = relComps.joined(separator: "\\") #else - result = RelativePath(relComps.joined(separator: "/")) + let pathString = relComps.joined(separator: "/") #endif + do { + result = try RelativePath(validating: pathString) + } catch { + preconditionFailure("invalid relative path computed from \(pathString)") + } + } else { // General case, in which we might well need `..` components to go // "up" before we can go "down" the directory tree. @@ -974,10 +941,15 @@ extension AbsolutePath { var relComps = Array(repeating: "..", count: newBaseComps.count) relComps.append(contentsOf: newPathComps) #if os(Windows) - result = RelativePath(unsafeUncheckedPath: relComps.joined(separator: "\\")) + let pathString = relComps.joined(separator: "\\") #else - result = RelativePath(relComps.joined(separator: "/")) + let pathString = relComps.joined(separator: "/") #endif + do { + result = try RelativePath(validating: pathString) + } catch { + preconditionFailure("invalid relative path computed from \(pathString)") + } } assert(AbsolutePath(base, result) == self) @@ -1064,13 +1036,31 @@ private func mayNeedNormalization(absolute string: String) -> Bool { // MARK: - `AbsolutePath` backwards compatibility, delete after deprecation period. extension AbsolutePath { + @_disfavoredOverload @available(*, deprecated, message: "use throwing `init(validating:)` variant instead") public init(_ absStr: String) { try! self.init(validating: absStr) } + @_disfavoredOverload @available(*, deprecated, message: "use throwing `init(validating:relativeTo:)` variant instead") public init(_ str: String, relativeTo basePath: AbsolutePath) { try! self.init(validating: str, relativeTo: basePath) } + + @_disfavoredOverload + @available(*, deprecated, message: "use throwing variant instead") + public init(_ absPath: AbsolutePath, _ relStr: String) { + try! self.init(absPath, validating: relStr) + } +} + +// MARK: - `AbsolutePath` backwards compatibility, delete after deprecation period. + +extension RelativePath { + @_disfavoredOverload + @available(*, deprecated, message: "use throwing variant instead") + public init(_ string: String) { + try! self.init(validating: string) + } } diff --git a/Sources/TSCBasic/Process.swift b/Sources/TSCBasic/Process.swift index 8d7111d4..6c8aa117 100644 --- a/Sources/TSCBasic/Process.swift +++ b/Sources/TSCBasic/Process.swift @@ -389,7 +389,7 @@ public final class Process { outputRedirection: outputRedirection, startNewProcessGroup: startNewProcessGroup, loggingHandler: verbose ? { message in - stdoutStream <<< message <<< "\n" + stdoutStream.send(message).send("\n") stdoutStream.flush() } : nil ) @@ -529,6 +529,9 @@ public final class Process { let process = Foundation.Process() _process = process process.arguments = Array(arguments.dropFirst()) // Avoid including the executable URL twice. + if let workingDirectory { + process.currentDirectoryURL = workingDirectory.asURL + } process.executableURL = executablePath.asURL process.environment = environment @@ -1276,13 +1279,13 @@ extension ProcessResult.Error: CustomStringConvertible { let stream = BufferedOutputByteStream() switch result.exitStatus { case .terminated(let code): - stream <<< "terminated(\(code)): " + stream.send("terminated(\(code)): ") #if os(Windows) case .abnormal(let exception): - stream <<< "abnormal(\(exception)): " + stream.send("abnormal(\(exception)): ") #else case .signalled(let signal): - stream <<< "signalled(\(signal)): " + stream.send("signalled(\(signal)): ") #endif } @@ -1292,15 +1295,15 @@ extension ProcessResult.Error: CustomStringConvertible { if args.first == "sandbox-exec", args.count > 3 { args = args.suffix(from: 3).map({$0}) } - stream <<< args.map({ $0.spm_shellEscaped() }).joined(separator: " ") + stream.send(args.map({ $0.spm_shellEscaped() }).joined(separator: " ")) // Include the output, if present. if let output = try? result.utf8Output() + result.utf8stderrOutput() { // We indent the output to keep it visually separated from everything else. let indentation = " " - stream <<< " output:\n" <<< indentation <<< output.replacingOccurrences(of: "\n", with: "\n" + indentation) + stream.send(" output:\n").send(indentation).send(output.replacingOccurrences(of: "\n", with: "\n" + indentation)) if !output.hasSuffix("\n") { - stream <<< "\n" + stream.send("\n") } } @@ -1333,7 +1336,7 @@ extension FileHandle: WritableByteStream { extension Process { @available(*, deprecated) fileprivate static func logToStdout(_ message: String) { - stdoutStream <<< message <<< "\n" + stdoutStream.send(message).send("\n") stdoutStream.flush() } } diff --git a/Sources/TSCBasic/TerminalController.swift b/Sources/TSCBasic/TerminalController.swift index 001e62a2..19556e58 100644 --- a/Sources/TSCBasic/TerminalController.swift +++ b/Sources/TSCBasic/TerminalController.swift @@ -166,13 +166,13 @@ public final class TerminalController { /// Clears the current line and moves the cursor to beginning of the line.. public func clearLine() { - stream <<< clearLineString <<< "\r" + stream.send(clearLineString).send("\r") flush() } /// Moves the cursor y columns up. public func moveCursor(up: Int) { - stream <<< "\u{001B}[\(up)A" + stream.send("\u{001B}[\(up)A") flush() } @@ -184,7 +184,7 @@ public final class TerminalController { /// Inserts a new line character into the stream. public func endLine() { - stream <<< "\n" + stream.send("\n") flush() } @@ -198,9 +198,9 @@ public final class TerminalController { private func writeWrapped(_ string: String, inColor color: Color, bold: Bool = false, stream: WritableByteStream) { // Don't wrap if string is empty or color is no color. guard !string.isEmpty && color != .noColor else { - stream <<< string + stream.send(string) return } - stream <<< color.string <<< (bold ? boldString : "") <<< string <<< resetString + stream.send(color.string).send(bold ? boldString : "").send(string).send(resetString) } } diff --git a/Sources/TSCBasic/WritableByteStream.swift b/Sources/TSCBasic/WritableByteStream.swift index 0a5d63c2..aee907e3 100644 --- a/Sources/TSCBasic/WritableByteStream.swift +++ b/Sources/TSCBasic/WritableByteStream.swift @@ -27,11 +27,11 @@ public protocol ByteStreamable { /// different output destinations, e.g., a file or an in memory buffer. This is /// loosely modeled on LLVM's llvm::raw_ostream class. /// -/// The stream is generally used in conjunction with the custom streaming -/// operator '<<<'. For example: +/// The stream is generally used in conjunction with the `appending` function. +/// For example: /// /// let stream = BufferedOutputByteStream() -/// stream <<< "Hello, world!" +/// stream.appending("Hello, world!") /// /// would write the UTF8 encoding of "Hello, world!" to the stream. /// @@ -39,7 +39,7 @@ public protocol ByteStreamable { /// in the `Format` struct (used for namespacing purposes). For example: /// /// let items = ["hello", "world"] -/// stream <<< Format.asSeparatedList(items, separator: " ") +/// stream.appending(Format.asSeparatedList(items, separator: " ")) /// /// would write each item in the list to the stream, separating them with a /// space. @@ -66,7 +66,7 @@ public extension WritableByteStream { // Public alias to the old name to not introduce API compatibility. public typealias OutputByteStream = WritableByteStream -#if os(Android) +#if os(Android) || canImport(Musl) public typealias FILEPointer = OpaquePointer #else public typealias FILEPointer = UnsafeMutablePointer @@ -137,6 +137,34 @@ extension WritableByteStream { } } } + + // MARK: helpers that return `self` + + // FIXME: This override shouldn't be necesary but removing it causes a 30% performance regression. This problem is + // tracked by the following bug: https://bugs.swift.org/browse/SR-8535 + @discardableResult + public func send(_ value: ArraySlice) -> WritableByteStream { + value.write(to: self) + return self + } + + @discardableResult + public func send(_ value: ByteStreamable) -> WritableByteStream { + value.write(to: self) + return self + } + + @discardableResult + public func send(_ value: CustomStringConvertible) -> WritableByteStream { + value.description.write(to: self) + return self + } + + @discardableResult + public func send(_ value: ByteStreamable & CustomStringConvertible) -> WritableByteStream { + value.write(to: self) + return self + } } /// The `WritableByteStream` base class. @@ -366,24 +394,29 @@ precedencegroup StreamingPrecedence { // FIXME: This override shouldn't be necesary but removing it causes a 30% performance regression. This problem is // tracked by the following bug: https://bugs.swift.org/browse/SR-8535 + +@available(*, deprecated, message: "use send(_:) function on WritableByteStream instead") @discardableResult public func <<< (stream: WritableByteStream, value: ArraySlice) -> WritableByteStream { value.write(to: stream) return stream } +@available(*, deprecated, message: "use send(_:) function on WritableByteStream instead") @discardableResult public func <<< (stream: WritableByteStream, value: ByteStreamable) -> WritableByteStream { value.write(to: stream) return stream } +@available(*, deprecated, message: "use send(_:) function on WritableByteStream instead") @discardableResult public func <<< (stream: WritableByteStream, value: CustomStringConvertible) -> WritableByteStream { value.description.write(to: stream) return stream } +@available(*, deprecated, message: "use send(_:) function on WritableByteStream instead") @discardableResult public func <<< (stream: WritableByteStream, value: ByteStreamable & CustomStringConvertible) -> WritableByteStream { value.write(to: stream) @@ -450,7 +483,7 @@ public struct Format { let value: Bool func write(to stream: WritableByteStream) { - stream <<< (value ? "true" : "false") + stream.send(value ? "true" : "false") } } @@ -463,7 +496,7 @@ public struct Format { func write(to stream: WritableByteStream) { // FIXME: Diagnose integers which cannot be represented in JSON. - stream <<< value.description + stream.send(value.description) } } @@ -478,7 +511,7 @@ public struct Format { // FIXME: What should we do about NaN, etc.? // // FIXME: Is Double.debugDescription the best representation? - stream <<< value.debugDescription + stream.send(value.debugDescription) } } @@ -494,9 +527,9 @@ public struct Format { let value: String func write(to stream: WritableByteStream) { - stream <<< UInt8(ascii: "\"") + stream.send(UInt8(ascii: "\"")) stream.writeJSONEscaped(value) - stream <<< UInt8(ascii: "\"") + stream.send(UInt8(ascii: "\"")) } } @@ -514,12 +547,12 @@ public struct Format { let items: [String] func write(to stream: WritableByteStream) { - stream <<< UInt8(ascii: "[") + stream.send(UInt8(ascii: "[")) for (i, item) in items.enumerated() { - if i != 0 { stream <<< "," } - stream <<< Format.asJSON(item) + if i != 0 { stream.send(",") } + stream.send(Format.asJSON(item)) } - stream <<< UInt8(ascii: "]") + stream.send(UInt8(ascii: "]")) } } @@ -531,12 +564,12 @@ public struct Format { let items: [String: String] func write(to stream: WritableByteStream) { - stream <<< UInt8(ascii: "{") + stream.send(UInt8(ascii: "{")) for (offset: i, element: (key: key, value: value)) in items.enumerated() { - if i != 0 { stream <<< "," } - stream <<< Format.asJSON(key) <<< ":" <<< Format.asJSON(value) + if i != 0 { stream.send(",") } + stream.send(Format.asJSON(key)).send(":").send(Format.asJSON(value)) } - stream <<< UInt8(ascii: "}") + stream.send(UInt8(ascii: "}")) } } @@ -551,12 +584,12 @@ public struct Format { let transform: (T) -> String func write(to stream: WritableByteStream) { - stream <<< UInt8(ascii: "[") + stream.send(UInt8(ascii: "[")) for (i, item) in items.enumerated() { - if i != 0 { stream <<< "," } - stream <<< Format.asJSON(transform(item)) + if i != 0 { stream.send(",") } + stream.send(Format.asJSON(transform(item))) } - stream <<< UInt8(ascii: "]") + stream.send(UInt8(ascii: "]")) } } @@ -572,10 +605,10 @@ public struct Format { for (i, item) in items.enumerated() { // Add the separator, if necessary. if i != 0 { - stream <<< separator + stream.send(separator) } - stream <<< item + stream.send(item) } } } @@ -596,8 +629,8 @@ public struct Format { func write(to stream: WritableByteStream) { for (i, item) in items.enumerated() { - if i != 0 { stream <<< separator } - stream <<< transform(item) + if i != 0 { stream.send(separator) } + stream.send(transform(item)) } } } @@ -617,7 +650,7 @@ public struct Format { func write(to stream: WritableByteStream) { for _ in 0..= maxWidth - padding { - stream <<< argument <<< "\n" + stream.send(argument).send("\n") // Align full width because usage is to be printed on a new line. - stream <<< Format.asRepeating(string: " ", count: maxWidth + padding) + stream.send(Format.asRepeating(string: " ", count: maxWidth + padding)) } else { - stream <<< argument + stream.send(argument) // Align to the remaining empty space on the line. - stream <<< Format.asRepeating(string: " ", count: maxWidth - count) + stream.send(Format.asRepeating(string: " ", count: maxWidth - count)) } - stream <<< usage + stream.send(usage) } - stream <<< "OVERVIEW: " <<< overview + stream.send("OVERVIEW: ").send(overview) if !usage.isEmpty { - stream <<< "\n\n" + stream.send("\n\n") // Get the binary name from command line arguments. let defaultCommandName = CommandLine.arguments[0].components(separatedBy: "/").last! - stream <<< "USAGE: " <<< (commandName ?? defaultCommandName) <<< " " <<< usage + stream.send("USAGE: ").send(commandName ?? defaultCommandName).send(" ").send(usage) } if optionArguments.count > 0 { - stream <<< "\n\n" - stream <<< "OPTIONS:" + stream.send("\n\nOPTIONS:") for argument in optionArguments.lazy.sorted(by: { $0.name < $1.name }) { guard let usage = argument.usage else { continue } // Create name with its shortname, if available. @@ -997,8 +996,7 @@ public final class ArgumentParser { } if subparsers.keys.count > 0 { - stream <<< "\n\n" - stream <<< "SUBCOMMANDS:" + stream.send("\n\nSUBCOMMANDS:") for (command, parser) in subparsers.sorted(by: { $0.key < $1.key }) { // Special case for hidden subcommands. guard !parser.overview.isEmpty else { continue } @@ -1007,8 +1005,7 @@ public final class ArgumentParser { } if positionalArguments.count > 0 { - stream <<< "\n\n" - stream <<< "POSITIONAL ARGUMENTS:" + stream.send("\n\nPOSITIONAL ARGUMENTS:") for argument in positionalArguments { guard let usage = argument.usage else { continue } print(formatted: argument.name, usage: usage, on: stream) @@ -1016,11 +1013,11 @@ public final class ArgumentParser { } if let seeAlso = seeAlso { - stream <<< "\n\n" - stream <<< "SEE ALSO: \(seeAlso)" + stream.send("\n\n") + stream.send("SEE ALSO: \(seeAlso)") } - stream <<< "\n" + stream.send("\n") stream.flush() } } diff --git a/Sources/TSCUtility/ArgumentParserShellCompletion.swift b/Sources/TSCUtility/ArgumentParserShellCompletion.swift index be772987..1b714b65 100644 --- a/Sources/TSCUtility/ArgumentParserShellCompletion.swift +++ b/Sources/TSCUtility/ArgumentParserShellCompletion.swift @@ -29,18 +29,18 @@ extension ArgumentParser { switch shell { case .bash: // Information about how to include this function in a completion script. - stream <<< """ + stream.send(""" # Generates completions for \(commandName) # # Parameters # - the start position of this parser; set to 1 if unknown - """ + """) generateBashSwiftTool(name: name, on: stream) case .zsh: // Information about how to include this function in a completion script. - stream <<< """ + stream.send(""" # Generates completions for \(commandName) # # In the final compdef file, set the following file header: @@ -49,7 +49,7 @@ extension ArgumentParser { # local context state state_descr line # typeset -A opt_args - """ + """) generateZshSwiftTool(name: name, on: stream) } @@ -59,11 +59,11 @@ extension ArgumentParser { // MARK: - BASH fileprivate func generateBashSwiftTool(name: String, on stream: WritableByteStream) { - stream <<< """ + stream.send(""" function \(name) { - """ + """) // Suggest positional arguments. Beware that this forces positional arguments // before options. For example [swift package pin ] expects a name as the @@ -71,13 +71,13 @@ extension ArgumentParser { // the positional argument; [swift package pin MyPackage ] will list them // just fine. for (index, argument) in positionalArguments.enumerated() { - stream <<< " if [[ $COMP_CWORD == $(($1+\(index))) ]]; then\n" + stream.send(" if [[ $COMP_CWORD == $(($1+\(index))) ]]; then\n") generateBashCompletion(argument, on: stream) - stream <<< " fi\n" + stream.send(" fi\n") } // Suggest subparsers in addition to other arguments. - stream <<< " if [[ $COMP_CWORD == $1 ]]; then\n" + stream.send(" if [[ $COMP_CWORD == $1 ]]; then\n") var completions = [String]() for (subName, _) in subparsers { completions.append(subName) @@ -88,37 +88,37 @@ extension ArgumentParser { completions.append(shortName) } } - stream <<< """ + stream.send(""" COMPREPLY=( $(compgen -W "\(completions.joined(separator: " "))" -- $cur) ) return fi - """ + """) // Suggest completions based on previous word. generateBashCasePrev(on: stream) // Forward completions to subparsers. - stream <<< " case ${COMP_WORDS[$1]} in\n" + stream.send(" case ${COMP_WORDS[$1]} in\n") for (subName, _) in subparsers { - stream <<< """ + stream.send(""" (\(subName)) \(name)_\(subName) $(($1+1)) return ;; - """ + """) } - stream <<< " esac\n" + stream.send(" esac\n") // In all other cases (no positional / previous / subparser), suggest // this parsers completions. - stream <<< """ + stream.send(""" COMPREPLY=( $(compgen -W "\(completions.joined(separator: " "))" -- $cur) ) } - """ + """) for (subName, subParser) in subparsers { subParser.generateBashSwiftTool(name: "\(name)_\(subName)", on: stream) @@ -126,42 +126,42 @@ extension ArgumentParser { } fileprivate func generateBashCasePrev(on stream: WritableByteStream) { - stream <<< " case $prev in\n" + stream.send(" case $prev in\n") for argument in optionArguments { let flags = [argument.name] + (argument.shortName.map({ [$0] }) ?? []) - stream <<< " (\(flags.joined(separator: "|")))\n" + stream.send(" (\(flags.joined(separator: "|")))\n") generateBashCompletion(argument, on: stream) - stream <<< " ;;\n" + stream.send(" ;;\n") } - stream <<< " esac\n" + stream.send(" esac\n") } fileprivate func generateBashCompletion(_ argument: AnyArgument, on stream: WritableByteStream) { switch argument.completion { case .none: // return; no value to complete - stream <<< " return\n" + stream.send(" return\n") case .unspecified: break case .values(let values): let x = values.map({ $0.value }).joined(separator: " ") - stream <<< """ + stream.send(""" COMPREPLY=( $(compgen -W "\(x)" -- $cur) ) return - """ + """) case .filename: - stream <<< """ + stream.send(""" _filedir return - """ + """) case .function(let name): - stream <<< """ + stream.send(""" \(name) return - """ + """) } } @@ -169,15 +169,15 @@ extension ArgumentParser { private func generateZshSwiftTool(name: String, on stream: WritableByteStream) { // Completions are provided by zsh's _arguments builtin. - stream <<< """ + stream.send(""" \(name)() { arguments=( - """ + """) for argument in positionalArguments { - stream <<< " \"" + stream.send(" \"") generateZshCompletion(argument, on: stream) - stream <<< "\"\n" + stream.send("\"\n") } for argument in optionArguments { generateZshArgument(argument, on: stream) @@ -185,58 +185,58 @@ extension ArgumentParser { // Use a simple state-machine when dealing with sub parsers. if subparsers.count > 0 { - stream <<< """ + stream.send(""" '(-): :->command' '(-)*:: :->arg' - """ + """) } - stream <<< """ + stream.send(""" ) _arguments $arguments && return - """ + """) // Handle the state set by the state machine. if subparsers.count > 0 { - stream <<< """ + stream.send(""" case $state in (command) local modes modes=( - """ + """) for (subName, subParser) in subparsers { - stream <<< """ + stream.send(""" '\(subName):\(subParser.overview)' - """ + """) } - stream <<< """ + stream.send(""" ) _describe "mode" modes ;; (arg) case ${words[1]} in - """ + """) for (subName, _) in subparsers { - stream <<< """ + stream.send(""" (\(subName)) \(name)_\(subName) ;; - """ + """) } - stream <<< """ + stream.send(""" esac ;; esac - """ + """) } - stream <<< "}\n\n" + stream.send("}\n\n") for (subName, subParser) in subparsers { subParser.generateZshSwiftTool(name: "\(name)_\(subName)", on: stream) @@ -245,10 +245,10 @@ extension ArgumentParser { /// Generates an option argument for `_arguments`, complete with description and completion values. fileprivate func generateZshArgument(_ argument: AnyArgument, on stream: WritableByteStream) { - stream <<< " \"" + stream.send(" \"") switch argument.shortName { - case .none: stream <<< "\(argument.name)" - case let shortName?: stream <<< "(\(argument.name) \(shortName))\"{\(argument.name),\(shortName)}\"" + case .none: stream.send("\(argument.name)") + case let shortName?: stream.send("(\(argument.name) \(shortName))\"{\(argument.name),\(shortName)}\"") } let description = removeDefaultRegex @@ -256,10 +256,10 @@ extension ArgumentParser { .replacingOccurrences(of: "\"", with: "\\\"") .replacingOccurrences(of: "[", with: "\\[") .replacingOccurrences(of: "]", with: "\\]") - stream <<< "[\(description)]" + stream.send("[\(description)]") generateZshCompletion(argument, on: stream) - stream <<< "\"\n" + stream.send("\"\n") } /// Generates completion values, as part of an item for `_arguments`. @@ -269,16 +269,16 @@ extension ArgumentParser { .replacingOccurrences(of: "\"", with: "\\\"") switch argument.completion { - case .none: stream <<< ":\(message): " + case .none: stream.send(":\(message): ") case .unspecified: break - case .filename: stream <<< ":\(message):_files" + case .filename: stream.send(":\(message):_files") case let .values(values): - stream <<< ": :{_values ''" + stream.send(": :{_values ''") for (value, description) in values { - stream <<< " '\(value)[\(description)]'" + stream.send(" '\(value)[\(description)]'") } - stream <<< "}" - case .function(let name): stream <<< ":\(message):\(name)" + stream.send("}") + case .function(let name): stream.send(":\(message):\(name)") } } } diff --git a/Sources/TSCUtility/Diagnostics.swift b/Sources/TSCUtility/Diagnostics.swift index 5b869044..8bd54548 100644 --- a/Sources/TSCUtility/Diagnostics.swift +++ b/Sources/TSCUtility/Diagnostics.swift @@ -208,9 +208,9 @@ public enum PackageLocation { public var description: String { let stream = BufferedOutputByteStream() if let name = name { - stream <<< "'\(name)' " + stream.send("'\(name)' ") } - stream <<< packagePath + stream.send(packagePath) return stream.bytes.description } } diff --git a/Sources/TSCUtility/FSWatch.swift b/Sources/TSCUtility/FSWatch.swift index b0a4cd8e..5cea437b 100644 --- a/Sources/TSCUtility/FSWatch.swift +++ b/Sources/TSCUtility/FSWatch.swift @@ -54,7 +54,7 @@ public class FSWatch { self._watcher = NoOpWatcher(paths: paths, latency: latency, delegate: _WatcherDelegate(block: block)) #elseif os(Windows) self._watcher = RDCWatcher(paths: paths, latency: latency, delegate: _WatcherDelegate(block: block)) - #elseif canImport(Glibc) + #elseif canImport(Glibc) || canImport(Musl) var ipaths: [AbsolutePath: Inotify.WatchOptions] = [:] // FIXME: We need to recurse here. @@ -106,7 +106,7 @@ extension NoOpWatcher: _FileWatcher{} #elseif os(Windows) extension FSWatch._WatcherDelegate: RDCWatcherDelegate {} extension RDCWatcher: _FileWatcher {} -#elseif canImport(Glibc) +#elseif canImport(Glibc) || canImport(Musl) extension FSWatch._WatcherDelegate: InotifyDelegate {} extension Inotify: _FileWatcher{} #elseif os(macOS) @@ -296,7 +296,7 @@ public final class RDCWatcher { } } -#elseif canImport(Glibc) +#elseif canImport(Glibc) || canImport(Musl) /// The delegate for receiving inotify events. public protocol InotifyDelegate { @@ -621,7 +621,7 @@ public final class Inotify { // FIXME: Swift should provide shims for FD_ macros private func FD_ZERO(_ set: inout fd_set) { - #if os(Android) + #if os(Android) || canImport(Musl) #if arch(arm) set.fds_bits = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) @@ -641,7 +641,7 @@ private func FD_ZERO(_ set: inout fd_set) { private func FD_SET(_ fd: Int32, _ set: inout fd_set) { let intOffset = Int(fd / 16) let bitOffset = Int(fd % 16) - #if os(Android) + #if os(Android) || canImport(Musl) var fd_bits = set.fds_bits let mask: UInt = 1 << bitOffset #else @@ -685,7 +685,7 @@ private func FD_SET(_ fd: Int32, _ set: inout fd_set) { #endif default: break } - #if os(Android) + #if os(Android) || canImport(Musl) set.fds_bits = fd_bits #else set.__fds_bits = fd_bits @@ -695,7 +695,7 @@ private func FD_SET(_ fd: Int32, _ set: inout fd_set) { private func FD_ISSET(_ fd: Int32, _ set: inout fd_set) -> Bool { let intOffset = Int(fd / 32) let bitOffset = Int(fd % 32) - #if os(Android) + #if os(Android) || canImport(Musl) let fd_bits = set.fds_bits let mask: UInt = 1 << bitOffset #else diff --git a/Sources/TSCUtility/IndexStore.swift b/Sources/TSCUtility/IndexStore.swift index 8f633966..7ba5b07b 100644 --- a/Sources/TSCUtility/IndexStore.swift +++ b/Sources/TSCUtility/IndexStore.swift @@ -457,7 +457,7 @@ private struct _DLOpenFlags: RawRepresentable, OptionSet { public static let deepBind: _DLOpenFlags = _DLOpenFlags(rawValue: 0) #else public static let first: _DLOpenFlags = _DLOpenFlags(rawValue: 0) - #if os(Linux) + #if os(Linux) && canImport(Glibc) public static let deepBind: _DLOpenFlags = _DLOpenFlags(rawValue: RTLD_DEEPBIND) #else public static let deepBind: _DLOpenFlags = _DLOpenFlags(rawValue: 0) diff --git a/Sources/TSCUtility/InterruptHandler.swift b/Sources/TSCUtility/InterruptHandler.swift index 8978f83d..c10d4fe1 100644 --- a/Sources/TSCUtility/InterruptHandler.swift +++ b/Sources/TSCUtility/InterruptHandler.swift @@ -8,6 +8,8 @@ See http://swift.org/CONTRIBUTORS.txt for Swift project authors */ +#if !canImport(Musl) + import TSCLibc import TSCBasic @@ -135,3 +137,5 @@ public final class InterruptHandler { thread.join() } } + +#endif diff --git a/Sources/TSCUtility/ProgressAnimation.swift b/Sources/TSCUtility/ProgressAnimation.swift index 5651d814..c0383531 100644 --- a/Sources/TSCUtility/ProgressAnimation.swift +++ b/Sources/TSCUtility/ProgressAnimation.swift @@ -42,8 +42,8 @@ public final class SingleLinePercentProgressAnimation: ProgressAnimationProtocol public func update(step: Int, total: Int, text: String) { if let header = header, !hasDisplayedHeader { - stream <<< header - stream <<< "\n" + stream.send(header) + stream.send("\n") stream.flush() hasDisplayedHeader = true } @@ -51,7 +51,7 @@ public final class SingleLinePercentProgressAnimation: ProgressAnimationProtocol let percentage = step * 100 / total let roundedPercentage = Int(Double(percentage / 10).rounded(.down)) * 10 if percentage != 100, !displayedPercentages.contains(roundedPercentage) { - stream <<< String(roundedPercentage) <<< ".. " + stream.send(String(roundedPercentage)).send(".. ") displayedPercentages.insert(roundedPercentage) } @@ -60,7 +60,7 @@ public final class SingleLinePercentProgressAnimation: ProgressAnimationProtocol public func complete(success: Bool) { if success { - stream <<< "OK" + stream.send("OK") stream.flush() } } @@ -89,8 +89,8 @@ public final class MultiLineNinjaProgressAnimation: ProgressAnimationProtocol { guard text != lastDisplayedText else { return } - stream <<< "[\(step)/\(total)] " <<< text - stream <<< "\n" + stream.send("[\(step)/\(total)] ").send(text) + stream.send("\n") stream.flush() lastDisplayedText = text } @@ -171,15 +171,15 @@ public final class MultiLinePercentProgressAnimation: ProgressAnimationProtocol assert(step <= total) if !hasDisplayedHeader, !header.isEmpty { - stream <<< header - stream <<< "\n" + stream.send(header) + stream.send("\n") stream.flush() hasDisplayedHeader = true } let percentage = step * 100 / total - stream <<< "\(percentage)%: " <<< text - stream <<< "\n" + stream.send("\(percentage)%: ").send(text) + stream.send("\n") stream.flush() lastDisplayedText = text } diff --git a/Sources/TSCUtility/Triple.swift b/Sources/TSCUtility/Triple.swift index 2a207e7d..b1880c14 100644 --- a/Sources/TSCUtility/Triple.swift +++ b/Sources/TSCUtility/Triple.swift @@ -20,6 +20,7 @@ import TSCBasic /// @see Destination.target /// @see https://github.com/apple/swift-llvm/blob/stable/include/llvm/ADT/Triple.h /// +@available(*, deprecated, message: "use `Basics.Triple` if you're libSwiftPM client, create application-specific `Triple` type otherwise") public struct Triple: Encodable, Equatable, Sendable { public let tripleString: String @@ -234,6 +235,7 @@ public struct Triple: Encodable, Equatable, Sendable { } } +@available(*, deprecated) extension Triple { /// The file prefix for dynamic libraries public var dynamicLibraryPrefix: String { @@ -289,6 +291,7 @@ extension Triple { } } +@available(*, deprecated) extension Triple.Error: CustomNSError { public var errorUserInfo: [String : Any] { return [NSLocalizedDescriptionKey: "\(self)"] diff --git a/Sources/TSCUtility/dlopen.swift b/Sources/TSCUtility/dlopen.swift index 3effa0ee..0d101335 100644 --- a/Sources/TSCUtility/dlopen.swift +++ b/Sources/TSCUtility/dlopen.swift @@ -66,7 +66,7 @@ public struct DLOpenFlags: RawRepresentable, OptionSet { public static let deepBind: DLOpenFlags = DLOpenFlags(rawValue: 0) #else public static let first: DLOpenFlags = DLOpenFlags(rawValue: 0) - #if os(Linux) + #if os(Linux) && canImport(Glibc) public static let deepBind: DLOpenFlags = DLOpenFlags(rawValue: RTLD_DEEPBIND) #else public static let deepBind: DLOpenFlags = DLOpenFlags(rawValue: 0) diff --git a/Tests/TSCBasicPerformanceTests/PathPerfTests.swift b/Tests/TSCBasicPerformanceTests/PathPerfTests.swift index 86fc3a27..66fd0fbe 100644 --- a/Tests/TSCBasicPerformanceTests/PathPerfTests.swift +++ b/Tests/TSCBasicPerformanceTests/PathPerfTests.swift @@ -19,7 +19,7 @@ class PathPerfTests: XCTestCasePerf { @available(*, deprecated) func testJoinPerf_X100000() { #if canImport(Darwin) - let absPath = AbsolutePath(path: "/hello/little") + let absPath = AbsolutePath("/hello/little") let relPath = RelativePath("world") let N = 100000 self.measure { diff --git a/Tests/TSCBasicPerformanceTests/SHA256PerfTests.swift b/Tests/TSCBasicPerformanceTests/SHA256PerfTests.swift index eeff7ee8..4709348d 100644 --- a/Tests/TSCBasicPerformanceTests/SHA256PerfTests.swift +++ b/Tests/TSCBasicPerformanceTests/SHA256PerfTests.swift @@ -20,7 +20,7 @@ class SHA256PerfTests: XCTestCasePerf { let byte = "f" let stream = BufferedOutputByteStream() for _ in 0..<20000 { - stream <<< byte + stream.send(byte) } measure { for _ in 0..<1000 { diff --git a/Tests/TSCBasicPerformanceTests/WritableByteStreamPerfTests.swift b/Tests/TSCBasicPerformanceTests/WritableByteStreamPerfTests.swift index aae8d8e5..c732b64a 100644 --- a/Tests/TSCBasicPerformanceTests/WritableByteStreamPerfTests.swift +++ b/Tests/TSCBasicPerformanceTests/WritableByteStreamPerfTests.swift @@ -51,7 +51,7 @@ class OutputByteStreamPerfTests: XCTestCasePerf { for _ in 0..<10 { let stream = BufferedOutputByteStream() for _ in 0..<(1 << 16) { - stream <<< sequence + stream.send(sequence) } XCTAssertEqual(stream.bytes.count, 1 << 20) } @@ -66,7 +66,7 @@ class OutputByteStreamPerfTests: XCTestCasePerf { for _ in 0..<10 { let stream = BufferedOutputByteStream() for _ in 0..<(1 << 20) { - stream <<< byte + stream.send(byte) } XCTAssertEqual(stream.bytes.count, 1 << 20) } @@ -80,7 +80,7 @@ class OutputByteStreamPerfTests: XCTestCasePerf { for _ in 0..<1 { let stream = BufferedOutputByteStream() for _ in 0..<(1 << 20) { - stream <<< Character("X") + stream.send(Character("X")) } XCTAssertEqual(stream.bytes.count, 1 << 20) } @@ -97,7 +97,7 @@ class OutputByteStreamPerfTests: XCTestCasePerf { for _ in 0..<100 { let stream = BufferedOutputByteStream() for _ in 0..<(1 << 16) { - stream <<< bytes16 + stream.send(bytes16) } XCTAssertEqual(stream.bytes.count, 1 << 20) } @@ -116,7 +116,7 @@ class OutputByteStreamPerfTests: XCTestCasePerf { for _ in 0..<100 { let stream = BufferedOutputByteStream() for _ in 0..<(1 << 16) { - stream <<< bytes16 + stream.send(bytes16) } XCTAssertEqual(stream.bytes.count, 1 << 20) } @@ -133,7 +133,7 @@ class OutputByteStreamPerfTests: XCTestCasePerf { for _ in 0..<1000 { let stream = BufferedOutputByteStream() for _ in 0..<(1 << 10) { - stream <<< bytes1k + stream.send(bytes1k) } XCTAssertEqual(stream.bytes.count, 1 << 20) } @@ -150,7 +150,7 @@ class OutputByteStreamPerfTests: XCTestCasePerf { for _ in 0..<10 { let stream = BufferedOutputByteStream() for _ in 0..<(1 << 16) { - stream <<< string16 + stream.send(string16) } XCTAssertEqual(stream.bytes.count, 1 << 20) } @@ -167,7 +167,7 @@ class OutputByteStreamPerfTests: XCTestCasePerf { for _ in 0..<100 { let stream = BufferedOutputByteStream() for _ in 0..<(1 << 10) { - stream <<< bytes1k + stream.send(bytes1k) } XCTAssertEqual(stream.bytes.count, 1 << 20) } @@ -206,10 +206,10 @@ class OutputByteStreamPerfTests: XCTestCasePerf { let stream = BufferedOutputByteStream() for _ in 0..<(1 << 10) { for string in listOfStrings { - stream <<< Format.asJSON(string) + stream.send(Format.asJSON(string)) } - stream <<< Format.asJSON(listOfStrings) - stream <<< Format.asJSON(listOfThings, transform: { $0.value }) + stream.send(Format.asJSON(listOfStrings)) + stream.send(Format.asJSON(listOfThings, transform: { $0.value })) } XCTAssertGreaterThan(stream.bytes.count, 1000) } diff --git a/Tests/TSCBasicTests/ByteStringTests.swift b/Tests/TSCBasicTests/ByteStringTests.swift index 9fb665b2..3cada230 100644 --- a/Tests/TSCBasicTests/ByteStringTests.swift +++ b/Tests/TSCBasicTests/ByteStringTests.swift @@ -64,7 +64,7 @@ class ByteStringTests: XCTestCase { func testByteStreamable() { let s = BufferedOutputByteStream() - s <<< ByteString([1, 2, 3]) + s.send(ByteString([1, 2, 3])) XCTAssertEqual(s.bytes, [1, 2, 3]) } diff --git a/Tests/TSCBasicTests/FileSystemTests.swift b/Tests/TSCBasicTests/FileSystemTests.swift index 8fc62006..29430b91 100644 --- a/Tests/TSCBasicTests/FileSystemTests.swift +++ b/Tests/TSCBasicTests/FileSystemTests.swift @@ -23,17 +23,17 @@ class FileSystemTests: XCTestCase { try! withTemporaryFile { file in try! withTemporaryDirectory(removeTreeOnDeinit: true) { tempDirPath in // exists() - XCTAssert(fs.exists(AbsolutePath(path: "/"))) - XCTAssert(!fs.exists(AbsolutePath(path: "/does-not-exist"))) + XCTAssert(fs.exists("/")) + XCTAssert(!fs.exists("/does-not-exist")) // isFile() XCTAssertTrue(fs.exists(file.path)) XCTAssertTrue(fs.isFile(file.path)) XCTAssertEqual(try fs.getFileInfo(file.path).fileType, .typeRegular) XCTAssertFalse(fs.isDirectory(file.path)) - XCTAssertFalse(fs.isFile(AbsolutePath(path: "/does-not-exist"))) - XCTAssertFalse(fs.isSymlink(AbsolutePath(path: "/does-not-exist"))) - XCTAssertThrowsError(try fs.getFileInfo(AbsolutePath(path: "/does-not-exist"))) + XCTAssertFalse(fs.isFile("/does-not-exist")) + XCTAssertFalse(fs.isSymlink("/does-not-exist")) + XCTAssertThrowsError(try fs.getFileInfo("/does-not-exist")) // isSymlink() let sym = tempDirPath.appending(component: "hello") @@ -48,12 +48,12 @@ class FileSystemTests: XCTestCase { let executableSym = tempDirPath.appending(component: "exec-sym") try! fs.createSymbolicLink(executableSym, pointingAt: executable, relative: false) let stream = BufferedOutputByteStream() - stream <<< """ + stream.send(""" #!/bin/sh set -e exit - """ + """) try! fs.writeFileContents(executable, bytes: stream.bytes) try! Process.checkNonZeroExit(args: "chmod", "+x", executable.pathString) XCTAssertTrue(fs.isExecutableFile(executable)) @@ -61,25 +61,25 @@ class FileSystemTests: XCTestCase { XCTAssertTrue(fs.isSymlink(executableSym)) XCTAssertFalse(fs.isExecutableFile(sym)) XCTAssertFalse(fs.isExecutableFile(file.path)) - XCTAssertFalse(fs.isExecutableFile(AbsolutePath(path: "/does-not-exist"))) - XCTAssertFalse(fs.isExecutableFile(AbsolutePath(path: "/"))) + XCTAssertFalse(fs.isExecutableFile("/does-not-exist")) + XCTAssertFalse(fs.isExecutableFile("/")) // isDirectory() - XCTAssert(fs.isDirectory(AbsolutePath(path: "/"))) - XCTAssert(!fs.isDirectory(AbsolutePath(path: "/does-not-exist"))) + XCTAssert(fs.isDirectory("/")) + XCTAssert(!fs.isDirectory("/does-not-exist")) // getDirectoryContents() do { - _ = try fs.getDirectoryContents(AbsolutePath(path: "/does-not-exist")) + _ = try fs.getDirectoryContents("/does-not-exist") XCTFail("Unexpected success") } catch { XCTAssertEqual(error.localizedDescription, "The folder “does-not-exist” doesn’t exist.") } - let thisDirectoryContents = try! fs.getDirectoryContents(AbsolutePath(path: #file).parentDirectory) + let thisDirectoryContents = try! fs.getDirectoryContents(AbsolutePath(#file).parentDirectory) XCTAssertTrue(!thisDirectoryContents.contains(where: { $0 == "." })) XCTAssertTrue(!thisDirectoryContents.contains(where: { $0 == ".." })) - XCTAssertTrue(thisDirectoryContents.contains(where: { $0 == AbsolutePath(path: #file).basename })) + XCTAssertTrue(thisDirectoryContents.contains(where: { $0 == AbsolutePath(#file).basename })) } } } @@ -287,7 +287,7 @@ class FileSystemTests: XCTestCase { XCTAssertEqual(data, ByteString(testData)) // Atomic writes - let inMemoryFilePath = AbsolutePath(path: "/file.text") + let inMemoryFilePath = AbsolutePath("/file.text") XCTAssertNoThrow(try TSCBasic.InMemoryFileSystem(files: [:]).writeFileContents(inMemoryFilePath, bytes: ByteString(testData), atomically: true)) XCTAssertNoThrow(try TSCBasic.InMemoryFileSystem(files: [:]).writeFileContents(inMemoryFilePath, bytes: ByteString(testData), atomically: false)) // Local file system does support atomic writes, so it doesn't throw. @@ -323,9 +323,9 @@ class FileSystemTests: XCTestCase { // Check read/write against root. #if os(Android) - let root = AbsolutePath(path: "/system/") + let root = AbsolutePath("/system/") #else - let root = AbsolutePath(path: "/") + let root = AbsolutePath("/") #endif XCTAssertThrows(FileSystemError(.ioError(code: TSCLibc.EPERM), root)) { _ = try fs.readFileContents(root) @@ -444,7 +444,7 @@ class FileSystemTests: XCTestCase { func testInMemoryBasics() throws { let fs = InMemoryFileSystem() - let doesNotExist = AbsolutePath(path: "/does-not-exist") + let doesNotExist = AbsolutePath("/does-not-exist") // exists() XCTAssert(!fs.exists(doesNotExist)) @@ -464,22 +464,22 @@ class FileSystemTests: XCTestCase { } // createDirectory() - XCTAssert(!fs.isDirectory(AbsolutePath(path: "/new-dir"))) - try fs.createDirectory(AbsolutePath(path: "/new-dir/subdir"), recursive: true) - XCTAssert(fs.isDirectory(AbsolutePath(path: "/new-dir"))) - XCTAssert(fs.isDirectory(AbsolutePath(path: "/new-dir/subdir"))) - XCTAssertEqual(try fs.getDirectoryContents(AbsolutePath(path: "/")), ["new-dir"]) - XCTAssertEqual(try fs.getDirectoryContents(AbsolutePath(path: "/new-dir")), ["subdir"]) + XCTAssert(!fs.isDirectory("/new-dir")) + try fs.createDirectory("/new-dir/subdir", recursive: true) + XCTAssert(fs.isDirectory("/new-dir")) + XCTAssert(fs.isDirectory("/new-dir/subdir")) + XCTAssertEqual(try fs.getDirectoryContents("/"), ["new-dir"]) + XCTAssertEqual(try fs.getDirectoryContents("/new-dir"), ["subdir"]) } func testInMemoryCreateDirectory() { let fs = InMemoryFileSystem() // Make sure root entry isn't created. - try! fs.createDirectory(AbsolutePath(path: "/"), recursive: true) + try! fs.createDirectory("/", recursive: true) let rootContents = try! fs.getDirectoryContents(.root) XCTAssertEqual(rootContents, []) - let subdir = AbsolutePath(path: "/new-dir/subdir") + let subdir = AbsolutePath("/new-dir/subdir") try! fs.createDirectory(subdir, recursive: true) XCTAssert(fs.isDirectory(subdir)) @@ -494,7 +494,7 @@ class FileSystemTests: XCTestCase { XCTAssert(fs.isDirectory(subsubdir)) // Check non-recursive failing subdir case. - let veryNewDir = AbsolutePath(path: "/very-new-dir") + let veryNewDir = AbsolutePath("/very-new-dir") let newsubdir = veryNewDir.appending(component: "subdir") XCTAssert(!fs.isDirectory(newsubdir)) XCTAssertThrows(FileSystemError(.noEntry, veryNewDir)) { @@ -503,7 +503,7 @@ class FileSystemTests: XCTestCase { XCTAssert(!fs.isDirectory(newsubdir)) // Check directory creation over a file. - let filePath = AbsolutePath(path: "/mach_kernel") + let filePath = AbsolutePath("/mach_kernel") try! fs.writeFileContents(filePath, bytes: [0xCD, 0x0D]) XCTAssert(fs.exists(filePath) && !fs.isDirectory(filePath)) XCTAssertThrows(FileSystemError(.notDirectory, filePath)) { @@ -548,10 +548,10 @@ class FileSystemTests: XCTestCase { func testInMemoryReadWriteFile() { let fs = InMemoryFileSystem() - try! fs.createDirectory(AbsolutePath(path: "/new-dir/subdir"), recursive: true) + try! fs.createDirectory("/new-dir/subdir", recursive: true) // Check read/write of a simple file. - let filePath = AbsolutePath(path: "/new-dir/subdir").appending(component: "new-file.txt") + let filePath = AbsolutePath("/new-dir/subdir").appending(component: "new-file.txt") XCTAssert(!fs.exists(filePath)) XCTAssertFalse(fs.isFile(filePath)) try! fs.writeFileContents(filePath, bytes: "Hello, world!") @@ -575,7 +575,7 @@ class FileSystemTests: XCTestCase { XCTAssertEqual(try! fs.readFileContents(filePath), "Hello, new world!") // Check read/write against root. - let root = AbsolutePath(path: "/") + let root = AbsolutePath("/") XCTAssertThrows(FileSystemError(.isDirectory, root)) { _ = try fs.readFileContents(root) } @@ -596,7 +596,7 @@ class FileSystemTests: XCTestCase { XCTAssert(fs.exists(filePath)) // Check read/write into a missing directory. - let missingParent = AbsolutePath(path: "/does/not") + let missingParent = AbsolutePath("/does/not") let missingFile = missingParent.appending(component: "exist") XCTAssertThrows(FileSystemError(.noEntry, missingFile)) { _ = try fs.readFileContents(missingFile) @@ -609,8 +609,8 @@ class FileSystemTests: XCTestCase { func testInMemoryFsCopy() throws { let fs = InMemoryFileSystem() - try! fs.createDirectory(AbsolutePath(path: "/new-dir/subdir"), recursive: true) - let filePath = AbsolutePath(path: "/new-dir/subdir").appending(component: "new-file.txt") + try! fs.createDirectory("/new-dir/subdir", recursive: true) + let filePath = AbsolutePath("/new-dir/subdir").appending(component: "new-file.txt") try! fs.writeFileContents(filePath, bytes: "Hello, world!") XCTAssertEqual(try! fs.readFileContents(filePath), "Hello, world!") @@ -629,7 +629,7 @@ class FileSystemTests: XCTestCase { func testInMemCopyAndMoveItem() throws { let fs = InMemoryFileSystem() - let path = AbsolutePath(path: "/tmp") + let path = AbsolutePath("/tmp") try fs.createDirectory(path) let source = path.appending(component: "source") let destination = path.appending(component: "destination") @@ -707,33 +707,33 @@ class FileSystemTests: XCTestCase { func testRootedFileSystem() throws { // Create the test file system. let baseFileSystem = InMemoryFileSystem() as FileSystem - try baseFileSystem.createDirectory(AbsolutePath(path: "/base/rootIsHere/subdir"), recursive: true) - try baseFileSystem.writeFileContents(AbsolutePath(path: "/base/rootIsHere/subdir/file"), bytes: "Hello, world!") + try baseFileSystem.createDirectory("/base/rootIsHere/subdir", recursive: true) + try baseFileSystem.writeFileContents("/base/rootIsHere/subdir/file", bytes: "Hello, world!") // Create the rooted file system. - let rerootedFileSystem = RerootedFileSystemView(baseFileSystem, rootedAt: AbsolutePath(path: "/base/rootIsHere")) + let rerootedFileSystem = RerootedFileSystemView(baseFileSystem, rootedAt: "/base/rootIsHere") // Check that it has the appropriate view. - XCTAssert(rerootedFileSystem.exists(AbsolutePath(path: "/subdir"))) - XCTAssert(rerootedFileSystem.isDirectory(AbsolutePath(path: "/subdir"))) - XCTAssert(rerootedFileSystem.exists(AbsolutePath(path: "/subdir/file"))) - XCTAssertEqual(try rerootedFileSystem.readFileContents(AbsolutePath(path: "/subdir/file")), "Hello, world!") + XCTAssert(rerootedFileSystem.exists("/subdir")) + XCTAssert(rerootedFileSystem.isDirectory("/subdir")) + XCTAssert(rerootedFileSystem.exists("/subdir/file")) + XCTAssertEqual(try rerootedFileSystem.readFileContents("/subdir/file"), "Hello, world!") // Check that mutations work appropriately. - XCTAssert(!baseFileSystem.exists(AbsolutePath(path: "/base/rootIsHere/subdir2"))) - try rerootedFileSystem.createDirectory(AbsolutePath(path: "/subdir2")) - XCTAssert(baseFileSystem.isDirectory(AbsolutePath(path: "/base/rootIsHere/subdir2"))) + XCTAssert(!baseFileSystem.exists("/base/rootIsHere/subdir2")) + try rerootedFileSystem.createDirectory("/subdir2") + XCTAssert(baseFileSystem.isDirectory("/base/rootIsHere/subdir2")) } func testRootedCreateSymlink() throws { // Create the test file system. let baseFileSystem = InMemoryFileSystem() as FileSystem - try baseFileSystem.createDirectory(AbsolutePath(path: "/base/rootIsHere/subdir"), recursive: true) + try baseFileSystem.createDirectory("/base/rootIsHere/subdir", recursive: true) // Create the rooted file system. - let fs = RerootedFileSystemView(baseFileSystem, rootedAt: AbsolutePath(path: "/base/rootIsHere")) + let fs = RerootedFileSystemView(baseFileSystem, rootedAt: "/base/rootIsHere") - let path = AbsolutePath(path: "/test") + let path = AbsolutePath("/test") try fs.createDirectory(path, recursive: true) let source = path.appending(component: "source") @@ -818,7 +818,7 @@ class FileSystemTests: XCTestCase { func testInMemoryFileSystemFileLock() throws { let fs = InMemoryFileSystem() - let path = AbsolutePath(path: "/") + let path = AbsolutePath("/") try fs.createDirectory(path) let fileA = path.appending(component: "fileA") @@ -846,11 +846,11 @@ class FileSystemTests: XCTestCase { func testRerootedFileSystemViewFileLock() throws { let inMemoryFS = InMemoryFileSystem() - let rootPath = AbsolutePath(path: "/tmp") + let rootPath = AbsolutePath("/tmp") try inMemoryFS.createDirectory(rootPath) let fs = RerootedFileSystemView(inMemoryFS, rootedAt: rootPath) - let path = AbsolutePath(path: "/") + let path = AbsolutePath("/") try fs.createDirectory(path) let fileA = path.appending(component: "fileA") @@ -860,6 +860,19 @@ class FileSystemTests: XCTestCase { try _testFileSystemFileLock(fileSystem: fs, fileA: fileA, fileB: fileB, lockFile: lockFile) } +#if canImport(Darwin) + func testHasAttribute() throws { + try withTemporaryDirectory(removeTreeOnDeinit: true) { tempDir in + let filePath = tempDir.appending(component: "quarantined") + try localFileSystem.writeFileContents(filePath, bytes: "") + try Process.checkNonZeroExit(args: "xattr", "-w", FileSystemAttribute.quarantine.rawValue, "foo", filePath.pathString) + XCTAssertTrue(localFileSystem.hasAttribute(.quarantine, filePath)) + try Process.checkNonZeroExit(args: "xattr", "-d", FileSystemAttribute.quarantine.rawValue, filePath.pathString) + XCTAssertFalse(localFileSystem.hasAttribute(.quarantine, filePath)) + } + } +#endif + private func _testFileSystemFileLock(fileSystem fs: FileSystem, fileA: AbsolutePath, fileB: AbsolutePath, lockFile: AbsolutePath) throws { // write initial value, since reader may start before writers and files would not exist try fs.writeFileContents(fileA, bytes: "0") diff --git a/Tests/TSCBasicTests/PathShimTests.swift b/Tests/TSCBasicTests/PathShimTests.swift index 072e18ff..1af04a37 100644 --- a/Tests/TSCBasicTests/PathShimTests.swift +++ b/Tests/TSCBasicTests/PathShimTests.swift @@ -36,17 +36,17 @@ class WalkTests : XCTestCase { func testNonRecursive() throws { #if os(Android) let root = "/system" - var expected = [ - AbsolutePath(path: "\(root)/usr"), - AbsolutePath(path: "\(root)/bin"), - AbsolutePath(path: "\(root)/xbin") + var expected: [AbsolutePath] = [ + "\(root)/usr", + "\(root)/bin", + "\(root)/xbin" ] #else let root = "" - var expected = [ - AbsolutePath(path: "/usr"), - AbsolutePath(path: "/bin"), - AbsolutePath(path: "/sbin") + var expected: [AbsolutePath] = [ + "/usr", + "/bin", + "/sbin" ] #endif for x in try walk(AbsolutePath(validating: "\(root)/"), recursively: false) { @@ -63,7 +63,7 @@ class WalkTests : XCTestCase { } func testRecursive() { - let root = AbsolutePath(path: #file).parentDirectory.parentDirectory.parentDirectory.appending(component: "Sources") + let root = AbsolutePath(#file).parentDirectory.parentDirectory.parentDirectory.appending(component: "Sources") var expected = [ root.appending(component: "TSCBasic"), root.appending(component: "TSCUtility") diff --git a/Tests/TSCBasicTests/PathTests.swift b/Tests/TSCBasicTests/PathTests.swift index cdb27a4d..450aa29e 100644 --- a/Tests/TSCBasicTests/PathTests.swift +++ b/Tests/TSCBasicTests/PathTests.swift @@ -8,41 +8,41 @@ See http://swift.org/CONTRIBUTORS.txt for Swift project authors */ -import XCTest import Foundation - import TSCBasic +import TSCTestSupport +import XCTest class PathTests: XCTestCase { func testBasics() { - XCTAssertEqual(AbsolutePath(path: "/").pathString, "/") - XCTAssertEqual(AbsolutePath(path: "/a").pathString, "/a") - XCTAssertEqual(AbsolutePath(path: "/a/b/c").pathString, "/a/b/c") + XCTAssertEqual(AbsolutePath("/").pathString, "/") + XCTAssertEqual(AbsolutePath("/a").pathString, "/a") + XCTAssertEqual(AbsolutePath("/a/b/c").pathString, "/a/b/c") XCTAssertEqual(RelativePath(".").pathString, ".") XCTAssertEqual(RelativePath("a").pathString, "a") XCTAssertEqual(RelativePath("a/b/c").pathString, "a/b/c") XCTAssertEqual(RelativePath("~").pathString, "~") // `~` is not special } - func testStringInitialization() { - let abs1 = AbsolutePath(path: "/") + func testStringInitialization() throws { + let abs1 = AbsolutePath("/") let abs2 = AbsolutePath(abs1, ".") XCTAssertEqual(abs1, abs2) let rel3 = "." - let abs3 = AbsolutePath(abs2, rel3) + let abs3 = try AbsolutePath(abs2, validating: rel3) XCTAssertEqual(abs2, abs3) - let base = AbsolutePath(path: "/base/path") - let abs4 = AbsolutePath(path: "/a/b/c", relativeTo: base) - XCTAssertEqual(abs4, AbsolutePath(path: "/a/b/c")) - let abs5 = AbsolutePath(path: "./a/b/c", relativeTo: base) - XCTAssertEqual(abs5, AbsolutePath(path: "/base/path/a/b/c")) - let abs6 = AbsolutePath(path: "~/bla", relativeTo: base) // `~` isn't special - XCTAssertEqual(abs6, AbsolutePath(path: "/base/path/~/bla")) + let base = AbsolutePath("/base/path") + let abs4 = AbsolutePath("/a/b/c", relativeTo: base) + XCTAssertEqual(abs4, AbsolutePath("/a/b/c")) + let abs5 = AbsolutePath("./a/b/c", relativeTo: base) + XCTAssertEqual(abs5, AbsolutePath("/base/path/a/b/c")) + let abs6 = AbsolutePath("~/bla", relativeTo: base) // `~` isn't special + XCTAssertEqual(abs6, AbsolutePath("/base/path/~/bla")) } func testStringLiteralInitialization() { - let abs = AbsolutePath(path: "/") + let abs = AbsolutePath("/") XCTAssertEqual(abs.pathString, "/") let rel1 = RelativePath(".") XCTAssertEqual(rel1.pathString, ".") @@ -51,34 +51,34 @@ class PathTests: XCTestCase { } func testRepeatedPathSeparators() { - XCTAssertEqual(AbsolutePath(path: "/ab//cd//ef").pathString, "/ab/cd/ef") - XCTAssertEqual(AbsolutePath(path: "/ab///cd//ef").pathString, "/ab/cd/ef") + XCTAssertEqual(AbsolutePath("/ab//cd//ef").pathString, "/ab/cd/ef") + XCTAssertEqual(AbsolutePath("/ab///cd//ef").pathString, "/ab/cd/ef") XCTAssertEqual(RelativePath("ab//cd//ef").pathString, "ab/cd/ef") XCTAssertEqual(RelativePath("ab//cd///ef").pathString, "ab/cd/ef") } func testTrailingPathSeparators() { - XCTAssertEqual(AbsolutePath(path: "/ab/cd/ef/").pathString, "/ab/cd/ef") - XCTAssertEqual(AbsolutePath(path: "/ab/cd/ef//").pathString, "/ab/cd/ef") + XCTAssertEqual(AbsolutePath("/ab/cd/ef/").pathString, "/ab/cd/ef") + XCTAssertEqual(AbsolutePath("/ab/cd/ef//").pathString, "/ab/cd/ef") XCTAssertEqual(RelativePath("ab/cd/ef/").pathString, "ab/cd/ef") XCTAssertEqual(RelativePath("ab/cd/ef//").pathString, "ab/cd/ef") } func testDotPathComponents() { - XCTAssertEqual(AbsolutePath(path: "/ab/././cd//ef").pathString, "/ab/cd/ef") - XCTAssertEqual(AbsolutePath(path: "/ab/./cd//ef/.").pathString, "/ab/cd/ef") + XCTAssertEqual(AbsolutePath("/ab/././cd//ef").pathString, "/ab/cd/ef") + XCTAssertEqual(AbsolutePath("/ab/./cd//ef/.").pathString, "/ab/cd/ef") XCTAssertEqual(RelativePath("ab/./cd/././ef").pathString, "ab/cd/ef") XCTAssertEqual(RelativePath("ab/./cd/ef/.").pathString, "ab/cd/ef") } func testDotDotPathComponents() { - XCTAssertEqual(AbsolutePath(path: "/..").pathString, "/") - XCTAssertEqual(AbsolutePath(path: "/../../../../..").pathString, "/") - XCTAssertEqual(AbsolutePath(path: "/abc/..").pathString, "/") - XCTAssertEqual(AbsolutePath(path: "/abc/../..").pathString, "/") - XCTAssertEqual(AbsolutePath(path: "/../abc").pathString, "/abc") - XCTAssertEqual(AbsolutePath(path: "/../abc/..").pathString, "/") - XCTAssertEqual(AbsolutePath(path: "/../abc/../def").pathString, "/def") + XCTAssertEqual(AbsolutePath("/..").pathString, "/") + XCTAssertEqual(AbsolutePath("/../../../../..").pathString, "/") + XCTAssertEqual(AbsolutePath("/abc/..").pathString, "/") + XCTAssertEqual(AbsolutePath("/abc/../..").pathString, "/") + XCTAssertEqual(AbsolutePath("/../abc").pathString, "/abc") + XCTAssertEqual(AbsolutePath("/../abc/..").pathString, "/") + XCTAssertEqual(AbsolutePath("/../abc/../def").pathString, "/def") XCTAssertEqual(RelativePath("..").pathString, "..") XCTAssertEqual(RelativePath("../..").pathString, "../..") XCTAssertEqual(RelativePath(".././..").pathString, "../..") @@ -88,8 +88,8 @@ class PathTests: XCTestCase { } func testCombinationsAndEdgeCases() { - XCTAssertEqual(AbsolutePath(path: "///").pathString, "/") - XCTAssertEqual(AbsolutePath(path: "/./").pathString, "/") + XCTAssertEqual(AbsolutePath("///").pathString, "/") + XCTAssertEqual(AbsolutePath("/./").pathString, "/") XCTAssertEqual(RelativePath("").pathString, ".") XCTAssertEqual(RelativePath(".").pathString, ".") XCTAssertEqual(RelativePath("./abc").pathString, "abc") @@ -117,11 +117,11 @@ class PathTests: XCTestCase { } func testDirectoryNameExtraction() { - XCTAssertEqual(AbsolutePath(path: "/").dirname, "/") - XCTAssertEqual(AbsolutePath(path: "/a").dirname, "/") - XCTAssertEqual(AbsolutePath(path: "/./a").dirname, "/") - XCTAssertEqual(AbsolutePath(path: "/../..").dirname, "/") - XCTAssertEqual(AbsolutePath(path: "/ab/c//d/").dirname, "/ab/c") + XCTAssertEqual(AbsolutePath("/").dirname, "/") + XCTAssertEqual(AbsolutePath("/a").dirname, "/") + XCTAssertEqual(AbsolutePath("/./a").dirname, "/") + XCTAssertEqual(AbsolutePath("/../..").dirname, "/") + XCTAssertEqual(AbsolutePath("/ab/c//d/").dirname, "/ab/c") XCTAssertEqual(RelativePath("ab/c//d/").dirname, "ab/c") XCTAssertEqual(RelativePath("../a").dirname, "..") XCTAssertEqual(RelativePath("../a/..").dirname, ".") @@ -134,10 +134,10 @@ class PathTests: XCTestCase { } func testBaseNameExtraction() { - XCTAssertEqual(AbsolutePath(path: "/").basename, "/") - XCTAssertEqual(AbsolutePath(path: "/a").basename, "a") - XCTAssertEqual(AbsolutePath(path: "/./a").basename, "a") - XCTAssertEqual(AbsolutePath(path: "/../..").basename, "/") + XCTAssertEqual(AbsolutePath("/").basename, "/") + XCTAssertEqual(AbsolutePath("/a").basename, "a") + XCTAssertEqual(AbsolutePath("/./a").basename, "a") + XCTAssertEqual(AbsolutePath("/../..").basename, "/") XCTAssertEqual(RelativePath("../..").basename, "..") XCTAssertEqual(RelativePath("../a").basename, "a") XCTAssertEqual(RelativePath("../a/..").basename, "..") @@ -150,10 +150,10 @@ class PathTests: XCTestCase { } func testBaseNameWithoutExt() { - XCTAssertEqual(AbsolutePath(path: "/").basenameWithoutExt, "/") - XCTAssertEqual(AbsolutePath(path: "/a").basenameWithoutExt, "a") - XCTAssertEqual(AbsolutePath(path: "/./a").basenameWithoutExt, "a") - XCTAssertEqual(AbsolutePath(path: "/../..").basenameWithoutExt, "/") + XCTAssertEqual(AbsolutePath("/").basenameWithoutExt, "/") + XCTAssertEqual(AbsolutePath("/a").basenameWithoutExt, "a") + XCTAssertEqual(AbsolutePath("/./a").basenameWithoutExt, "a") + XCTAssertEqual(AbsolutePath("/../..").basenameWithoutExt, "/") XCTAssertEqual(RelativePath("../..").basenameWithoutExt, "..") XCTAssertEqual(RelativePath("../a").basenameWithoutExt, "a") XCTAssertEqual(RelativePath("../a/..").basenameWithoutExt, "..") @@ -164,8 +164,8 @@ class PathTests: XCTestCase { XCTAssertEqual(RelativePath("").basenameWithoutExt, ".") XCTAssertEqual(RelativePath(".").basenameWithoutExt, ".") - XCTAssertEqual(AbsolutePath(path: "/a.txt").basenameWithoutExt, "a") - XCTAssertEqual(AbsolutePath(path: "/./a.txt").basenameWithoutExt, "a") + XCTAssertEqual(AbsolutePath("/a.txt").basenameWithoutExt, "a") + XCTAssertEqual(AbsolutePath("/./a.txt").basenameWithoutExt, "a") XCTAssertEqual(RelativePath("../a.bc").basenameWithoutExt, "a") XCTAssertEqual(RelativePath("abc.swift").basenameWithoutExt, "abc") XCTAssertEqual(RelativePath("../a.b.c").basenameWithoutExt, "a.b") @@ -198,60 +198,60 @@ class PathTests: XCTestCase { } func testParentDirectory() { - XCTAssertEqual(AbsolutePath(path: "/").parentDirectory, AbsolutePath(path: "/")) - XCTAssertEqual(AbsolutePath(path: "/").parentDirectory.parentDirectory, AbsolutePath(path: "/")) - XCTAssertEqual(AbsolutePath(path: "/bar").parentDirectory, AbsolutePath(path: "/")) - XCTAssertEqual(AbsolutePath(path: "/bar/../foo/..//").parentDirectory.parentDirectory, AbsolutePath(path: "/")) - XCTAssertEqual(AbsolutePath(path: "/bar/../foo/..//yabba/a/b").parentDirectory.parentDirectory, AbsolutePath(path: "/yabba")) + XCTAssertEqual(AbsolutePath("/").parentDirectory, AbsolutePath("/")) + XCTAssertEqual(AbsolutePath("/").parentDirectory.parentDirectory, AbsolutePath("/")) + XCTAssertEqual(AbsolutePath("/bar").parentDirectory, AbsolutePath("/")) + XCTAssertEqual(AbsolutePath("/bar/../foo/..//").parentDirectory.parentDirectory, AbsolutePath("/")) + XCTAssertEqual(AbsolutePath("/bar/../foo/..//yabba/a/b").parentDirectory.parentDirectory, AbsolutePath("/yabba")) } @available(*, deprecated) func testConcatenation() { - XCTAssertEqual(AbsolutePath(AbsolutePath(path: "/"), RelativePath("")).pathString, "/") - XCTAssertEqual(AbsolutePath(AbsolutePath(path: "/"), RelativePath(".")).pathString, "/") - XCTAssertEqual(AbsolutePath(AbsolutePath(path: "/"), RelativePath("..")).pathString, "/") - XCTAssertEqual(AbsolutePath(AbsolutePath(path: "/"), RelativePath("bar")).pathString, "/bar") - XCTAssertEqual(AbsolutePath(AbsolutePath(path: "/foo/bar"), RelativePath("..")).pathString, "/foo") - XCTAssertEqual(AbsolutePath(AbsolutePath(path: "/bar"), RelativePath("../foo")).pathString, "/foo") - XCTAssertEqual(AbsolutePath(AbsolutePath(path: "/bar"), RelativePath("../foo/..//")).pathString, "/") - XCTAssertEqual(AbsolutePath(AbsolutePath(path: "/bar/../foo/..//yabba/"), RelativePath("a/b")).pathString, "/yabba/a/b") - - XCTAssertEqual(AbsolutePath(path: "/").appending(RelativePath("")).pathString, "/") - XCTAssertEqual(AbsolutePath(path: "/").appending(RelativePath(".")).pathString, "/") - XCTAssertEqual(AbsolutePath(path: "/").appending(RelativePath("..")).pathString, "/") - XCTAssertEqual(AbsolutePath(path: "/").appending(RelativePath("bar")).pathString, "/bar") - XCTAssertEqual(AbsolutePath(path: "/foo/bar").appending(RelativePath("..")).pathString, "/foo") - XCTAssertEqual(AbsolutePath(path: "/bar").appending(RelativePath("../foo")).pathString, "/foo") - XCTAssertEqual(AbsolutePath(path: "/bar").appending(RelativePath("../foo/..//")).pathString, "/") - XCTAssertEqual(AbsolutePath(path: "/bar/../foo/..//yabba/").appending(RelativePath("a/b")).pathString, "/yabba/a/b") - - XCTAssertEqual(AbsolutePath(path: "/").appending(component: "a").pathString, "/a") - XCTAssertEqual(AbsolutePath(path: "/a").appending(component: "b").pathString, "/a/b") - XCTAssertEqual(AbsolutePath(path: "/").appending(components: "a", "b").pathString, "/a/b") - XCTAssertEqual(AbsolutePath(path: "/a").appending(components: "b", "c").pathString, "/a/b/c") - - XCTAssertEqual(AbsolutePath(path: "/a/b/c").appending(components: "", "c").pathString, "/a/b/c/c") - XCTAssertEqual(AbsolutePath(path: "/a/b/c").appending(components: "").pathString, "/a/b/c") - XCTAssertEqual(AbsolutePath(path: "/a/b/c").appending(components: ".").pathString, "/a/b/c") - XCTAssertEqual(AbsolutePath(path: "/a/b/c").appending(components: "..").pathString, "/a/b") - XCTAssertEqual(AbsolutePath(path: "/a/b/c").appending(components: "..", "d").pathString, "/a/b/d") - XCTAssertEqual(AbsolutePath(path: "/").appending(components: "..").pathString, "/") - XCTAssertEqual(AbsolutePath(path: "/").appending(components: ".").pathString, "/") - XCTAssertEqual(AbsolutePath(path: "/").appending(components: "..", "a").pathString, "/a") + XCTAssertEqual(AbsolutePath(AbsolutePath("/"), RelativePath("")).pathString, "/") + XCTAssertEqual(AbsolutePath(AbsolutePath("/"), RelativePath(".")).pathString, "/") + XCTAssertEqual(AbsolutePath(AbsolutePath("/"), RelativePath("..")).pathString, "/") + XCTAssertEqual(AbsolutePath(AbsolutePath("/"), RelativePath("bar")).pathString, "/bar") + XCTAssertEqual(AbsolutePath(AbsolutePath("/foo/bar"), RelativePath("..")).pathString, "/foo") + XCTAssertEqual(AbsolutePath(AbsolutePath("/bar"), RelativePath("../foo")).pathString, "/foo") + XCTAssertEqual(AbsolutePath(AbsolutePath("/bar"), RelativePath("../foo/..//")).pathString, "/") + XCTAssertEqual(AbsolutePath(AbsolutePath("/bar/../foo/..//yabba/"), RelativePath("a/b")).pathString, "/yabba/a/b") + + XCTAssertEqual(AbsolutePath("/").appending(RelativePath("")).pathString, "/") + XCTAssertEqual(AbsolutePath("/").appending(RelativePath(".")).pathString, "/") + XCTAssertEqual(AbsolutePath("/").appending(RelativePath("..")).pathString, "/") + XCTAssertEqual(AbsolutePath("/").appending(RelativePath("bar")).pathString, "/bar") + XCTAssertEqual(AbsolutePath("/foo/bar").appending(RelativePath("..")).pathString, "/foo") + XCTAssertEqual(AbsolutePath("/bar").appending(RelativePath("../foo")).pathString, "/foo") + XCTAssertEqual(AbsolutePath("/bar").appending(RelativePath("../foo/..//")).pathString, "/") + XCTAssertEqual(AbsolutePath("/bar/../foo/..//yabba/").appending(RelativePath("a/b")).pathString, "/yabba/a/b") + + XCTAssertEqual(AbsolutePath("/").appending(component: "a").pathString, "/a") + XCTAssertEqual(AbsolutePath("/a").appending(component: "b").pathString, "/a/b") + XCTAssertEqual(AbsolutePath("/").appending(components: "a", "b").pathString, "/a/b") + XCTAssertEqual(AbsolutePath("/a").appending(components: "b", "c").pathString, "/a/b/c") + + XCTAssertEqual(AbsolutePath("/a/b/c").appending(components: "", "c").pathString, "/a/b/c/c") + XCTAssertEqual(AbsolutePath("/a/b/c").appending(components: "").pathString, "/a/b/c") + XCTAssertEqual(AbsolutePath("/a/b/c").appending(components: ".").pathString, "/a/b/c") + XCTAssertEqual(AbsolutePath("/a/b/c").appending(components: "..").pathString, "/a/b") + XCTAssertEqual(AbsolutePath("/a/b/c").appending(components: "..", "d").pathString, "/a/b/d") + XCTAssertEqual(AbsolutePath("/").appending(components: "..").pathString, "/") + XCTAssertEqual(AbsolutePath("/").appending(components: ".").pathString, "/") + XCTAssertEqual(AbsolutePath("/").appending(components: "..", "a").pathString, "/a") XCTAssertEqual(RelativePath("hello").appending(components: "a", "b", "c", "..").pathString, "hello/a/b") XCTAssertEqual(RelativePath("hello").appending(RelativePath("a/b/../c/d")).pathString, "hello/a/c/d") } func testPathComponents() { - XCTAssertEqual(AbsolutePath(path: "/").components, ["/"]) - XCTAssertEqual(AbsolutePath(path: "/.").components, ["/"]) - XCTAssertEqual(AbsolutePath(path: "/..").components, ["/"]) - XCTAssertEqual(AbsolutePath(path: "/bar").components, ["/", "bar"]) - XCTAssertEqual(AbsolutePath(path: "/foo/bar/..").components, ["/", "foo"]) - XCTAssertEqual(AbsolutePath(path: "/bar/../foo").components, ["/", "foo"]) - XCTAssertEqual(AbsolutePath(path: "/bar/../foo/..//").components, ["/"]) - XCTAssertEqual(AbsolutePath(path: "/bar/../foo/..//yabba/a/b/").components, ["/", "yabba", "a", "b"]) + XCTAssertEqual(AbsolutePath("/").components, ["/"]) + XCTAssertEqual(AbsolutePath("/.").components, ["/"]) + XCTAssertEqual(AbsolutePath("/..").components, ["/"]) + XCTAssertEqual(AbsolutePath("/bar").components, ["/", "bar"]) + XCTAssertEqual(AbsolutePath("/foo/bar/..").components, ["/", "foo"]) + XCTAssertEqual(AbsolutePath("/bar/../foo").components, ["/", "foo"]) + XCTAssertEqual(AbsolutePath("/bar/../foo/..//").components, ["/"]) + XCTAssertEqual(AbsolutePath("/bar/../foo/..//yabba/a/b/").components, ["/", "yabba", "a", "b"]) XCTAssertEqual(RelativePath("").components, ["."]) XCTAssertEqual(RelativePath(".").components, ["."]) @@ -272,44 +272,44 @@ class PathTests: XCTestCase { } func testRelativePathFromAbsolutePaths() { - XCTAssertEqual(AbsolutePath(path: "/").relative(to: AbsolutePath(path: "/")), RelativePath(".")); - XCTAssertEqual(AbsolutePath(path: "/a/b/c/d").relative(to: AbsolutePath(path: "/")), RelativePath("a/b/c/d")); - XCTAssertEqual(AbsolutePath(path: "/").relative(to: AbsolutePath(path: "/a/b/c")), RelativePath("../../..")); - XCTAssertEqual(AbsolutePath(path: "/a/b/c/d").relative(to: AbsolutePath(path: "/a/b")), RelativePath("c/d")); - XCTAssertEqual(AbsolutePath(path: "/a/b/c/d").relative(to: AbsolutePath(path: "/a/b/c")), RelativePath("d")); - XCTAssertEqual(AbsolutePath(path: "/a/b/c/d").relative(to: AbsolutePath(path: "/a/c/d")), RelativePath("../../b/c/d")); - XCTAssertEqual(AbsolutePath(path: "/a/b/c/d").relative(to: AbsolutePath(path: "/b/c/d")), RelativePath("../../../a/b/c/d")); + XCTAssertEqual(AbsolutePath("/").relative(to: AbsolutePath("/")), RelativePath(".")); + XCTAssertEqual(AbsolutePath("/a/b/c/d").relative(to: AbsolutePath("/")), RelativePath("a/b/c/d")); + XCTAssertEqual(AbsolutePath("/").relative(to: AbsolutePath("/a/b/c")), RelativePath("../../..")); + XCTAssertEqual(AbsolutePath("/a/b/c/d").relative(to: AbsolutePath("/a/b")), RelativePath("c/d")); + XCTAssertEqual(AbsolutePath("/a/b/c/d").relative(to: AbsolutePath("/a/b/c")), RelativePath("d")); + XCTAssertEqual(AbsolutePath("/a/b/c/d").relative(to: AbsolutePath("/a/c/d")), RelativePath("../../b/c/d")); + XCTAssertEqual(AbsolutePath("/a/b/c/d").relative(to: AbsolutePath("/b/c/d")), RelativePath("../../../a/b/c/d")); } func testComparison() { - XCTAssertTrue(AbsolutePath(path: "/") <= AbsolutePath(path: "/")); - XCTAssertTrue(AbsolutePath(path: "/abc") < AbsolutePath(path: "/def")); - XCTAssertTrue(AbsolutePath(path: "/2") <= AbsolutePath(path: "/2.1")); - XCTAssertTrue(AbsolutePath(path: "/3.1") > AbsolutePath(path: "/2")); - XCTAssertTrue(AbsolutePath(path: "/2") >= AbsolutePath(path: "/2")); - XCTAssertTrue(AbsolutePath(path: "/2.1") >= AbsolutePath(path: "/2")); + XCTAssertTrue(AbsolutePath("/") <= AbsolutePath("/")); + XCTAssertTrue(AbsolutePath("/abc") < AbsolutePath("/def")); + XCTAssertTrue(AbsolutePath("/2") <= AbsolutePath("/2.1")); + XCTAssertTrue(AbsolutePath("/3.1") > AbsolutePath("/2")); + XCTAssertTrue(AbsolutePath("/2") >= AbsolutePath("/2")); + XCTAssertTrue(AbsolutePath("/2.1") >= AbsolutePath("/2")); } func testAncestry() { - XCTAssertTrue(AbsolutePath(path: "/a/b/c/d/e/f").isDescendantOfOrEqual(to: AbsolutePath(path: "/a/b/c/d"))) - XCTAssertTrue(AbsolutePath(path: "/a/b/c/d/e/f.swift").isDescendantOfOrEqual(to: AbsolutePath(path: "/a/b/c"))) - XCTAssertTrue(AbsolutePath(path: "/").isDescendantOfOrEqual(to: AbsolutePath(path: "/"))) - XCTAssertTrue(AbsolutePath(path: "/foo/bar").isDescendantOfOrEqual(to: AbsolutePath(path: "/"))) - XCTAssertFalse(AbsolutePath(path: "/foo/bar").isDescendantOfOrEqual(to: AbsolutePath(path: "/foo/bar/baz"))) - XCTAssertFalse(AbsolutePath(path: "/foo/bar").isDescendantOfOrEqual(to: AbsolutePath(path: "/bar"))) - - XCTAssertFalse(AbsolutePath(path: "/foo/bar").isDescendant(of: AbsolutePath(path: "/foo/bar"))) - XCTAssertTrue(AbsolutePath(path: "/foo/bar").isDescendant(of: AbsolutePath(path: "/foo"))) - - XCTAssertTrue(AbsolutePath(path: "/a/b/c/d").isAncestorOfOrEqual(to: AbsolutePath(path: "/a/b/c/d/e/f"))) - XCTAssertTrue(AbsolutePath(path: "/a/b/c").isAncestorOfOrEqual(to: AbsolutePath(path: "/a/b/c/d/e/f.swift"))) - XCTAssertTrue(AbsolutePath(path: "/").isAncestorOfOrEqual(to: AbsolutePath(path: "/"))) - XCTAssertTrue(AbsolutePath(path: "/").isAncestorOfOrEqual(to: AbsolutePath(path: "/foo/bar"))) - XCTAssertFalse(AbsolutePath(path: "/foo/bar/baz").isAncestorOfOrEqual(to: AbsolutePath(path: "/foo/bar"))) - XCTAssertFalse(AbsolutePath(path: "/bar").isAncestorOfOrEqual(to: AbsolutePath(path: "/foo/bar"))) - - XCTAssertFalse(AbsolutePath(path: "/foo/bar").isAncestor(of: AbsolutePath(path: "/foo/bar"))) - XCTAssertTrue(AbsolutePath(path: "/foo").isAncestor(of: AbsolutePath(path: "/foo/bar"))) + XCTAssertTrue(AbsolutePath("/a/b/c/d/e/f").isDescendantOfOrEqual(to: AbsolutePath("/a/b/c/d"))) + XCTAssertTrue(AbsolutePath("/a/b/c/d/e/f.swift").isDescendantOfOrEqual(to: AbsolutePath("/a/b/c"))) + XCTAssertTrue(AbsolutePath("/").isDescendantOfOrEqual(to: AbsolutePath("/"))) + XCTAssertTrue(AbsolutePath("/foo/bar").isDescendantOfOrEqual(to: AbsolutePath("/"))) + XCTAssertFalse(AbsolutePath("/foo/bar").isDescendantOfOrEqual(to: AbsolutePath("/foo/bar/baz"))) + XCTAssertFalse(AbsolutePath("/foo/bar").isDescendantOfOrEqual(to: AbsolutePath("/bar"))) + + XCTAssertFalse(AbsolutePath("/foo/bar").isDescendant(of: AbsolutePath("/foo/bar"))) + XCTAssertTrue(AbsolutePath("/foo/bar").isDescendant(of: AbsolutePath("/foo"))) + + XCTAssertTrue(AbsolutePath("/a/b/c/d").isAncestorOfOrEqual(to: AbsolutePath("/a/b/c/d/e/f"))) + XCTAssertTrue(AbsolutePath("/a/b/c").isAncestorOfOrEqual(to: AbsolutePath("/a/b/c/d/e/f.swift"))) + XCTAssertTrue(AbsolutePath("/").isAncestorOfOrEqual(to: AbsolutePath("/"))) + XCTAssertTrue(AbsolutePath("/").isAncestorOfOrEqual(to: AbsolutePath("/foo/bar"))) + XCTAssertFalse(AbsolutePath("/foo/bar/baz").isAncestorOfOrEqual(to: AbsolutePath("/foo/bar"))) + XCTAssertFalse(AbsolutePath("/bar").isAncestorOfOrEqual(to: AbsolutePath("/foo/bar"))) + + XCTAssertFalse(AbsolutePath("/foo/bar").isAncestor(of: AbsolutePath("/foo/bar"))) + XCTAssertTrue(AbsolutePath("/foo").isAncestor(of: AbsolutePath("/foo/bar"))) } func testAbsolutePathValidation() { @@ -328,12 +328,13 @@ class PathTests: XCTestCase { XCTAssertNoThrow(try RelativePath(validating: "a/b/c/d")) XCTAssertThrowsError(try RelativePath(validating: "/a/b/d")) { error in - XCTAssertEqual("\(error)", "invalid relative path '/a/b/d'; relative path should not begin with '/' or '~'") + XCTAssertEqual("\(error)", "invalid relative path '/a/b/d'; relative path should not begin with '/'") + //XCTAssertEqual("\(error)", "invalid relative path '/a/b/d'; relative path should not begin with '/' or '~'") } - XCTAssertThrowsError(try RelativePath(validating: "~/a/b/d")) { error in + /*XCTAssertThrowsError(try RelativePath(validating: "~/a/b/d")) { error in XCTAssertEqual("\(error)", "invalid relative path '~/a/b/d'; relative path should not begin with '/' or '~'") - } + }*/ } func testCodable() throws { @@ -350,14 +351,14 @@ class PathTests: XCTestCase { } do { - let foo = Foo(path: AbsolutePath(path: "/path/to/foo")) + let foo = Foo(path: "/path/to/foo") let data = try JSONEncoder().encode(foo) let decodedFoo = try JSONDecoder().decode(Foo.self, from: data) XCTAssertEqual(foo, decodedFoo) } do { - let foo = Foo(path: AbsolutePath(path: "/path/to/../to/foo")) + let foo = Foo(path: "/path/to/../to/foo") let data = try JSONEncoder().encode(foo) let decodedFoo = try JSONDecoder().decode(Foo.self, from: data) XCTAssertEqual(foo, decodedFoo) @@ -366,14 +367,14 @@ class PathTests: XCTestCase { } do { - let bar = Bar(path: RelativePath("path/to/bar")) + let bar = Bar(path: "path/to/bar") let data = try JSONEncoder().encode(bar) let decodedBar = try JSONDecoder().decode(Bar.self, from: data) XCTAssertEqual(bar, decodedBar) } do { - let bar = Bar(path: RelativePath("path/to/../to/bar")) + let bar = Bar(path: "path/to/../to/bar") let data = try JSONEncoder().encode(bar) let decodedBar = try JSONDecoder().decode(Bar.self, from: data) XCTAssertEqual(bar, decodedBar) @@ -403,4 +404,19 @@ class PathTests: XCTestCase { // FIXME: We also need tests for dirname, basename, suffix, etc. // FIXME: We also need test for stat() operations. + + #if os(Windows) + func testNormalization() { + XCTAssertEqual( + AbsolutePath(#"C:\Users\compnerd\AppData\Local\Programs\Swift\Toolchains\0.0.0+Asserts\usr\bin\swiftc.exe"#) + .pathString, + #"C:\Users\compnerd\AppData\Local\Programs\Swift\Toolchains\0.0.0+Asserts\usr\bin\swiftc.exe"# + ) + XCTAssertEqual( + AbsolutePath(#"c:\Users\compnerd\AppData\Local\Programs\Swift\Toolchains\0.0.0+Asserts\usr\bin\swiftc.exe"#) + .pathString, + #"C:\Users\compnerd\AppData\Local\Programs\Swift\Toolchains\0.0.0+Asserts\usr\bin\swiftc.exe"# + ) + } + #endif } diff --git a/Tests/TSCBasicTests/ProcessSetTests.swift b/Tests/TSCBasicTests/ProcessSetTests.swift index 700605d6..e6f98c11 100644 --- a/Tests/TSCBasicTests/ProcessSetTests.swift +++ b/Tests/TSCBasicTests/ProcessSetTests.swift @@ -17,7 +17,7 @@ import TSCTestSupport class ProcessSetTests: XCTestCase { #if !os(Windows) // Signals are not supported in Windows func script(_ name: String) -> String { - return AbsolutePath(path: #file).parentDirectory.appending(components: "processInputs", name).pathString + return AbsolutePath(#file).parentDirectory.appending(components: "processInputs", name).pathString } func testSigInt() throws { diff --git a/Tests/TSCBasicTests/ProcessTests.swift b/Tests/TSCBasicTests/ProcessTests.swift index cb5f98df..d9eb3aca 100644 --- a/Tests/TSCBasicTests/ProcessTests.swift +++ b/Tests/TSCBasicTests/ProcessTests.swift @@ -50,7 +50,7 @@ class ProcessTests: XCTestCase { try withTemporaryFile { file in let count = 10_000 let stream = BufferedOutputByteStream() - stream <<< Format.asRepeating(string: "a", count: count) + stream.send(Format.asRepeating(string: "a", count: count)) try localFileSystem.writeFileContents(file.path, bytes: stream.bytes) #if os(Windows) let cat = "cat.exe" @@ -466,16 +466,11 @@ class ProcessTests: XCTestCase { fileprivate extension Process { private static func env() -> [String:String] { - var env = ProcessEnv.vars - #if os(macOS) - // Many of these tests use Python which might not be in the default `PATH` when running these tests from Xcode. - env["PATH"] = "\(env["PATH"] ?? ""):/usr/local/bin" - #endif - return env + return ProcessEnv.vars } private static func script(_ name: String) -> String { - return AbsolutePath(path: #file).parentDirectory.appending(components: "processInputs", name).pathString + return AbsolutePath(#file).parentDirectory.appending(components: "processInputs", name).pathString } convenience init(scriptName: String, arguments: [String] = [], outputRedirection: OutputRedirection = .collect) { diff --git a/Tests/TSCBasicTests/SHA256Tests.swift b/Tests/TSCBasicTests/SHA256Tests.swift index 396c658c..967bdfe7 100644 --- a/Tests/TSCBasicTests/SHA256Tests.swift +++ b/Tests/TSCBasicTests/SHA256Tests.swift @@ -32,7 +32,7 @@ class SHA256Tests: XCTestCase { let byte = "f" let stream = BufferedOutputByteStream() for _ in 0..<20000 { - stream <<< byte + stream.send(byte) } XCTAssertEqual(sha256.hash(stream.bytes).hexadecimalRepresentation, "23d00697ba26b4140869bab958431251e7e41982794d41b605b6a1d5dee56abf") } diff --git a/Tests/TSCBasicTests/TemporaryFileTests.swift b/Tests/TSCBasicTests/TemporaryFileTests.swift index 2d724673..de3c8197 100644 --- a/Tests/TSCBasicTests/TemporaryFileTests.swift +++ b/Tests/TSCBasicTests/TemporaryFileTests.swift @@ -26,9 +26,9 @@ class TemporaryFileTests: XCTestCase { // Try writing some data to the file. let stream = BufferedOutputByteStream() - stream <<< "foo" - stream <<< "bar" - stream <<< "baz" + stream.send("foo") + stream.send("bar") + stream.send("baz") try localFileSystem.writeFileContents(file.path, bytes: stream.bytes) // Go to the beginning of the file. @@ -50,9 +50,9 @@ class TemporaryFileTests: XCTestCase { // Try writing some data to the file. let stream = BufferedOutputByteStream() - stream <<< "foo" - stream <<< "bar" - stream <<< "baz" + stream.send("foo") + stream.send("bar") + stream.send("baz") try localFileSystem.writeFileContents(file.path, bytes: stream.bytes) // Go to the beginning of the file. diff --git a/Tests/TSCBasicTests/WritableByteStreamTests.swift b/Tests/TSCBasicTests/WritableByteStreamTests.swift index 2a12b904..e9b2eef4 100644 --- a/Tests/TSCBasicTests/WritableByteStreamTests.swift +++ b/Tests/TSCBasicTests/WritableByteStreamTests.swift @@ -34,7 +34,12 @@ class WritableByteStreamTests: XCTestCase { func testStreamOperator() { let stream = BufferedOutputByteStream() - stream <<< "Hello" <<< Character(",") <<< Character(" ") <<< [UInt8]("wor".utf8) <<< [UInt8]("world".utf8)[3..<5] + stream + .send("Hello") + .send(Character(",")) + .send(Character(" ")) + .send([UInt8]("wor".utf8)) + .send([UInt8]("world".utf8)[3..<5]) XCTAssertEqual(stream.position, "Hello, world".utf8.count) XCTAssertEqual(stream.bytes, "Hello, world") @@ -47,15 +52,15 @@ class WritableByteStreamTests: XCTestCase { let bigBlock = [UInt8](repeating: 1, count: blockSize) var stream = BufferedOutputByteStream() - stream <<< smallBlock <<< bigBlock + stream.send(smallBlock).send(bigBlock) XCTAssertEqual(stream.bytes, ByteString(smallBlock + bigBlock)) stream = BufferedOutputByteStream() - stream <<< bigBlock <<< smallBlock + stream.send(bigBlock).send(smallBlock) XCTAssertEqual(stream.bytes, ByteString(bigBlock + smallBlock)) stream = BufferedOutputByteStream() - stream <<< bigBlock <<< bigBlock + stream.send(bigBlock).send(bigBlock) XCTAssertEqual(stream.bytes, ByteString(bigBlock + bigBlock)) } } @@ -77,34 +82,34 @@ class WritableByteStreamTests: XCTestCase { // Test other random types. var stream = BufferedOutputByteStream() - stream <<< Format.asJSON(false) + stream.send(Format.asJSON(false)) XCTAssertEqual(stream.bytes, "false") stream = BufferedOutputByteStream() - stream <<< Format.asJSON(1 as Int) + stream .send(Format.asJSON(1 as Int)) XCTAssertEqual(stream.bytes, "1") stream = BufferedOutputByteStream() - stream <<< Format.asJSON(1.2 as Double) + stream.send(Format.asJSON(1.2 as Double)) XCTAssertEqual(stream.bytes, "1.2") } func testFormattedOutput() { do { let stream = BufferedOutputByteStream() - stream <<< Format.asJSON("\n") + stream.send(Format.asJSON("\n")) XCTAssertEqual(stream.bytes, "\"\\n\"") } do { let stream = BufferedOutputByteStream() - stream <<< Format.asJSON(["hello", "world\n"]) + stream.send(Format.asJSON(["hello", "world\n"])) XCTAssertEqual(stream.bytes, "[\"hello\",\"world\\n\"]") } do { let stream = BufferedOutputByteStream() - stream <<< Format.asJSON(["hello": "world\n"]) + stream.send(Format.asJSON(["hello": "world\n"])) XCTAssertEqual(stream.bytes, "{\"hello\":\"world\\n\"}") } @@ -114,13 +119,13 @@ class WritableByteStreamTests: XCTestCase { init(_ value: String) { self.value = value } } let stream = BufferedOutputByteStream() - stream <<< Format.asJSON([MyThing("hello"), MyThing("world\n")], transform: { $0.value }) + stream.send(Format.asJSON([MyThing("hello"), MyThing("world\n")], transform: { $0.value })) XCTAssertEqual(stream.bytes, "[\"hello\",\"world\\n\"]") } do { let stream = BufferedOutputByteStream() - stream <<< Format.asSeparatedList(["hello", "world"], separator: ", ") + stream.send(Format.asSeparatedList(["hello", "world"], separator: ", ")) XCTAssertEqual(stream.bytes, "hello, world") } @@ -130,25 +135,25 @@ class WritableByteStreamTests: XCTestCase { init(_ value: String) { self.value = value } } let stream = BufferedOutputByteStream() - stream <<< Format.asSeparatedList([MyThing("hello"), MyThing("world")], transform: { $0.value }, separator: ", ") + stream.send(Format.asSeparatedList([MyThing("hello"), MyThing("world")], transform: { $0.value }, separator: ", ")) XCTAssertEqual(stream.bytes, "hello, world") } do { var stream = BufferedOutputByteStream() - stream <<< Format.asRepeating(string: "foo", count: 1) + stream.send(Format.asRepeating(string: "foo", count: 1)) XCTAssertEqual(stream.bytes, "foo") stream = BufferedOutputByteStream() - stream <<< Format.asRepeating(string: "foo", count: 0) + stream.send(Format.asRepeating(string: "foo", count: 0)) XCTAssertEqual(stream.bytes, "") stream = BufferedOutputByteStream() - stream <<< Format.asRepeating(string: "x", count: 4) + stream.send(Format.asRepeating(string: "x", count: 4)) XCTAssertEqual(stream.bytes, "xxxx") stream = BufferedOutputByteStream() - stream <<< Format.asRepeating(string: "foo", count: 3) + stream.send(Format.asRepeating(string: "foo", count: 3)) XCTAssertEqual(stream.bytes, "foofoofoo") } } @@ -161,11 +166,11 @@ class WritableByteStreamTests: XCTestCase { } let stream = try LocalFileOutputByteStream(tempFile.path) - stream <<< "Hello" + stream.send("Hello") stream.flush() XCTAssertEqual(read(), "Hello") - stream <<< " World" + stream.send(" World") try stream.close() XCTAssertEqual(read(), "Hello World") @@ -196,7 +201,7 @@ class WritableByteStreamTests: XCTestCase { let t1 = Thread { for _ in 0..<1000 { - threadSafeStream <<< "Hello" + threadSafeStream.send("Hello") } } threads.append(t1) diff --git a/Tests/TSCBasicTests/miscTests.swift b/Tests/TSCBasicTests/miscTests.swift index c4e8ebc9..0f9443f1 100644 --- a/Tests/TSCBasicTests/miscTests.swift +++ b/Tests/TSCBasicTests/miscTests.swift @@ -48,13 +48,13 @@ class miscTests: XCTestCase { } func testEnvSearchPaths() throws { - let cwd = AbsolutePath(path: "/dummy") + let cwd = AbsolutePath("/dummy") let paths = getEnvSearchPaths(pathString: "something:.:abc/../.build/debug:/usr/bin:/bin/", currentWorkingDirectory: cwd) XCTAssertEqual(paths, try ["/dummy/something", "/dummy", "/dummy/.build/debug", "/usr/bin", "/bin"].map({ try AbsolutePath(validating: $0)})) } func testEmptyEnvSearchPaths() throws { - let cwd = AbsolutePath(path: "/dummy") + let cwd = AbsolutePath("/dummy") let paths = getEnvSearchPaths(pathString: "", currentWorkingDirectory: cwd) XCTAssertEqual(paths, []) diff --git a/Tests/TSCBasicTests/processInputs/deadlock-if-blocking-io b/Tests/TSCBasicTests/processInputs/deadlock-if-blocking-io index 88ad54d0..9bc79efe 100755 --- a/Tests/TSCBasicTests/processInputs/deadlock-if-blocking-io +++ b/Tests/TSCBasicTests/processInputs/deadlock-if-blocking-io @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import sys diff --git a/Tests/TSCBasicTests/processInputs/in-to-out b/Tests/TSCBasicTests/processInputs/in-to-out index 3b86f0d9..94490b16 100755 --- a/Tests/TSCBasicTests/processInputs/in-to-out +++ b/Tests/TSCBasicTests/processInputs/in-to-out @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import sys diff --git a/Tests/TSCBasicTests/processInputs/long-stdout-stderr b/Tests/TSCBasicTests/processInputs/long-stdout-stderr index d630a026..684fcca5 100755 --- a/Tests/TSCBasicTests/processInputs/long-stdout-stderr +++ b/Tests/TSCBasicTests/processInputs/long-stdout-stderr @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import sys diff --git a/Tests/TSCBasicTests/processInputs/simple-stdout-stderr b/Tests/TSCBasicTests/processInputs/simple-stdout-stderr index c4f58949..a840caee 100755 --- a/Tests/TSCBasicTests/processInputs/simple-stdout-stderr +++ b/Tests/TSCBasicTests/processInputs/simple-stdout-stderr @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import sys diff --git a/Tests/TSCUtilityTests/BitstreamTests.swift b/Tests/TSCUtilityTests/BitstreamTests.swift index bc099a95..700f13b3 100644 --- a/Tests/TSCUtilityTests/BitstreamTests.swift +++ b/Tests/TSCUtilityTests/BitstreamTests.swift @@ -33,7 +33,7 @@ final class BitstreamTests: XCTestCase { } } - let bitstreamPath = AbsolutePath(path: #file).parentDirectory + let bitstreamPath = AbsolutePath(#file).parentDirectory .appending(components: "Inputs", "serialized.dia") let contents = try localFileSystem.readFileContents(bitstreamPath) var visitor = LoggingVisitor() @@ -126,7 +126,7 @@ final class BitstreamTests: XCTestCase { } } - let bitstreamPath = AbsolutePath(path: #file).parentDirectory + let bitstreamPath = AbsolutePath(#file).parentDirectory .appending(components: "Inputs", "serialized.dia") let contents = try localFileSystem.readFileContents(bitstreamPath) var visitor = LoggingVisitor() diff --git a/Tests/TSCUtilityTests/PkgConfigParserTests.swift b/Tests/TSCUtilityTests/PkgConfigParserTests.swift index 1c97bb74..7e4c3c7f 100644 --- a/Tests/TSCUtilityTests/PkgConfigParserTests.swift +++ b/Tests/TSCUtilityTests/PkgConfigParserTests.swift @@ -127,10 +127,10 @@ final class PkgConfigParserTests: XCTestCase { try localFileSystem.createDirectory(fakePkgConfig.parentDirectory) let stream = BufferedOutputByteStream() - stream <<< """ + stream.send(""" #!/bin/sh echo "/Volumes/BestDrive/pkgconfig" - """ + """) try localFileSystem.writeFileContents(fakePkgConfig, bytes: stream.bytes) // `FileSystem` does not support `chmod` on Linux, so we shell out instead. _ = try Process.popen(args: "chmod", "+x", fakePkgConfig.pathString) diff --git a/Tests/TSCUtilityTests/PlatformTests.swift b/Tests/TSCUtilityTests/PlatformTests.swift index d995943c..72abeb06 100644 --- a/Tests/TSCUtilityTests/PlatformTests.swift +++ b/Tests/TSCUtilityTests/PlatformTests.swift @@ -14,6 +14,7 @@ import TSCTestSupport @testable import TSCUtility +@available(*, deprecated) final class PlatformTests: XCTestCase { func testFindCurrentPlatformDebian() { let fs = InMemoryFileSystem(files: ["/etc/debian_version": "xxx"]) diff --git a/Tests/TSCUtilityTests/SerializedDiagnosticsTests.swift b/Tests/TSCUtilityTests/SerializedDiagnosticsTests.swift index 469d0bb7..2a024e85 100644 --- a/Tests/TSCUtilityTests/SerializedDiagnosticsTests.swift +++ b/Tests/TSCUtilityTests/SerializedDiagnosticsTests.swift @@ -15,7 +15,7 @@ import TSCUtility final class SerializedDiagnosticsTests: XCTestCase { func testReadSwiftDiagnosticWithNote() throws { - let serializedDiagnosticsPath = AbsolutePath(path: #file).parentDirectory + let serializedDiagnosticsPath = AbsolutePath(#file).parentDirectory .appending(components: "Inputs", "multiblock.dia") let contents = try localFileSystem.readFileContents(serializedDiagnosticsPath) let serializedDiags = try SerializedDiagnostics(bytes: contents) @@ -97,7 +97,7 @@ final class SerializedDiagnosticsTests: XCTestCase { } func testReadSwiftSerializedDiags() throws { - let serializedDiagnosticsPath = AbsolutePath(path: #file).parentDirectory + let serializedDiagnosticsPath = AbsolutePath(#file).parentDirectory .appending(components: "Inputs", "serialized.dia") let contents = try localFileSystem.readFileContents(serializedDiagnosticsPath) let serializedDiags = try SerializedDiagnostics(bytes: contents) @@ -145,7 +145,7 @@ final class SerializedDiagnosticsTests: XCTestCase { } func testReadDiagsWithNoLocation() throws { - let serializedDiagnosticsPath = AbsolutePath(path: #file).parentDirectory + let serializedDiagnosticsPath = AbsolutePath(#file).parentDirectory .appending(components: "Inputs", "no-location.dia") let contents = try localFileSystem.readFileContents(serializedDiagnosticsPath) let serializedDiags = try SerializedDiagnostics(bytes: contents) @@ -164,7 +164,7 @@ final class SerializedDiagnosticsTests: XCTestCase { } func testReadDiagsWithUnknownRecord() throws { - let serializedDiagnosticsPath = AbsolutePath(path: #file).parentDirectory + let serializedDiagnosticsPath = AbsolutePath(#file).parentDirectory .appending(components: "Inputs", "string_init_ambig.dia") let contents = try localFileSystem.readFileContents(serializedDiagnosticsPath) let serializedDiags = try SerializedDiagnostics(bytes: contents) @@ -174,7 +174,7 @@ final class SerializedDiagnosticsTests: XCTestCase { } func testReadClangSerializedDiags() throws { - let serializedDiagnosticsPath = AbsolutePath(path: #file).parentDirectory + let serializedDiagnosticsPath = AbsolutePath(#file).parentDirectory .appending(components: "Inputs", "clang.dia") let contents = try localFileSystem.readFileContents(serializedDiagnosticsPath) let serializedDiags = try SerializedDiagnostics(bytes: contents) diff --git a/Tests/TSCUtilityTests/SimplePersistenceTests.swift b/Tests/TSCUtilityTests/SimplePersistenceTests.swift index a4ed5ed5..aea5cccd 100644 --- a/Tests/TSCUtilityTests/SimplePersistenceTests.swift +++ b/Tests/TSCUtilityTests/SimplePersistenceTests.swift @@ -119,7 +119,7 @@ class SimplePersistenceTests: XCTestCase { func testBasics() throws { let fs = InMemoryFileSystem() let stateFile = AbsolutePath.root.appending(components: "subdir", "state.json") - let foo = Foo(int: 1, path: AbsolutePath(path: "/hello"), fileSystem: fs) + let foo = Foo(int: 1, path: "/hello", fileSystem: fs) // Restoring right now should return false because state is not present. XCTAssertFalse(try foo.restore()) @@ -133,7 +133,7 @@ class SimplePersistenceTests: XCTestCase { foo.int = 5 XCTAssertTrue(try foo.restore()) XCTAssertEqual(foo.int, 1) - XCTAssertEqual(foo.path, AbsolutePath(path: "/hello")) + XCTAssertEqual(foo.path, "/hello") // Modify state's schema version. let newJSON = JSON(["version": 2]) @@ -183,7 +183,7 @@ class SimplePersistenceTests: XCTestCase { let fs = InMemoryFileSystem() let stateFile = AbsolutePath.root.appending(components: "subdir", "state.json") try fs.writeFileContents(stateFile) { - $0 <<< """ + $0.send(""" { "version": 0, "object": { @@ -191,17 +191,17 @@ class SimplePersistenceTests: XCTestCase { "old_int": 4 } } - """ + """) } - let foo = Foo(int: 1, path: AbsolutePath(path: "/hello"), fileSystem: fs) - XCTAssertEqual(foo.path, AbsolutePath(path: "/hello")) + let foo = Foo(int: 1, path: "/hello", fileSystem: fs) + XCTAssertEqual(foo.path, "/hello") XCTAssertEqual(foo.int, 1) // Load from an older but supported schema state file. XCTAssertTrue(try foo.restore()) - XCTAssertEqual(foo.path, AbsolutePath(path: "/oldpath")) + XCTAssertEqual(foo.path, "/oldpath") XCTAssertEqual(foo.int, 4) } } diff --git a/Tests/TSCUtilityTests/TripleTests.swift b/Tests/TSCUtilityTests/TripleTests.swift index a9c2c3df..68caa7c8 100644 --- a/Tests/TSCUtilityTests/TripleTests.swift +++ b/Tests/TSCUtilityTests/TripleTests.swift @@ -11,6 +11,7 @@ import TSCUtility import XCTest +@available(*, deprecated) class TripleTests : XCTestCase { func testTriple() { let linux = try? Triple("x86_64-unknown-linux-gnu") diff --git a/Utilities/ci.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Utilities/ci.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 00000000..6c5bf7fb --- /dev/null +++ b/Utilities/ci.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,7 @@ +{ + "object": { + "pins": [ + ] + }, + "version": 1 +}