@@ -338,162 +338,12 @@ extension ExitTest {
338338 childEnvironment [ " SWT_EXPERIMENTAL_EXIT_TEST_SOURCE_LOCATION " ] = String ( decoding: json, as: UTF8 . self)
339339 }
340340
341- return try await _spawnAndWait (
341+ return try await spawnAndWait (
342342 forExecutableAtPath: childProcessExecutablePath,
343343 arguments: childArguments,
344344 environment: childEnvironment
345345 )
346346 }
347347 }
348-
349- /// Spawn a process and wait for it to terminate.
350- ///
351- /// - Parameters:
352- /// - executablePath: The path to the executable to spawn.
353- /// - arguments: The arguments to pass to the executable, not including the
354- /// executable path.
355- /// - environment: The environment block to pass to the executable.
356- ///
357- /// - Returns: The exit condition of the spawned process.
358- ///
359- /// - Throws: Any error that prevented the process from spawning or its exit
360- /// condition from being read.
361- private static func _spawnAndWait(
362- forExecutableAtPath executablePath: String ,
363- arguments: [ String ] ,
364- environment: [ String : String ]
365- ) async throws -> ExitCondition {
366- // Darwin and Linux differ in their optionality for the posix_spawn types we
367- // use, so use this typealias to paper over the differences.
368- #if SWT_TARGET_OS_APPLE
369- typealias P < T> = T ?
370- #elseif os(Linux) || os(FreeBSD)
371- typealias P < T> = T
372- #endif
373-
374- #if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD)
375- let pid = try withUnsafeTemporaryAllocation ( of: P< posix_spawn_file_actions_t> . self , capacity: 1 ) { fileActions in
376- guard 0 == posix_spawn_file_actions_init ( fileActions. baseAddress!) else {
377- throw CError ( rawValue: swt_errno ( ) )
378- }
379- defer {
380- _ = posix_spawn_file_actions_destroy ( fileActions. baseAddress!)
381- }
382-
383- // Do not forward standard I/O.
384- _ = posix_spawn_file_actions_addopen ( fileActions. baseAddress!, STDIN_FILENO, " /dev/null " , O_RDONLY, 0 )
385- _ = posix_spawn_file_actions_addopen ( fileActions. baseAddress!, STDOUT_FILENO, " /dev/null " , O_WRONLY, 0 )
386- _ = posix_spawn_file_actions_addopen ( fileActions. baseAddress!, STDERR_FILENO, " /dev/null " , O_WRONLY, 0 )
387-
388- return try withUnsafeTemporaryAllocation ( of: P< posix_spawnattr_t> . self , capacity: 1 ) { attrs in
389- guard 0 == posix_spawnattr_init ( attrs. baseAddress!) else {
390- throw CError ( rawValue: swt_errno ( ) )
391- }
392- defer {
393- _ = posix_spawnattr_destroy ( attrs. baseAddress!)
394- }
395- #if SWT_TARGET_OS_APPLE
396- // Close all other file descriptors open in the parent. Note that Linux
397- // does not support this flag and, unlike Foundation.Process, we do not
398- // attempt to emulate it.
399- _ = posix_spawnattr_setflags ( attrs. baseAddress!, CShort ( POSIX_SPAWN_CLOEXEC_DEFAULT) )
400- #endif
401-
402- var argv : [ UnsafeMutablePointer < CChar > ? ] = [ strdup ( executablePath) ]
403- argv += arguments. lazy. map { strdup ( $0) }
404- argv. append ( nil )
405- defer {
406- for arg in argv {
407- free ( arg)
408- }
409- }
410-
411- var environ : [ UnsafeMutablePointer < CChar > ? ] = environment. map { strdup ( " \( $0. key) = \( $0. value) " ) }
412- environ. append ( nil )
413- defer {
414- for environ in environ {
415- free ( environ)
416- }
417- }
418-
419- var pid = pid_t ( )
420- guard 0 == posix_spawn ( & pid, executablePath, fileActions. baseAddress!, attrs. baseAddress, argv, environ) else {
421- throw CError ( rawValue: swt_errno ( ) )
422- }
423- return pid
424- }
425- }
426-
427- return try await wait ( for: pid)
428- #elseif os(Windows)
429- // NOTE: Windows processes are responsible for handling their own
430- // command-line escaping. This code is adapted from the code in
431- // swift-corelibs-foundation (SEE: quoteWindowsCommandLine()) which was
432- // itself adapted from the code published by Microsoft at
433- // https://learn.microsoft.com/en-gb/archive/blogs/twistylittlepassagesallalike/everyone-quotes-command-line-arguments-the-wrong-way
434- let commandLine = ( CollectionOfOne ( executablePath) + arguments) . lazy
435- . map { arg in
436- if !arg. contains ( where: { " \t \n \" " . contains ( $0) } ) {
437- return arg
438- }
439-
440- var quoted = " \" "
441- var unquoted = arg. unicodeScalars
442- while !unquoted. isEmpty {
443- guard let firstNonBackslash = unquoted. firstIndex ( where: { $0 != " \\ " } ) else {
444- let backslashCount = unquoted. count
445- quoted. append ( String ( repeating: " \\ " , count: backslashCount * 2 ) )
446- break
447- }
448- let backslashCount = unquoted. distance ( from: unquoted. startIndex, to: firstNonBackslash)
449- if ( unquoted [ firstNonBackslash] == " \" " ) {
450- quoted. append ( String ( repeating: " \\ " , count: backslashCount * 2 + 1 ) )
451- quoted. append ( String ( unquoted [ firstNonBackslash] ) )
452- } else {
453- quoted. append ( String ( repeating: " \\ " , count: backslashCount) )
454- quoted. append ( String ( unquoted [ firstNonBackslash] ) )
455- }
456- unquoted. removeFirst ( backslashCount + 1 )
457- }
458- quoted. append ( " \" " )
459- return quoted
460- } . joined ( separator: " " )
461- let environ = environment. map { " \( $0. key) = \( $0. value) " } . joined ( separator: " \0 " ) + " \0 \0 "
462-
463- let processHandle : HANDLE ! = try commandLine. withCString ( encodedAs: UTF16 . self) { commandLine in
464- try environ. withCString ( encodedAs: UTF16 . self) { environ in
465- var processInfo = PROCESS_INFORMATION ( )
466-
467- var startupInfo = STARTUPINFOW ( )
468- startupInfo. cb = DWORD ( MemoryLayout . size ( ofValue: startupInfo) )
469- guard CreateProcessW (
470- nil ,
471- . init( mutating: commandLine) ,
472- nil ,
473- nil ,
474- false ,
475- DWORD ( CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT) ,
476- . init( mutating: environ) ,
477- nil ,
478- & startupInfo,
479- & processInfo
480- ) else {
481- throw Win32Error ( rawValue: GetLastError ( ) )
482- }
483- _ = CloseHandle ( processInfo. hThread)
484-
485- return processInfo. hProcess
486- }
487- }
488- defer {
489- CloseHandle ( processHandle)
490- }
491-
492- return try await wait ( for: processHandle)
493- #else
494- #warning("Platform-specific implementation missing: process spawning unavailable")
495- throw SystemError ( description: " Exit tests are unimplemented on this platform. " )
496- #endif
497- }
498348}
499349#endif
0 commit comments