Skip to content

Commit d70911e

Browse files
committed
SwiftPrivateLibcExtras: port to Windows
Enable the libc extras to be built for Windows. This is a dependency for StdlibUnittests.
1 parent df5712d commit d70911e

File tree

3 files changed

+131
-34
lines changed

3 files changed

+131
-34
lines changed

stdlib/private/SwiftPrivateLibcExtras/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,5 @@ add_swift_target_library(swiftSwiftPrivateLibcExtras ${SWIFT_STDLIB_LIBRARY_BUIL
1515
SWIFT_MODULE_DEPENDS_FREEBSD Glibc
1616
SWIFT_MODULE_DEPENDS_CYGWIN Glibc
1717
SWIFT_MODULE_DEPENDS_HAIKU Glibc
18+
SWIFT_MODULE_DEPENDS_WINDOWS MSVCRT WinSDK
1819
INSTALL_IN_COMPONENT stdlib-experimental)

stdlib/private/SwiftPrivateLibcExtras/Subprocess.swift

Lines changed: 124 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,133 @@ import SwiftPrivate
1515
import Darwin
1616
#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku)
1717
import Glibc
18+
#elseif os(Windows)
19+
import MSVCRT
20+
import WinSDK
1821
#endif
1922

20-
23+
internal func _signalToString(_ signal: Int) -> String {
24+
switch CInt(signal) {
25+
case SIGILL: return "SIGILL"
26+
case SIGABRT: return "SIGABRT"
27+
case SIGFPE: return "SIGFPE"
28+
case SIGSEGV: return "SIGSEGV"
2129
#if !os(Windows)
22-
// posix_spawn is not available on Windows.
30+
case SIGTRAP: return "SIGTRAP"
31+
case SIGBUS: return "SIGBUS"
32+
case SIGSYS: return "SIGSYS"
33+
#endif
34+
default: return "SIG???? (\(signal))"
35+
}
36+
}
37+
38+
public enum ProcessTerminationStatus : CustomStringConvertible {
39+
case exit(Int)
40+
case signal(Int)
41+
42+
public var description: String {
43+
switch self {
44+
case .exit(let status):
45+
return "Exit(\(status))"
46+
case .signal(let signal):
47+
return "Signal(\(_signalToString(signal)))"
48+
}
49+
}
50+
}
51+
52+
53+
#if os(Windows)
54+
public func spawnChild(_ args: [String])
55+
-> (process: HANDLE, stdin: HANDLE, stdout: HANDLE, stderr: HANDLE) {
56+
var _stdin: (read: HANDLE?, write: HANDLE?)
57+
var _stdout: (read: HANDLE?, write: HANDLE?)
58+
var _stderr: (read: HANDLE?, write: HANDLE?)
59+
60+
var saAttributes: SECURITY_ATTRIBUTES = SECURITY_ATTRIBUTES()
61+
saAttributes.nLength = DWORD(MemoryLayout<SECURITY_ATTRIBUTES>.size)
62+
saAttributes.bInheritHandle = TRUE
63+
saAttributes.lpSecurityDescriptor = nil
64+
65+
if CreatePipe(&_stdin.read, &_stdin.write, &saAttributes, 0) == FALSE {
66+
fatalError("CreatePipe() failed")
67+
}
68+
if SetHandleInformation(_stdin.write, HANDLE_FLAG_INHERIT, 0) == FALSE {
69+
fatalError("SetHandleInformation() failed")
70+
}
71+
72+
if CreatePipe(&_stdout.read, &_stdout.write, &saAttributes, 0) == FALSE {
73+
fatalError("CreatePipe() failed")
74+
}
75+
if SetHandleInformation(_stdout.read, HANDLE_FLAG_INHERIT, 0) == FALSE {
76+
fatalError("SetHandleInformation() failed")
77+
}
78+
79+
if CreatePipe(&_stderr.read, &_stderr.write, &saAttributes, 0) == FALSE {
80+
fatalError("CreatePipe() failed")
81+
}
82+
if SetHandleInformation(_stderr.read, HANDLE_FLAG_INHERIT, 0) == FALSE {
83+
fatalError("SetHandleInformation() failed")
84+
}
85+
86+
var siStartupInfo: STARTUPINFOW = STARTUPINFOW()
87+
siStartupInfo.cb = DWORD(MemoryLayout<STARTUPINFOW>.size)
88+
siStartupInfo.hStdError = _stderr.write
89+
siStartupInfo.hStdOutput = _stdout.write
90+
siStartupInfo.hStdInput = _stdin.read
91+
siStartupInfo.dwFlags |= STARTF_USESTDHANDLES
92+
93+
var piProcessInfo: PROCESS_INFORMATION = PROCESS_INFORMATION()
94+
95+
// TODO(compnerd): properly quote the command line being invoked here. See
96+
// https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/
97+
// for more details on how to properly quote the command line for Windows.
98+
let command: String =
99+
([CommandLine.arguments[0]] + args).joined(separator: " ")
100+
command.withCString(encodedAs: UTF16.self) { cString in
101+
if CreateProcessW(nil, UnsafeMutablePointer<WCHAR>(mutating: cString),
102+
nil, nil, TRUE, 0, nil, nil,
103+
&siStartupInfo, &piProcessInfo) == FALSE {
104+
let dwError: DWORD = GetLastError()
105+
fatalError("CreateProcessW() failed \(dwError)")
106+
}
107+
}
108+
109+
if CloseHandle(_stdin.read) == FALSE {
110+
fatalError("CloseHandle() failed")
111+
}
112+
if CloseHandle(_stdout.write) == FALSE {
113+
fatalError("CloseHandle() failed")
114+
}
115+
if CloseHandle(_stderr.write) == FALSE {
116+
fatalError("CloseHandle() failed")
117+
}
118+
119+
// CloseHandle(piProcessInfo.hProcess)
120+
CloseHandle(piProcessInfo.hThread)
121+
122+
return (piProcessInfo.hProcess,
123+
_stdin.write ?? INVALID_HANDLE_VALUE,
124+
_stdout.read ?? INVALID_HANDLE_VALUE,
125+
_stderr.read ?? INVALID_HANDLE_VALUE)
126+
}
127+
128+
public func waitProcess(_ process: HANDLE) -> ProcessTerminationStatus {
129+
let result = WaitForSingleObject(process, INFINITE)
130+
if result != WAIT_OBJECT_0 {
131+
fatalError("WaitForSingleObject() failed")
132+
}
133+
134+
var status: DWORD = 0
135+
if GetExitCodeProcess(process, &status) == FALSE {
136+
fatalError("GetExitCodeProcess() failed")
137+
}
138+
139+
if status & DWORD(0x80000000) == DWORD(0x80000000) {
140+
return .signal(Int(status))
141+
}
142+
return .exit(Int(status))
143+
}
144+
#else
23145
// posix_spawn is not available on Android.
24146
// posix_spawn is not available on Haiku.
25147
#if !os(Android) && !os(Haiku)
@@ -237,33 +359,6 @@ internal func _make_posix_spawn_file_actions_t()
237359
#endif
238360
#endif
239361

240-
internal func _signalToString(_ signal: Int) -> String {
241-
switch CInt(signal) {
242-
case SIGILL: return "SIGILL"
243-
case SIGTRAP: return "SIGTRAP"
244-
case SIGABRT: return "SIGABRT"
245-
case SIGFPE: return "SIGFPE"
246-
case SIGBUS: return "SIGBUS"
247-
case SIGSEGV: return "SIGSEGV"
248-
case SIGSYS: return "SIGSYS"
249-
default: return "SIG???? (\(signal))"
250-
}
251-
}
252-
253-
public enum ProcessTerminationStatus : CustomStringConvertible {
254-
case exit(Int)
255-
case signal(Int)
256-
257-
public var description: String {
258-
switch self {
259-
case .exit(let status):
260-
return "Exit(\(status))"
261-
case .signal(let signal):
262-
return "Signal(\(_signalToString(signal)))"
263-
}
264-
}
265-
}
266-
267362
public func posixWaitpid(_ pid: pid_t) -> ProcessTerminationStatus {
268363
var status: CInt = 0
269364
#if os(Cygwin)

stdlib/private/SwiftPrivateLibcExtras/SwiftPrivateLibcExtras.swift

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,11 @@ import Darwin
1616
#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) || os(Cygwin) || os(Haiku)
1717
import Glibc
1818
#elseif os(Windows)
19-
import ucrt
19+
import MSVCRT
2020
#endif
2121

22-
#if !os(Windows)
2322
public func _stdlib_mkstemps(_ template: inout String, _ suffixlen: CInt) -> CInt {
24-
#if os(Android) || os(Haiku)
23+
#if os(Android) || os(Haiku) || os(Windows)
2524
preconditionFailure("mkstemps doesn't work on your platform")
2625
#else
2726
var utf8CStr = template.utf8CString
@@ -35,8 +34,8 @@ public func _stdlib_mkstemps(_ template: inout String, _ suffixlen: CInt) -> CIn
3534
return fd
3635
#endif
3736
}
38-
#endif
3937

38+
#if !os(Windows)
4039
public var _stdlib_FD_SETSIZE: CInt {
4140
return 1024
4241
}
@@ -85,7 +84,6 @@ public struct _stdlib_fd_set {
8584
}
8685
}
8786

88-
#if !os(Windows)
8987
public func _stdlib_select(
9088
_ readfds: inout _stdlib_fd_set, _ writefds: inout _stdlib_fd_set,
9189
_ errorfds: inout _stdlib_fd_set, _ timeout: UnsafeMutablePointer<timeval>?
@@ -135,6 +133,7 @@ public func _stdlib_pipe() -> (readEnd: CInt, writeEnd: CInt, error: CInt) {
135133
}
136134

137135

136+
#if !os(Windows)
138137
//
139138
// Functions missing in `Darwin` module.
140139
//
@@ -161,3 +160,5 @@ public func WEXITSTATUS(_ status: CInt) -> CInt {
161160
public func WTERMSIG(_ status: CInt) -> CInt {
162161
return _WSTATUS(status)
163162
}
163+
#endif
164+

0 commit comments

Comments
 (0)