Skip to content

[DNM] Windows test reverts #1160

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Sources/Testing/Attachments/Attachment.swift
Original file line number Diff line number Diff line change
Expand Up @@ -473,7 +473,7 @@ extension Attachment where AttachableValue: ~Copyable {
// file exists at this path (note "x" in the mode string), an error will
// be thrown and we'll try again by adding a suffix.
let preferredPath = appendPathComponent(preferredName, to: directoryPath)
file = try FileHandle(atPath: preferredPath, mode: "wxeb")
file = try FileHandle(atPath: preferredPath, mode: "wxb")
result = preferredPath
} catch {
// Split the extension(s) off the preferred name. The first component in
Expand All @@ -489,7 +489,7 @@ extension Attachment where AttachableValue: ~Copyable {
// Propagate any error *except* EEXIST, which would indicate that the
// name was already in use (so we should try again with a new suffix.)
do {
file = try FileHandle(atPath: preferredPath, mode: "wxeb")
file = try FileHandle(atPath: preferredPath, mode: "wxb")
result = preferredPath
break
} catch let error as CError where error.rawValue == swt_EEXIST() {
Expand Down
50 changes: 15 additions & 35 deletions Sources/Testing/ExitTests/SpawnProcess.swift
Original file line number Diff line number Diff line change
Expand Up @@ -124,27 +124,11 @@ func spawnExecutable(
guard let fd else {
throw SystemError(description: "A child process cannot inherit a file handle without an associated file descriptor. Please file a bug report at https://github.com/swiftlang/swift-testing/issues/new")
}
if let standardFD, standardFD != fd {
if let standardFD {
_ = posix_spawn_file_actions_adddup2(fileActions, fd, standardFD)
} else {
#if SWT_TARGET_OS_APPLE
_ = posix_spawn_file_actions_addinherit_np(fileActions, fd)
#else
// posix_spawn_file_actions_adddup2() will automatically clear
// FD_CLOEXEC after forking but before execing even if the old and
// new file descriptors are equal. This behavior is supported by
// Glibc ≥ 2.29, FreeBSD, OpenBSD, and Android (Bionic) and is
// standardized in POSIX.1-2024 (see https://pubs.opengroup.org/onlinepubs/9799919799/functions/posix_spawn_file_actions_adddup2.html
// and https://www.austingroupbugs.net/view.php?id=411).
_ = posix_spawn_file_actions_adddup2(fileActions, fd, fd)
#if canImport(Glibc)
if _slowPath(glibcVersion.major < 2 || (glibcVersion.major == 2 && glibcVersion.minor < 29)) {
// This system is using an older version of glibc that does not
// implement FD_CLOEXEC clearing in posix_spawn_file_actions_adddup2(),
// so we must clear it here in the parent process.
try setFD_CLOEXEC(false, onFileDescriptor: fd)
}
#endif
#endif
highestFD = max(highestFD, fd)
}
Expand Down Expand Up @@ -173,6 +157,8 @@ func spawnExecutable(
#if !SWT_NO_DYNAMIC_LINKING
// This platform doesn't have POSIX_SPAWN_CLOEXEC_DEFAULT, but we can at
// least close all file descriptors higher than the highest inherited one.
// We are assuming here that the caller didn't set FD_CLOEXEC on any of
// these file descriptors.
_ = _posix_spawn_file_actions_addclosefrom_np?(fileActions, highestFD + 1)
#endif
#elseif os(FreeBSD)
Expand Down Expand Up @@ -231,42 +217,36 @@ func spawnExecutable(
}
#elseif os(Windows)
return try _withStartupInfoEx(attributeCount: 1) { startupInfo in
func inherit(_ fileHandle: borrowing FileHandle) throws -> HANDLE? {
func inherit(_ fileHandle: borrowing FileHandle, as outWindowsHANDLE: inout HANDLE?) throws {
try fileHandle.withUnsafeWindowsHANDLE { windowsHANDLE in
guard let windowsHANDLE else {
throw SystemError(description: "A child process cannot inherit a file handle without an associated Windows handle. Please file a bug report at https://github.com/swiftlang/swift-testing/issues/new")
}

// Ensure the file handle can be inherited by the child process.
guard SetHandleInformation(windowsHANDLE, DWORD(HANDLE_FLAG_INHERIT), DWORD(HANDLE_FLAG_INHERIT)) else {
throw Win32Error(rawValue: GetLastError())
}

return windowsHANDLE
outWindowsHANDLE = windowsHANDLE
}
}
func inherit(_ fileHandle: borrowing FileHandle?) throws -> HANDLE? {
func inherit(_ fileHandle: borrowing FileHandle?, as outWindowsHANDLE: inout HANDLE?) throws {
if fileHandle != nil {
return try inherit(fileHandle!)
try inherit(fileHandle!, as: &outWindowsHANDLE)
} else {
return nil
outWindowsHANDLE = nil
}
}

// Forward standard I/O streams.
startupInfo.pointee.StartupInfo.hStdInput = try inherit(standardInput)
startupInfo.pointee.StartupInfo.hStdOutput = try inherit(standardOutput)
startupInfo.pointee.StartupInfo.hStdError = try inherit(standardError)
try inherit(standardInput, as: &startupInfo.pointee.StartupInfo.hStdInput)
try inherit(standardOutput, as: &startupInfo.pointee.StartupInfo.hStdOutput)
try inherit(standardError, as: &startupInfo.pointee.StartupInfo.hStdError)
startupInfo.pointee.StartupInfo.dwFlags |= STARTF_USESTDHANDLES

// Ensure standard I/O streams and any explicitly added file handles are
// inherited by the child process.
var inheritedHandles = [HANDLE?](repeating: nil, count: additionalFileHandles.count + 3)
inheritedHandles[0] = startupInfo.pointee.StartupInfo.hStdInput
inheritedHandles[1] = startupInfo.pointee.StartupInfo.hStdOutput
inheritedHandles[2] = startupInfo.pointee.StartupInfo.hStdError
try inherit(standardInput, as: &inheritedHandles[0])
try inherit(standardOutput, as: &inheritedHandles[1])
try inherit(standardError, as: &inheritedHandles[2])
for i in 0 ..< additionalFileHandles.count {
inheritedHandles[i + 3] = try inherit(additionalFileHandles[i].pointee)
try inherit(additionalFileHandles[i].pointee, as: &inheritedHandles[i + 3])
}
inheritedHandles = inheritedHandles.compactMap(\.self)

Expand Down
97 changes: 5 additions & 92 deletions Sources/Testing/Support/FileHandle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,6 @@ struct FileHandle: ~Copyable, Sendable {
return
}

// On Windows, "N" is used rather than "e" to signify that a file handle is
// not inherited.
var mode = mode
if let eIndex = mode.firstIndex(of: "e") {
mode.replaceSubrange(eIndex ... eIndex, with: "N")
}

// Windows deprecates fopen() as insecure, so call _wfopen_s() instead.
let fileHandle = try path.withCString(encodedAs: UTF16.self) { path in
try mode.withCString(encodedAs: UTF16.self) { mode in
Expand All @@ -105,12 +98,8 @@ struct FileHandle: ~Copyable, Sendable {
/// - path: The path to read from.
///
/// - Throws: Any error preventing the stream from being opened.
///
/// By default, the resulting file handle is not inherited by any child
/// processes (that is, `FD_CLOEXEC` is set on POSIX-like systems and
/// `HANDLE_FLAG_INHERIT` is cleared on Windows.).
init(forReadingAtPath path: String) throws {
try self.init(atPath: path, mode: "reb")
try self.init(atPath: path, mode: "rb")
}

/// Initialize an instance of this type to write to the given path.
Expand All @@ -119,12 +108,8 @@ struct FileHandle: ~Copyable, Sendable {
/// - path: The path to write to.
///
/// - Throws: Any error preventing the stream from being opened.
///
/// By default, the resulting file handle is not inherited by any child
/// processes (that is, `FD_CLOEXEC` is set on POSIX-like systems and
/// `HANDLE_FLAG_INHERIT` is cleared on Windows.).
init(forWritingAtPath path: String) throws {
try self.init(atPath: path, mode: "web")
try self.init(atPath: path, mode: "wb")
}

/// Initialize an instance of this type with an existing C file handle.
Expand Down Expand Up @@ -460,17 +445,6 @@ extension FileHandle {
#if !SWT_NO_PIPES
// MARK: - Pipes

#if !SWT_TARGET_OS_APPLE && !os(Windows) && !SWT_NO_DYNAMIC_LINKING
/// Create a pipe with flags.
///
/// This function declaration is provided because `pipe2()` is only declared if
/// `_GNU_SOURCE` is set, but setting it causes build errors due to conflicts
/// with Swift's Glibc module.
private let _pipe2 = symbol(named: "pipe2").map {
castCFunction(at: $0, to: (@convention(c) (UnsafeMutablePointer<CInt>, CInt) -> CInt).self)
}
#endif

extension FileHandle {
/// Make a pipe connecting two new file handles.
///
Expand All @@ -487,36 +461,15 @@ extension FileHandle {
/// - Bug: This function should return a tuple containing the file handles
/// instead of returning them via `inout` arguments. Swift does not support
/// tuples with move-only elements. ([104669935](rdar://104669935))
///
/// By default, the resulting file handles are not inherited by any child
/// processes (that is, `FD_CLOEXEC` is set on POSIX-like systems and
/// `HANDLE_FLAG_INHERIT` is cleared on Windows.).
static func makePipe(readEnd: inout FileHandle?, writeEnd: inout FileHandle?) throws {
#if !os(Windows)
var pipe2Called = false
#endif

var (fdReadEnd, fdWriteEnd) = try withUnsafeTemporaryAllocation(of: CInt.self, capacity: 2) { fds in
#if os(Windows)
guard 0 == _pipe(fds.baseAddress, 0, _O_BINARY | _O_NOINHERIT) else {
guard 0 == _pipe(fds.baseAddress, 0, _O_BINARY) else {
throw CError(rawValue: swt_errno())
}
#else
#if !SWT_TARGET_OS_APPLE && !os(Windows) && !SWT_NO_DYNAMIC_LINKING
if let _pipe2 {
guard 0 == _pipe2(fds.baseAddress!, O_CLOEXEC) else {
throw CError(rawValue: swt_errno())
}
pipe2Called = true
}
#endif

if !pipe2Called {
// pipe2() is not available. Use pipe() instead and simulate O_CLOEXEC
// to the best of our ability.
guard 0 == pipe(fds.baseAddress!) else {
throw CError(rawValue: swt_errno())
}
guard 0 == pipe(fds.baseAddress!) else {
throw CError(rawValue: swt_errno())
}
#endif
return (fds[0], fds[1])
Expand All @@ -526,15 +479,6 @@ extension FileHandle {
Self._close(fdWriteEnd)
}

#if !os(Windows)
if !pipe2Called {
// pipe2() is not available. Use pipe() instead and simulate O_CLOEXEC
// to the best of our ability.
try setFD_CLOEXEC(true, onFileDescriptor: fdReadEnd)
try setFD_CLOEXEC(true, onFileDescriptor: fdWriteEnd)
}
#endif

do {
defer {
fdReadEnd = -1
Expand Down Expand Up @@ -688,35 +632,4 @@ func canonicalizePath(_ path: String) -> String? {
return nil
#endif
}

#if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(OpenBSD) || os(Android)
/// Set the given file descriptor's `FD_CLOEXEC` flag.
///
/// - Parameters:
/// - flag: The new value of `fd`'s `FD_CLOEXEC` flag.
/// - fd: The file descriptor.
///
/// - Throws: Any error that occurred while setting the flag.
func setFD_CLOEXEC(_ flag: Bool, onFileDescriptor fd: CInt) throws {
switch swt_getfdflags(fd) {
case -1:
// An error occurred reading the flags for this file descriptor.
throw CError(rawValue: swt_errno())
case let oldValue:
let newValue = if flag {
oldValue & ~FD_CLOEXEC
} else {
oldValue | FD_CLOEXEC
}
if oldValue == newValue {
// No need to make a second syscall as nothing has changed.
return
}
if -1 == swt_setfdflags(fd, newValue) {
// An error occurred setting the flags for this file descriptor.
throw CError(rawValue: swt_errno())
}
}
}
#endif
#endif
24 changes: 0 additions & 24 deletions Sources/Testing/Support/Versions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -153,30 +153,6 @@ let swiftStandardLibraryVersion: String = {
return "unknown"
}()

#if canImport(Glibc)
/// The (runtime, not compile-time) version of glibc in use on this system.
///
/// This value is not part of the public interface of the testing library.
let glibcVersion: (major: Int, minor: Int) = {
// Default to the statically available version number if the function call
// fails for some reason.
var major = Int(clamping: __GLIBC__)
var minor = Int(clamping: __GLIBC_MINOR__)

if let strVersion = gnu_get_libc_version() {
withUnsafeMutablePointer(to: &major) { major in
withUnsafeMutablePointer(to: &minor) { minor in
withVaList([major, minor]) { args in
_ = vsscanf(strVersion, "%zd.%zd", args)
}
}
}
}

return (major, minor)
}()
#endif

// MARK: - sysctlbyname() Wrapper

#if !SWT_NO_SYSCTL && SWT_TARGET_OS_APPLE
Expand Down
4 changes: 0 additions & 4 deletions Sources/_TestingInternals/include/Includes.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,6 @@
#include <sys/fcntl.h>
#endif

#if __has_include(<gnu/libc-version.h>)
#include <gnu/libc-version.h>
#endif

#if __has_include(<sys/resource.h>) && !defined(__wasi__)
#include <sys/resource.h>
#endif
Expand Down
20 changes: 0 additions & 20 deletions Sources/_TestingInternals/include/Stubs.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,26 +160,6 @@ static int swt_EEXIST(void) {
return EEXIST;
}

#if defined(F_GETFD)
/// Call `fcntl(F_GETFD)`.
///
/// This function is provided because `fcntl()` is a variadic function and
/// cannot be imported directly into Swift.
static int swt_getfdflags(int fd) {
return fcntl(fd, F_GETFD);
}
#endif

#if defined(F_SETFD)
/// Call `fcntl(F_SETFD)`.
///
/// This function is provided because `fcntl()` is a variadic function and
/// cannot be imported directly into Swift.
static int swt_setfdflags(int fd, int flags) {
return fcntl(fd, F_SETFD, flags);
}
#endif

SWT_ASSUME_NONNULL_END

#endif