Skip to content

Commit 3aa64f1

Browse files
authored
Merge pull request #3009 from abertelrud/vendor-latest-tools-support-core
Sync latest TSC changes from `main`
2 parents 9e1a4e8 + 1b52f4a commit 3aa64f1

29 files changed

+1021
-252
lines changed

swift-tools-support-core/.swiftformat

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# file options
2+
3+
--swiftversion 5.0
4+
--exclude .build
5+
6+
# format options
7+
8+
--self insert
9+
--patternlet inline
10+
--stripunusedargs unnamed-only
11+
--ifdef no-indent
12+
13+
# rules

swift-tools-support-core/Sources/TSCBasic/ByteString.swift

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -108,11 +108,7 @@ public struct ByteString: ExpressibleByArrayLiteral, Hashable {
108108
extension ByteString: CustomStringConvertible {
109109
/// Return the string decoded as a UTF8 sequence, or traps if not possible.
110110
public var description: String {
111-
guard let description = validDescription else {
112-
fatalError("invalid byte string: \(cString)")
113-
}
114-
115-
return description
111+
return cString
116112
}
117113

118114
/// Return the string decoded as a UTF8 sequence, if possible.

swift-tools-support-core/Sources/TSCBasic/CMakeLists.txt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ add_library(TSCBasic
4949
Thread.swift
5050
Tuple.swift
5151
misc.swift)
52+
5253
target_compile_options(TSCBasic PUBLIC
5354
# Don't use GNU strerror_r on Android.
5455
"$<$<PLATFORM_ID:Android>:SHELL:-Xcc -U_GNU_SOURCE>"
@@ -57,8 +58,10 @@ target_compile_options(TSCBasic PUBLIC
5758
target_link_libraries(TSCBasic PUBLIC
5859
TSCLibc)
5960
if(NOT CMAKE_SYSTEM_NAME STREQUAL Darwin)
60-
target_link_libraries(TSCBasic PUBLIC
61-
Foundation)
61+
if(Foundation_FOUND)
62+
target_link_libraries(TSCBasic PUBLIC
63+
Foundation)
64+
endif()
6265
endif()
6366
target_link_libraries(TSCBasic PRIVATE
6467
$<$<PLATFORM_ID:Windows>:Pathcch>)

swift-tools-support-core/Sources/TSCBasic/FileSystem.swift

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
import TSCLibc
1212
import Foundation
13+
import Dispatch
1314

1415
public enum FileSystemError: Swift.Error {
1516
/// Access to the path is denied.
@@ -200,6 +201,9 @@ public protocol FileSystem: class {
200201

201202
/// Move a file or directory.
202203
func move(from sourcePath: AbsolutePath, to destinationPath: AbsolutePath) throws
204+
205+
/// Execute the given block while holding the lock.
206+
func withLock<T>(on path: AbsolutePath, type: FileLock.LockType, _ body: () throws -> T) throws -> T
203207
}
204208

205209
/// Convenience implementations (default arguments aren't permitted in protocol
@@ -240,6 +244,10 @@ public extension FileSystem {
240244
func getFileInfo(_ path: AbsolutePath) throws -> FileInfo {
241245
throw FileSystemError.unsupported
242246
}
247+
248+
func withLock<T>(on path: AbsolutePath, type: FileLock.LockType, _ body: () throws -> T) throws -> T {
249+
throw FileSystemError.unsupported
250+
}
243251
}
244252

245253
/// Concrete FileSystem implementation which communicates with the local file system.
@@ -450,6 +458,11 @@ private class LocalFileSystem: FileSystem {
450458
guard !exists(destinationPath) else { throw FileSystemError.alreadyExistsAtDestination }
451459
try FileManager.default.moveItem(at: sourcePath.asURL, to: destinationPath.asURL)
452460
}
461+
462+
func withLock<T>(on path: AbsolutePath, type: FileLock.LockType = .exclusive, _ body: () throws -> T) throws -> T {
463+
let lock = FileLock(name: path.basename, cachePath: path.parentDirectory)
464+
return try lock.withLock(type: type, body)
465+
}
453466
}
454467

455468
// FIXME: This class does not yet support concurrent mutation safely.
@@ -499,9 +512,21 @@ public class InMemoryFileSystem: FileSystem {
499512
return contents
500513
}
501514
}
515+
// Used to ensure that DispatchQueues are releassed when they are no longer in use.
516+
private struct WeakReference<Value: AnyObject> {
517+
weak var reference: Value?
518+
519+
init(_ value: Value?) {
520+
self.reference = value
521+
}
522+
}
502523

503524
/// The root filesytem.
504525
private var root: Node
526+
/// A map that keeps weak references to all locked files.
527+
private var lockFiles = Dictionary<AbsolutePath, WeakReference<DispatchQueue>>()
528+
/// Used to access lockFiles in a thread safe manner.
529+
private let lockFilesLock = Lock()
505530

506531
public init() {
507532
root = Node(.directory(DirectoryContents()))
@@ -760,6 +785,21 @@ public class InMemoryFileSystem: FileSystem {
760785

761786
contents.entries[sourcePath.basename] = nil
762787
}
788+
789+
public func withLock<T>(on path: AbsolutePath, type: FileLock.LockType = .exclusive, _ body: () throws -> T) throws -> T {
790+
791+
let fileQueue: DispatchQueue = lockFilesLock.withLock {
792+
if let queueReference = lockFiles[path], let queue = queueReference.reference {
793+
return queue
794+
} else {
795+
let queue = DispatchQueue(label: "org.swift.swiftpm.in-memory-file-system.file-queue", attributes: .concurrent)
796+
lockFiles[path] = WeakReference(queue)
797+
return queue
798+
}
799+
}
800+
801+
return try fileQueue.sync(flags: type == .exclusive ? .barrier : .init() , execute: body)
802+
}
763803
}
764804

765805
/// A rerooted view on an existing FileSystem.
@@ -864,6 +904,10 @@ public class RerootedFileSystemView: FileSystem {
864904
public func move(from sourcePath: AbsolutePath, to destinationPath: AbsolutePath) throws {
865905
try underlyingFileSystem.move(from: formUnderlyingPath(sourcePath), to: formUnderlyingPath(sourcePath))
866906
}
907+
908+
public func withLock<T>(on path: AbsolutePath, type: FileLock.LockType = .exclusive, _ body: () throws -> T) throws -> T {
909+
return try underlyingFileSystem.withLock(on: formUnderlyingPath(path), type: type, body)
910+
}
867911
}
868912

869913
/// Public access to the local FS proxy.

swift-tools-support-core/Sources/TSCBasic/HashAlgorithms.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ public struct SHA256: HashAlgorithm {
174174
}
175175

176176
/// Wraps CryptoKit.SHA256 to provide a HashAlgorithm conformance to it.
177-
@available(macOS 10.15, *)
177+
@available(macOS 10.15, iOS 13, *)
178178
public struct CryptoKitSHA256: HashAlgorithm {
179179
public init() {
180180
}

swift-tools-support-core/Sources/TSCBasic/Lock.swift

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,18 @@ public struct Lock {
1919
public init() {
2020
}
2121

22+
func lock() {
23+
_lock.lock()
24+
}
25+
26+
func unlock() {
27+
_lock.unlock()
28+
}
29+
2230
/// Execute the given block while holding the lock.
2331
public func withLock<T> (_ body: () throws -> T) rethrows -> T {
24-
_lock.lock()
25-
defer { _lock.unlock() }
32+
lock()
33+
defer { unlock() }
2634
return try body()
2735
}
2836
}
@@ -35,6 +43,12 @@ enum ProcessLockError: Swift.Error {
3543
/// It can be used for things like serializing concurrent mutations on a shared resource
3644
/// by mutiple instances of a process. The `FileLock` is not thread-safe.
3745
public final class FileLock {
46+
47+
public enum LockType {
48+
case exclusive
49+
case shared
50+
}
51+
3852
/// File descriptor to the lock file.
3953
#if os(Windows)
4054
private var handle: HANDLE?
@@ -56,14 +70,14 @@ public final class FileLock {
5670
/// Try to aquire a lock. This method will block until lock the already aquired by other process.
5771
///
5872
/// Note: This method can throw if underlying POSIX methods fail.
59-
public func lock() throws {
73+
public func lock(type: LockType = .exclusive) throws {
6074
#if os(Windows)
6175
if handle == nil {
62-
let h = lockFile.pathString.withCString(encodedAs: UTF16.self, {
76+
let h: HANDLE = lockFile.pathString.withCString(encodedAs: UTF16.self, {
6377
CreateFileW(
6478
$0,
6579
UInt32(GENERIC_READ) | UInt32(GENERIC_WRITE),
66-
0,
80+
UInt32(FILE_SHARE_READ) | UInt32(FILE_SHARE_WRITE),
6781
nil,
6882
DWORD(OPEN_ALWAYS),
6983
DWORD(FILE_ATTRIBUTE_NORMAL),
@@ -79,9 +93,17 @@ public final class FileLock {
7993
overlapped.Offset = 0
8094
overlapped.OffsetHigh = 0
8195
overlapped.hEvent = nil
82-
if !LockFileEx(handle, DWORD(LOCKFILE_EXCLUSIVE_LOCK), 0,
83-
DWORD(INT_MAX), DWORD(INT_MAX), &overlapped) {
84-
throw ProcessLockError.unableToAquireLock(errno: Int32(GetLastError()))
96+
switch type {
97+
case .exclusive:
98+
if !LockFileEx(handle, DWORD(LOCKFILE_EXCLUSIVE_LOCK), 0,
99+
DWORD(INT_MAX), DWORD(INT_MAX), &overlapped) {
100+
throw ProcessLockError.unableToAquireLock(errno: Int32(GetLastError()))
101+
}
102+
case .shared:
103+
if !LockFileEx(handle, 0, 0,
104+
DWORD(INT_MAX), DWORD(INT_MAX), &overlapped) {
105+
throw ProcessLockError.unableToAquireLock(errno: Int32(GetLastError()))
106+
}
85107
}
86108
#else
87109
// Open the lock file.
@@ -94,7 +116,9 @@ public final class FileLock {
94116
}
95117
// Aquire lock on the file.
96118
while true {
97-
if flock(fileDescriptor!, LOCK_EX) == 0 {
119+
if type == .exclusive && flock(fileDescriptor!, LOCK_EX) == 0 {
120+
break
121+
} else if type == .shared && flock(fileDescriptor!, LOCK_SH) == 0 {
98122
break
99123
}
100124
// Retry if interrupted.
@@ -129,8 +153,8 @@ public final class FileLock {
129153
}
130154

131155
/// Execute the given block while holding the lock.
132-
public func withLock<T>(_ body: () throws -> T) throws -> T {
133-
try lock()
156+
public func withLock<T>(type: LockType = .exclusive, _ body: () throws -> T) throws -> T {
157+
try lock(type: type)
134158
defer { unlock() }
135159
return try body()
136160
}

swift-tools-support-core/Sources/TSCBasic/PathShims.swift

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,13 @@ import Foundation
2323
/// Returns the "real path" corresponding to `path` by resolving any symbolic links.
2424
public func resolveSymlinks(_ path: AbsolutePath) -> AbsolutePath {
2525
#if os(Windows)
26-
do {
27-
return try AbsolutePath(FileManager.default.destinationOfSymbolicLink(atPath: path.pathString).standardizingPath)
28-
} catch {
29-
return AbsolutePath(path.pathString.standardizingPath)
26+
var resolved: URL = URL(fileURLWithPath: path.pathString)
27+
if let destination = try? FileManager.default.destinationOfSymbolicLink(atPath: path.pathString) {
28+
resolved = URL(fileURLWithPath: destination, relativeTo: URL(fileURLWithPath: path.pathString))
29+
}
30+
31+
return resolved.standardized.withUnsafeFileSystemRepresentation {
32+
try! AbsolutePath(validating: String(cString: $0!))
3033
}
3134
#else
3235
let pathStr = path.pathString

swift-tools-support-core/Sources/TSCBasic/TerminalController.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
import TSCLibc
1212
#if os(Windows)
13-
import MSVCRT
13+
import CRT
1414
#endif
1515

1616
/// A class to have better control on tty output streams: standard output and standard error.

swift-tools-support-core/Sources/TSCBasic/Thread.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
*/
1010

1111
import Foundation
12+
#if os(Windows)
13+
import WinSDK
14+
#endif
1215

1316
/// This class bridges the gap between Darwin and Linux Foundation Threading API.
1417
/// It provides closure based execution and a join method to block the calling thread
@@ -64,6 +67,15 @@ final public class Thread {
6467
}
6568
}
6669
}
70+
71+
/// Causes the calling thread to yield execution to another thread.
72+
public static func yield() {
73+
#if os(Windows)
74+
SwitchToThread()
75+
#else
76+
sched_yield()
77+
#endif
78+
}
6779
}
6880

6981
#if canImport(Darwin)

swift-tools-support-core/Sources/TSCLibc/libc.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
#if canImport(Glibc)
1212
@_exported import Glibc
1313
#elseif os(Windows)
14-
@_exported import MSVCRT
14+
@_exported import CRT
1515
@_exported import WinSDK
1616
#else
1717
@_exported import Darwin.C

swift-tools-support-core/Sources/TSCTestSupport/misc.swift

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -25,27 +25,21 @@ public enum Configuration {
2525
}
2626

2727
/// Test helper utility for executing a block with a temporary directory.
28-
public func mktmpdir(
28+
public func testWithTemporaryDirectory(
2929
function: StaticString = #function,
30-
file: StaticString = #file,
31-
line: UInt = #line,
3230
body: (AbsolutePath) throws -> Void
33-
) {
34-
do {
35-
let cleanedFunction = function.description
36-
.replacingOccurrences(of: "(", with: "")
37-
.replacingOccurrences(of: ")", with: "")
38-
.replacingOccurrences(of: ".", with: "")
39-
try withTemporaryDirectory(prefix: "spm-tests-\(cleanedFunction)") { tmpDirPath in
40-
defer {
41-
// Unblock and remove the tmp dir on deinit.
42-
try? localFileSystem.chmod(.userWritable, path: tmpDirPath, options: [.recursive])
43-
try? localFileSystem.removeFileTree(tmpDirPath)
44-
}
45-
try body(tmpDirPath)
31+
) throws {
32+
let cleanedFunction = function.description
33+
.replacingOccurrences(of: "(", with: "")
34+
.replacingOccurrences(of: ")", with: "")
35+
.replacingOccurrences(of: ".", with: "")
36+
try withTemporaryDirectory(prefix: "spm-tests-\(cleanedFunction)") { tmpDirPath in
37+
defer {
38+
// Unblock and remove the tmp dir on deinit.
39+
try? localFileSystem.chmod(.userWritable, path: tmpDirPath, options: [.recursive])
40+
try? localFileSystem.removeFileTree(tmpDirPath)
4641
}
47-
} catch {
48-
XCTFail("\(error)", file: file, line: line)
42+
try body(tmpDirPath)
4943
}
5044
}
5145

swift-tools-support-core/Sources/TSCUtility/Bits.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,15 @@ struct Bits: RandomAccessCollection {
8484
return buffer.buffer.dropFirst(offset >> 3).prefix((newOffset - offset) >> 3)
8585
}
8686

87+
mutating func skip(bytes count: Int) throws {
88+
precondition(count >= 0)
89+
precondition(offset & 0b111 == 0)
90+
let newOffset = offset &+ (count << 3)
91+
precondition(newOffset >= offset)
92+
if newOffset > buffer.count { throw Error.bufferOverflow }
93+
offset = newOffset
94+
}
95+
8796
mutating func advance(toBitAlignment align: Int) throws {
8897
precondition(align > 0)
8998
precondition(offset &+ (align&-1) >= offset)

0 commit comments

Comments
 (0)