@@ -338,162 +338,12 @@ extension ExitTest {
338
338
childEnvironment [ " SWT_EXPERIMENTAL_EXIT_TEST_SOURCE_LOCATION " ] = String ( decoding: json, as: UTF8 . self)
339
339
}
340
340
341
- return try await _spawnAndWait (
341
+ return try await spawnAndWait (
342
342
forExecutableAtPath: childProcessExecutablePath,
343
343
arguments: childArguments,
344
344
environment: childEnvironment
345
345
)
346
346
}
347
347
}
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
- }
498
348
}
499
349
#endif
0 commit comments