diff --git a/Documentation/Porting.md b/Documentation/Porting.md index 90c49bded..39d1d8e88 100644 --- a/Documentation/Porting.md +++ b/Documentation/Porting.md @@ -256,7 +256,7 @@ platform conditional and provide a stub implementation: +++ b/Sources/Testing/Support/FileHandle.swift var isTTY: Bool { - #if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(Android) || os(WASI) + #if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(OpenBSD) || os(Android) || os(WASI) // ... +#elseif os(Classic) + return false diff --git a/Package.swift b/Package.swift index 13f517976..a72f7086a 100644 --- a/Package.swift +++ b/Package.swift @@ -57,7 +57,7 @@ let package = Package( cxxSettings: .packageSettings, swiftSettings: .packageSettings, linkerSettings: [ - .linkedLibrary("execinfo", .when(platforms: [.custom("freebsd")])) + .linkedLibrary("execinfo", .when(platforms: [.custom("freebsd"), .openbsd])) ] ), .testTarget( @@ -127,7 +127,7 @@ let package = Package( ) // BUG: swift-package-manager-#6367 -#if !os(Windows) && !os(FreeBSD) +#if !os(Windows) && !os(FreeBSD) && !os(OpenBSD) package.targets.append(contentsOf: [ .testTarget( name: "TestingMacrosTests", @@ -156,7 +156,7 @@ extension Array where Element == PackageDescription.SwiftSetting { .define("SWT_NO_EXIT_TESTS", .when(platforms: [.iOS, .watchOS, .tvOS, .visionOS, .wasi, .android])), .define("SWT_NO_PROCESS_SPAWNING", .when(platforms: [.iOS, .watchOS, .tvOS, .visionOS, .wasi, .android])), - .define("SWT_NO_SNAPSHOT_TYPES", .when(platforms: [.linux, .custom("freebsd"), .windows, .wasi])), + .define("SWT_NO_SNAPSHOT_TYPES", .when(platforms: [.linux, .custom("freebsd"), .openbsd, .windows, .wasi])), .define("SWT_NO_DYNAMIC_LINKING", .when(platforms: [.wasi])), .define("SWT_NO_PIPES", .when(platforms: [.wasi])), ] @@ -191,7 +191,7 @@ extension Array where Element == PackageDescription.CXXSetting { result += [ .define("SWT_NO_EXIT_TESTS", .when(platforms: [.iOS, .watchOS, .tvOS, .visionOS, .wasi, .android])), .define("SWT_NO_PROCESS_SPAWNING", .when(platforms: [.iOS, .watchOS, .tvOS, .visionOS, .wasi, .android])), - .define("SWT_NO_SNAPSHOT_TYPES", .when(platforms: [.linux, .custom("freebsd"), .windows, .wasi])), + .define("SWT_NO_SNAPSHOT_TYPES", .when(platforms: [.linux, .custom("freebsd"), .openbsd, .windows, .wasi])), .define("SWT_NO_DYNAMIC_LINKING", .when(platforms: [.wasi])), .define("SWT_NO_PIPES", .when(platforms: [.wasi])), ] diff --git a/Sources/Overlays/_Testing_Foundation/Attachments/Attachment+URL.swift b/Sources/Overlays/_Testing_Foundation/Attachments/Attachment+URL.swift index 9d0a0a5f1..305bdd35b 100644 --- a/Sources/Overlays/_Testing_Foundation/Attachments/Attachment+URL.swift +++ b/Sources/Overlays/_Testing_Foundation/Attachments/Attachment+URL.swift @@ -164,10 +164,23 @@ private func _compressContentsOfDirectory(at directoryURL: URL) async throws -> // // On Linux (which does not have FreeBSD's version of tar(1)), we can use // zip(1) instead. + // + // OpenBSD's tar(1) does not support writing PKZIP archives, and /usr/bin/zip + // tool is an optional install, so we check if it's present before trying to + // execute it. #if os(Linux) let archiverPath = "/usr/bin/zip" #elseif SWT_TARGET_OS_APPLE || os(FreeBSD) let archiverPath = "/usr/bin/tar" +#elseif os(OpenBSD) + let archiverPath = "/usr/local/bin/zip" + var isDirectory = false + if !FileManager.default.fileExists(atPath: archiverPath, isDirectory: &isDirectory) || isDirectory { + throw CocoaError(.fileNoSuchFile, userInfo: [ + NSLocalizedDescriptionKey: "The 'zip' package is not installed.", + NSFilePathErrorKey: archiverPath + ]) + } #elseif os(Windows) guard let archiverPath = _archiverPath else { throw CocoaError(.fileWriteUnknown, userInfo: [ @@ -187,7 +200,7 @@ private func _compressContentsOfDirectory(at directoryURL: URL) async throws -> let sourcePath = directoryURL.fileSystemPath let destinationPath = temporaryURL.fileSystemPath -#if os(Linux) +#if os(Linux) || os(OpenBSD) // The zip command constructs relative paths from the current working // directory rather than from command-line arguments. process.arguments = [destinationPath, "--recurse-paths", "."] diff --git a/Sources/Testing/ABI/EntryPoints/EntryPoint.swift b/Sources/Testing/ABI/EntryPoints/EntryPoint.swift index ea37326d5..a0a5df2a0 100644 --- a/Sources/Testing/ABI/EntryPoints/EntryPoint.swift +++ b/Sources/Testing/ABI/EntryPoints/EntryPoint.swift @@ -656,7 +656,7 @@ extension Event.ConsoleOutputRecorder.Options { /// Whether or not the system terminal claims to support 16-color ANSI escape /// codes. private static var _terminalSupports16ColorANSIEscapeCodes: Bool { -#if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(Android) +#if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(OpenBSD) || os(Android) if let termVariable = Environment.variable(named: "TERM") { return termVariable != "dumb" } @@ -678,7 +678,7 @@ extension Event.ConsoleOutputRecorder.Options { /// Whether or not the system terminal claims to support 256-color ANSI escape /// codes. private static var _terminalSupports256ColorANSIEscapeCodes: Bool { -#if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(Android) +#if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(OpenBSD) || os(Android) if let termVariable = Environment.variable(named: "TERM") { return strstr(termVariable, "256") != nil } @@ -700,7 +700,7 @@ extension Event.ConsoleOutputRecorder.Options { /// Whether or not the system terminal claims to support true-color ANSI /// escape codes. private static var _terminalSupportsTrueColorANSIEscapeCodes: Bool { -#if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(Android) +#if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(OpenBSD) || os(Android) if let colortermVariable = Environment.variable(named: "COLORTERM") { return strstr(colortermVariable, "truecolor") != nil } diff --git a/Sources/Testing/ABI/EntryPoints/SwiftPMEntryPoint.swift b/Sources/Testing/ABI/EntryPoints/SwiftPMEntryPoint.swift index 52e4d2a54..3c72e9f20 100644 --- a/Sources/Testing/ABI/EntryPoints/SwiftPMEntryPoint.swift +++ b/Sources/Testing/ABI/EntryPoints/SwiftPMEntryPoint.swift @@ -24,7 +24,7 @@ private import _TestingInternals /// /// This constant is not part of the public interface of the testing library. var EXIT_NO_TESTS_FOUND: CInt { -#if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(Android) || os(WASI) +#if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(OpenBSD) || os(Android) || os(WASI) EX_UNAVAILABLE #elseif os(Windows) CInt(ERROR_NOT_FOUND) diff --git a/Sources/Testing/Events/Recorder/Event.Symbol.swift b/Sources/Testing/Events/Recorder/Event.Symbol.swift index aed2bbc3c..0f50ed95c 100644 --- a/Sources/Testing/Events/Recorder/Event.Symbol.swift +++ b/Sources/Testing/Events/Recorder/Event.Symbol.swift @@ -106,7 +106,7 @@ extension Event.Symbol { /// be used to represent it in text-based output. The value of this property /// is platform-dependent. public var unicodeCharacter: Character { -#if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(Android) || os(WASI) +#if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(OpenBSD) || os(Android) || os(WASI) switch self { case .default: // Unicode: WHITE DIAMOND diff --git a/Sources/Testing/ExitTests/ExitCondition.swift b/Sources/Testing/ExitTests/ExitCondition.swift index d589b5367..19f884303 100644 --- a/Sources/Testing/ExitTests/ExitCondition.swift +++ b/Sources/Testing/ExitTests/ExitCondition.swift @@ -49,12 +49,13 @@ public enum ExitCondition: Sendable { /// | macOS | [``](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/_Exit.3.html), [``](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/sysexits.3.html) | /// | Linux | [``](https://sourceware.org/glibc/manual/latest/html_node/Exit-Status.html), `` | /// | FreeBSD | [``](https://man.freebsd.org/cgi/man.cgi?exit(3)), [``](https://man.freebsd.org/cgi/man.cgi?sysexits(3)) | + /// | OpenBSD | [``](https://man.openbsd.org/exit.3), [``](https://man.openbsd.org/sysexits.3) | /// | Windows | [``](https://learn.microsoft.com/en-us/cpp/c-runtime-library/exit-success-exit-failure) | /// - /// On macOS, FreeBSD, and Windows, the full exit code reported by the process - /// is yielded to the parent process. Linux and other POSIX-like systems may - /// only reliably report the low unsigned 8 bits (0–255) of the exit - /// code. + /// On macOS, FreeBSD, OpenBSD, and Windows, the full exit code reported by + /// the process is yielded to the parent process. Linux and other POSIX-like + /// systems may only reliably report the low unsigned 8 bits (0–255) of + /// the exit code. case exitCode(_ exitCode: CInt) /// The process terminated with the given signal. @@ -70,6 +71,7 @@ public enum ExitCondition: Sendable { /// | macOS | [``](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/signal.3.html) | /// | Linux | [``](https://sourceware.org/glibc/manual/latest/html_node/Standard-Signals.html) | /// | FreeBSD | [``](https://man.freebsd.org/cgi/man.cgi?signal(3)) | + /// | OpenBSD | [``](https://man.openbsd.org/signal.3) | /// | Windows | [``](https://learn.microsoft.com/en-us/cpp/c-runtime-library/signal-constants) | case signal(_ signal: CInt) } diff --git a/Sources/Testing/ExitTests/ExitTest.swift b/Sources/Testing/ExitTests/ExitTest.swift index 5c3179ae5..af7981297 100644 --- a/Sources/Testing/ExitTests/ExitTest.swift +++ b/Sources/Testing/ExitTests/ExitTest.swift @@ -110,9 +110,9 @@ extension ExitTest { // SEE: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/fs/coredump.c#n610 var rl = rlimit(rlim_cur: 1, rlim_max: 1) _ = setrlimit(CInt(RLIMIT_CORE.rawValue), &rl) -#elseif os(FreeBSD) - // As with Linux, disable the generation core files. FreeBSD does not, as - // far as I can tell, special-case RLIMIT_CORE=1. +#elseif os(FreeBSD) || os(OpenBSD) + // As with Linux, disable the generation core files. The BSDs do not, as far + // as I can tell, special-case RLIMIT_CORE=1. var rl = rlimit(rlim_cur: 0, rlim_max: 0) _ = setrlimit(RLIMIT_CORE, &rl) #elseif os(Windows) @@ -152,6 +152,14 @@ extension ExitTest { } #endif +#if os(OpenBSD) + // OpenBSD does not have posix_spawn_file_actions_addclosefrom_np(). + // However, it does have closefrom(2), which we call here as a best effort. + if let from = Environment.variable(named: "SWT_CLOSEFROM").flatMap(CInt.init) { + _ = closefrom(from) + } +#endif + do { try await body() } catch { @@ -344,7 +352,7 @@ extension ExitTest { } var fd: CInt? -#if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) +#if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(OpenBSD) fd = CInt(backChannelEnvironmentVariable) #elseif os(Windows) if let handle = UInt(backChannelEnvironmentVariable).flatMap(HANDLE.init(bitPattern:)) { @@ -541,7 +549,7 @@ extension ExitTest { // known environment variable to the corresponding file descriptor // (HANDLE on Windows.) var backChannelEnvironmentVariable: String? -#if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) +#if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(OpenBSD) backChannelEnvironmentVariable = backChannelWriteEnd.withUnsafePOSIXFileDescriptor { fd in fd.map(String.init(describing:)) } diff --git a/Sources/Testing/ExitTests/SpawnProcess.swift b/Sources/Testing/ExitTests/SpawnProcess.swift index 204fd9bbe..fd18aad8a 100644 --- a/Sources/Testing/ExitTests/SpawnProcess.swift +++ b/Sources/Testing/ExitTests/SpawnProcess.swift @@ -17,7 +17,7 @@ internal import _TestingInternals /// A platform-specific value identifying a process running on the current /// system. -#if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) +#if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(OpenBSD) typealias ProcessID = pid_t #elseif os(Windows) typealias ProcessID = HANDLE @@ -62,13 +62,13 @@ func spawnExecutable( ) throws -> ProcessID { // Darwin and Linux differ in their optionality for the posix_spawn types we // use, so use this typealias to paper over the differences. -#if SWT_TARGET_OS_APPLE || os(FreeBSD) +#if SWT_TARGET_OS_APPLE || os(FreeBSD) || os(OpenBSD) typealias P = T? #elseif os(Linux) typealias P = T #endif -#if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) +#if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(OpenBSD) return try withUnsafeTemporaryAllocation(of: P.self, capacity: 1) { fileActions in let fileActions = fileActions.baseAddress! guard 0 == posix_spawn_file_actions_init(fileActions) else { @@ -105,9 +105,7 @@ func spawnExecutable( } // Forward standard I/O streams and any explicitly added file handles. -#if os(Linux) || os(FreeBSD) - var highestFD = CInt(-1) -#endif + var highestFD = max(STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO) func inherit(_ fileHandle: borrowing FileHandle, as standardFD: CInt? = nil) throws { try fileHandle.withUnsafePOSIXFileDescriptor { fd in guard let fd else { @@ -118,9 +116,8 @@ func spawnExecutable( } else { #if SWT_TARGET_OS_APPLE _ = posix_spawn_file_actions_addinherit_np(fileActions, fd) -#elseif os(Linux) || os(FreeBSD) - highestFD = max(highestFD, fd) #endif + highestFD = max(highestFD, fd) } } } @@ -156,6 +153,12 @@ func spawnExecutable( // `swt_posix_spawn_file_actions_addclosefrom_np` to guard the // availability of this function. _ = posix_spawn_file_actions_addclosefrom_np(fileActions, highestFD + 1) +#elseif os(OpenBSD) + // OpenBSD does not have posix_spawn_file_actions_addclosefrom_np(). + // However, it does have closefrom(2), which we can call from within the + // spawned child process if we control its execution. + var environment = environment + environment["SWT_CLOSEFROM"] = String(describing: highestFD + 1) #else #warning("Platform-specific implementation missing: cannot close unused file descriptors") #endif diff --git a/Sources/Testing/ExitTests/WaitFor.swift b/Sources/Testing/ExitTests/WaitFor.swift index 668fe8dcb..239b4a4ba 100644 --- a/Sources/Testing/ExitTests/WaitFor.swift +++ b/Sources/Testing/ExitTests/WaitFor.swift @@ -11,7 +11,7 @@ #if !SWT_NO_PROCESS_SPAWNING internal import _TestingInternals -#if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) +#if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(OpenBSD) /// Block the calling thread, wait for the target process to exit, and return /// a value describing the conditions under which it exited. /// @@ -78,14 +78,14 @@ func wait(for pid: consuming pid_t) async throws -> ExitCondition { return try _blockAndWait(for: pid) } -#elseif SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) +#elseif SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(OpenBSD) /// A mapping of awaited child PIDs to their corresponding Swift continuations. private let _childProcessContinuations = Locked<[pid_t: CheckedContinuation]>() /// A condition variable used to suspend the waiter thread created by /// `_createWaitThread()` when there are no child processes to await. private nonisolated(unsafe) let _waitThreadNoChildrenCondition = { -#if os(FreeBSD) +#if os(FreeBSD) || os(OpenBSD) let result = UnsafeMutablePointer.allocate(capacity: 1) #else let result = UnsafeMutablePointer.allocate(capacity: 1) @@ -136,7 +136,7 @@ private let _createWaitThread: Void = { // Create the thread. It will run immediately; because it runs in an infinite // loop, we aren't worried about detaching or joining it. -#if SWT_TARGET_OS_APPLE || os(FreeBSD) +#if SWT_TARGET_OS_APPLE || os(FreeBSD) || os(OpenBSD) var thread: pthread_t? #else var thread = pthread_t() @@ -147,14 +147,16 @@ private let _createWaitThread: Void = { { _ in // Set the thread name to help with diagnostics. Note that different // platforms support different thread name lengths. See MAXTHREADNAMESIZE - // on Darwin, TASK_COMM_LEN on Linux, and MAXCOMLEN on FreeBSD. We try to - // maximize legibility in the available space. + // on Darwin, TASK_COMM_LEN on Linux, MAXCOMLEN on FreeBSD, and _MAXCOMLEN + // on OpenBSD. We try to maximize legibility in the available space. #if SWT_TARGET_OS_APPLE _ = pthread_setname_np("Swift Testing exit test monitor") #elseif os(Linux) _ = swt_pthread_setname_np(pthread_self(), "SWT ExT monitor") #elseif os(FreeBSD) _ = pthread_set_name_np(pthread_self(), "SWT ex test monitor") +#elseif os(OpenBSD) + _ = pthread_set_name_np(pthread_self(), "SWT exit test monitor") #else #warning("Platform-specific implementation missing: thread naming unavailable") #endif diff --git a/Sources/Testing/SourceAttribution/Backtrace+Symbolication.swift b/Sources/Testing/SourceAttribution/Backtrace+Symbolication.swift index 833b95231..89fdffce0 100644 --- a/Sources/Testing/SourceAttribution/Backtrace+Symbolication.swift +++ b/Sources/Testing/SourceAttribution/Backtrace+Symbolication.swift @@ -71,7 +71,7 @@ extension Backtrace { result[i] = SymbolicatedAddress(address: address, offset: offset, symbolName: symbolName) } } -#elseif os(Linux) || os(FreeBSD) || os(Android) +#elseif os(Linux) || os(FreeBSD) || os(OpenBSD) || os(Android) // Although these platforms have dladdr(), they do not have symbol names // from DWARF binaries by default, only from shared libraries. The standard // library's backtracing functionality has implemented sufficient ELF/DWARF diff --git a/Sources/Testing/SourceAttribution/Backtrace.swift b/Sources/Testing/SourceAttribution/Backtrace.swift index b8301c0a4..a6019860c 100644 --- a/Sources/Testing/SourceAttribution/Backtrace.swift +++ b/Sources/Testing/SourceAttribution/Backtrace.swift @@ -69,7 +69,7 @@ public struct Backtrace: Sendable { initializedCount = addresses.withMemoryRebound(to: UnsafeMutableRawPointer.self) { addresses in .init(clamping: backtrace(addresses.baseAddress!, .init(clamping: addresses.count))) } -#elseif os(Linux) || os(FreeBSD) +#elseif os(Linux) || os(FreeBSD) || os(OpenBSD) initializedCount = .init(clamping: backtrace(addresses.baseAddress!, .init(clamping: addresses.count))) #elseif os(Windows) initializedCount = Int(clamping: RtlCaptureStackBackTrace(0, ULONG(clamping: addresses.count), addresses.baseAddress!, nil)) @@ -181,7 +181,7 @@ extension Backtrace { /// crash. To avoid said crash, we'll keep a strong reference to the /// object (abandoning memory until the process exits.) /// ([swift-#62985](https://github.com/swiftlang/swift/issues/62985)) -#if os(Windows) || os(FreeBSD) +#if os(Windows) || os(FreeBSD) || os(OpenBSD) nonisolated(unsafe) var errorObject: AnyObject? #else nonisolated(unsafe) weak var errorObject: AnyObject? diff --git a/Sources/Testing/Support/Additions/CommandLineAdditions.swift b/Sources/Testing/Support/Additions/CommandLineAdditions.swift index 762ab7290..0fda59839 100644 --- a/Sources/Testing/Support/Additions/CommandLineAdditions.swift +++ b/Sources/Testing/Support/Additions/CommandLineAdditions.swift @@ -55,6 +55,14 @@ extension CommandLine { return String(cString: buffer.baseAddress!) } } +#elseif os(OpenBSD) + // OpenBSD does not have API to get a path to the running executable. Use + // arguments[0]. We do a basic sniff test for a path-like string, but + // otherwise return argv[0] verbatim. + guard let argv0 = arguments.first, argv0.contains("/") else { + throw CError(rawValue: ENOEXEC) + } + return argv0 #elseif os(Windows) var result: String? #if DEBUG @@ -87,7 +95,7 @@ extension CommandLine { return arguments[0] #else #warning("Platform-specific implementation missing: executable path unavailable") - return "" + throw SystemError(description: "The executable path of the current process could not be determined.") #endif } } diff --git a/Sources/Testing/Support/CError.swift b/Sources/Testing/Support/CError.swift index 47b9d6612..a8462fda4 100644 --- a/Sources/Testing/Support/CError.swift +++ b/Sources/Testing/Support/CError.swift @@ -47,8 +47,8 @@ func strerror(_ errorCode: CInt) -> String { _ = strerror_s(buffer.baseAddress!, buffer.count, errorCode) return strnlen(buffer.baseAddress!, buffer.count) } -#elseif os(FreeBSD) - // FreeBSD's implementation of strerror() is not thread-safe. +#elseif os(FreeBSD) || os(OpenBSD) + // FreeBSD's/OpenBSD's implementation of strerror() is not thread-safe. String(unsafeUninitializedCapacity: 1024) { buffer in _ = strerror_r(errorCode, buffer.baseAddress!, buffer.count) return strnlen(buffer.baseAddress!, buffer.count) diff --git a/Sources/Testing/Support/Environment.swift b/Sources/Testing/Support/Environment.swift index e10505877..ec2ee9c74 100644 --- a/Sources/Testing/Support/Environment.swift +++ b/Sources/Testing/Support/Environment.swift @@ -42,7 +42,7 @@ enum Environment { } } -#if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(Android) || os(WASI) +#if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(OpenBSD) || os(Android) || os(WASI) /// Get all environment variables from a POSIX environment block. /// /// - Parameters: @@ -103,7 +103,7 @@ enum Environment { } #endif return _get(fromEnviron: _NSGetEnviron()!.pointee!) -#elseif os(Linux) || os(FreeBSD) || os(Android) +#elseif os(Linux) || os(FreeBSD) || os(OpenBSD) || os(Android) _get(fromEnviron: swt_environ()) #elseif os(WASI) _get(fromEnviron: __wasilibc_get_environ()) @@ -170,7 +170,7 @@ enum Environment { } return nil } -#elseif SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(Android) || os(WASI) +#elseif SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(OpenBSD) || os(Android) || os(WASI) getenv(name).flatMap { String(validatingCString: $0) } #elseif os(Windows) name.withCString(encodedAs: UTF16.self) { name in diff --git a/Sources/Testing/Support/FileHandle.swift b/Sources/Testing/Support/FileHandle.swift index c234206f8..2a2bfe967 100644 --- a/Sources/Testing/Support/FileHandle.swift +++ b/Sources/Testing/Support/FileHandle.swift @@ -215,7 +215,7 @@ struct FileHandle: ~Copyable, Sendable { /// descriptor, `nil` is passed to `body`. borrowing func withUnsafePOSIXFileDescriptor(_ body: (CInt?) throws -> R) rethrows -> R { try withUnsafeCFILEHandle { handle in -#if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(Android) || os(WASI) +#if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(OpenBSD) || os(Android) || os(WASI) let fd = fileno(handle) #elseif os(Windows) let fd = _fileno(handle) @@ -274,7 +274,7 @@ struct FileHandle: ~Copyable, Sendable { /// other threads. borrowing func withLock(_ body: () throws -> R) rethrows -> R { try withUnsafeCFILEHandle { handle in -#if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(Android) +#if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(OpenBSD) || os(Android) flockfile(handle) defer { funlockfile(handle) @@ -309,7 +309,7 @@ extension FileHandle { // If possible, reserve enough space in the resulting buffer to contain // the contents of the file being read. var size: Int? -#if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(Android) || os(WASI) +#if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(OpenBSD) || os(Android) || os(WASI) withUnsafePOSIXFileDescriptor { fd in var s = stat() if let fd, 0 == fstat(fd, &s) { @@ -505,7 +505,7 @@ extension FileHandle { extension FileHandle { /// Is this file handle a TTY or PTY? var isTTY: Bool { -#if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(Android) || os(WASI) +#if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(OpenBSD) || os(Android) || os(WASI) // If stderr is a TTY and TERM is set, that's good enough for us. withUnsafePOSIXFileDescriptor { fd in if let fd, 0 != isatty(fd), let term = Environment.variable(named: "TERM"), !term.isEmpty { @@ -532,7 +532,7 @@ extension FileHandle { #if !SWT_NO_PIPES /// Is this file handle a pipe or FIFO? var isPipe: Bool { -#if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(Android) +#if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(OpenBSD) || os(Android) withUnsafePOSIXFileDescriptor { fd in guard let fd else { return false @@ -607,7 +607,7 @@ func fileExists(atPath path: String) -> Bool { /// resolved, the resulting string may differ slightly but refers to the same /// file system object. If the path could not be resolved, returns `nil`. func canonicalizePath(_ path: String) -> String? { -#if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(Android) || os(WASI) +#if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(OpenBSD) || os(Android) || os(WASI) path.withCString { path in if let resolvedCPath = realpath(path, nil) { defer { diff --git a/Sources/Testing/Support/GetSymbol.swift b/Sources/Testing/Support/GetSymbol.swift index 3d4eb32d8..264bc0daa 100644 --- a/Sources/Testing/Support/GetSymbol.swift +++ b/Sources/Testing/Support/GetSymbol.swift @@ -13,7 +13,7 @@ internal import _TestingInternals #if !SWT_NO_DYNAMIC_LINKING /// The platform-specific type of a loaded image handle. -#if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(Android) +#if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(OpenBSD) || os(Android) typealias ImageAddress = UnsafeMutableRawPointer #elseif os(Windows) typealias ImageAddress = HMODULE @@ -28,7 +28,7 @@ typealias ImageAddress = Never /// and cannot be imported directly into Swift. As well, `RTLD_DEFAULT` is only /// defined on Linux when `_GNU_SOURCE` is defined, so it is not sufficient to /// declare a wrapper function in the internal module's Stubs.h file. -#if SWT_TARGET_OS_APPLE || os(FreeBSD) +#if SWT_TARGET_OS_APPLE || os(FreeBSD) || os(OpenBSD) private nonisolated(unsafe) let RTLD_DEFAULT = ImageAddress(bitPattern: -2) #elseif os(Android) && _pointerBitWidth(_32) private nonisolated(unsafe) let RTLD_DEFAULT = ImageAddress(bitPattern: UInt(0xFFFFFFFF)) @@ -59,7 +59,7 @@ private nonisolated(unsafe) let RTLD_DEFAULT = ImageAddress(bitPattern: 0) /// calling `EnumProcessModules()` and iterating over the returned handles /// looking for one containing the given function. func symbol(in handle: ImageAddress? = nil, named symbolName: String) -> UnsafeRawPointer? { -#if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(Android) +#if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(OpenBSD) || os(Android) dlsym(handle ?? RTLD_DEFAULT, symbolName).map(UnsafeRawPointer.init) #elseif os(Windows) symbolName.withCString { symbolName in diff --git a/Sources/Testing/Support/Locked.swift b/Sources/Testing/Support/Locked.swift index 11c4c4c86..d0edbc801 100644 --- a/Sources/Testing/Support/Locked.swift +++ b/Sources/Testing/Support/Locked.swift @@ -38,7 +38,7 @@ struct Locked: RawRepresentable, Sendable where T: Sendable { /// or `OSAllocatedUnfairLock`. #if SWT_TARGET_OS_APPLE || os(Linux) || os(Android) || (os(WASI) && compiler(>=6.1) && _runtime(_multithreaded)) typealias PlatformLock = pthread_mutex_t -#elseif os(FreeBSD) +#elseif os(FreeBSD) || os(OpenBSD) typealias PlatformLock = pthread_mutex_t? #elseif os(Windows) typealias PlatformLock = SRWLOCK @@ -54,7 +54,7 @@ struct Locked: RawRepresentable, Sendable where T: Sendable { private final class _Storage: ManagedBuffer { deinit { withUnsafeMutablePointerToElements { lock in -#if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(Android) || (os(WASI) && compiler(>=6.1) && _runtime(_multithreaded)) +#if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(OpenBSD) || os(Android) || (os(WASI) && compiler(>=6.1) && _runtime(_multithreaded)) _ = pthread_mutex_destroy(lock) #elseif os(Windows) // No deinitialization needed. @@ -73,7 +73,7 @@ struct Locked: RawRepresentable, Sendable where T: Sendable { init(rawValue: T) { _storage = _Storage.create(minimumCapacity: 1, makingHeaderWith: { _ in rawValue }) _storage.withUnsafeMutablePointerToElements { lock in -#if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(Android) || (os(WASI) && compiler(>=6.1) && _runtime(_multithreaded)) +#if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(OpenBSD) || os(Android) || (os(WASI) && compiler(>=6.1) && _runtime(_multithreaded)) _ = pthread_mutex_init(lock, nil) #elseif os(Windows) InitializeSRWLock(lock) @@ -103,7 +103,7 @@ struct Locked: RawRepresentable, Sendable where T: Sendable { /// concurrency tools. nonmutating func withLock(_ body: (inout T) throws -> R) rethrows -> R { try _storage.withUnsafeMutablePointers { rawValue, lock in -#if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(Android) || (os(WASI) && compiler(>=6.1) && _runtime(_multithreaded)) +#if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(OpenBSD) || os(Android) || (os(WASI) && compiler(>=6.1) && _runtime(_multithreaded)) _ = pthread_mutex_lock(lock) defer { _ = pthread_mutex_unlock(lock) diff --git a/Sources/Testing/Support/Versions.swift b/Sources/Testing/Support/Versions.swift index 592722486..5e974c6f1 100644 --- a/Sources/Testing/Support/Versions.swift +++ b/Sources/Testing/Support/Versions.swift @@ -31,7 +31,7 @@ let operatingSystemVersion: String = { default: return "\(productVersion) (\(buildNumber))" } -#elseif !SWT_NO_UNAME && (SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD)) +#elseif !SWT_NO_UNAME && (SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(OpenBSD)) var name = utsname() if 0 == uname(&name) { let release = withUnsafeBytes(of: name.release) { release in diff --git a/Sources/Testing/Traits/Tags/Tag.Color+Loading.swift b/Sources/Testing/Traits/Tags/Tag.Color+Loading.swift index 3e3682e6f..2ab35b107 100644 --- a/Sources/Testing/Traits/Tags/Tag.Color+Loading.swift +++ b/Sources/Testing/Traits/Tags/Tag.Color+Loading.swift @@ -11,7 +11,7 @@ private import _TestingInternals #if !SWT_NO_FILE_IO -#if os(macOS) || (os(iOS) && targetEnvironment(macCatalyst)) || os(Linux) || os(FreeBSD) +#if os(macOS) || (os(iOS) && targetEnvironment(macCatalyst)) || os(Linux) || os(FreeBSD) || os(OpenBSD) /// The path to the current user's home directory, if known. private var _homeDirectoryPath: String? { #if SWT_TARGET_OS_APPLE @@ -57,7 +57,7 @@ var swiftTestingDirectoryPath: String? { // The (default) name of the .swift-testing directory. let swiftTestingDirectoryName = ".swift-testing" -#if os(macOS) || (os(iOS) && targetEnvironment(macCatalyst)) || os(Linux) || os(FreeBSD) +#if os(macOS) || (os(iOS) && targetEnvironment(macCatalyst)) || os(Linux) || os(FreeBSD) || os(OpenBSD) if let homeDirectoryPath = _homeDirectoryPath { return appendPathComponent(swiftTestingDirectoryName, to: homeDirectoryPath) } diff --git a/Sources/_TestingInternals/Discovery.cpp b/Sources/_TestingInternals/Discovery.cpp index eac49c45d..baf4ebd90 100644 --- a/Sources/_TestingInternals/Discovery.cpp +++ b/Sources/_TestingInternals/Discovery.cpp @@ -412,7 +412,7 @@ static void enumerateTypeMetadataSections(const SectionEnumerator& body) { } } -#elif defined(__linux__) || defined(__FreeBSD__) || defined(__ANDROID__) +#elif defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__ANDROID__) #pragma mark - ELF implementation /// Specifies the address range corresponding to a section. diff --git a/Sources/_TestingInternals/include/Includes.h b/Sources/_TestingInternals/include/Includes.h index 51c02e277..b1f4c7973 100644 --- a/Sources/_TestingInternals/include/Includes.h +++ b/Sources/_TestingInternals/include/Includes.h @@ -133,6 +133,10 @@ #include #endif +#if defined(__OpenBSD__) +#include +#endif + #if defined(_WIN32) #define WIN32_LEAN_AND_MEAN #define NOMINMAX diff --git a/Sources/_TestingInternals/include/Stubs.h b/Sources/_TestingInternals/include/Stubs.h index 4e114f751..caeb7c493 100644 --- a/Sources/_TestingInternals/include/Stubs.h +++ b/Sources/_TestingInternals/include/Stubs.h @@ -93,7 +93,7 @@ static DWORD_PTR swt_PROC_THREAD_ATTRIBUTE_HANDLE_LIST(void) { } #endif -#if defined(__linux__) || defined(__FreeBSD__) || defined(__ANDROID__) +#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__ANDROID__) /// The environment block. /// /// By POSIX convention, the environment block variable is declared in client diff --git a/Tests/TestingTests/Support/EnvironmentTests.swift b/Tests/TestingTests/Support/EnvironmentTests.swift index a4fb8ddd9..512ebfe7b 100644 --- a/Tests/TestingTests/Support/EnvironmentTests.swift +++ b/Tests/TestingTests/Support/EnvironmentTests.swift @@ -90,7 +90,7 @@ extension Environment { environment[name] = value } return true -#elseif SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(Android) || os(WASI) +#elseif SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(OpenBSD) || os(Android) || os(WASI) if let value { return 0 == setenv(name, value, 1) } diff --git a/Tests/TestingTests/Support/FileHandleTests.swift b/Tests/TestingTests/Support/FileHandleTests.swift index c7f347357..c837ac7cf 100644 --- a/Tests/TestingTests/Support/FileHandleTests.swift +++ b/Tests/TestingTests/Support/FileHandleTests.swift @@ -14,7 +14,7 @@ private import _TestingInternals #if !SWT_NO_FILE_IO // NOTE: we don't run these tests on iOS (etc.) because processes on those // platforms are sandboxed and do not have arbitrary filesystem access. -#if os(macOS) || os(Linux) || os(FreeBSD) || os(Android) || os(Windows) +#if os(macOS) || os(Linux) || os(FreeBSD) || os(OpenBSD) || os(Android) || os(Windows) @Suite("FileHandle Tests") struct FileHandleTests { // FileHandle is non-copyable, so it cannot yet be used as a test parameter. @@ -255,7 +255,7 @@ func temporaryDirectory() throws -> String { } return try #require(Environment.variable(named: "TMPDIR")) } -#elseif os(Linux) || os(FreeBSD) +#elseif os(Linux) || os(FreeBSD) || os(OpenBSD) "/tmp" #elseif os(Android) Environment.variable(named: "TMPDIR") ?? "/data/local/tmp"