@@ -284,25 +284,47 @@ func spawnExecutable(
284
284
let commandLine = _escapeCommandLine ( CollectionOfOne ( executablePath) + arguments)
285
285
let environ = environment. map { " \( $0. key) = \( $0. value) " } . joined ( separator: " \0 " ) + " \0 \0 "
286
286
287
+ // CreateProcessW() may modify the command line argument, so we must make
288
+ // a mutable copy of it. (environ is also passed as a mutable raw pointer,
289
+ // but it is not documented as actually being mutated.)
290
+ let commandLineCopy = commandLine. withCString ( encodedAs: UTF16 . self) { _wcsdup ( $0) }
291
+ defer {
292
+ free ( commandLineCopy)
293
+ }
294
+
295
+ // On Windows, a process holds a reference to its current working
296
+ // directory, which prevents other processes from deleting it. This causes
297
+ // code to fail if it tries to set the working directory to a temporary
298
+ // path. SEE: https://github.com/swiftlang/swift-testing/issues/1209
299
+ //
300
+ // This problem manifests for us when we spawn a child process without
301
+ // setting its working directory, which causes it to default to that of
302
+ // the parent process. To avoid this problem, we set the working directory
303
+ // of the new process to the root directory of the boot volume (which is
304
+ // unlikely to be deleted, one hopes).
305
+ //
306
+ // SEE: https://devblogs.microsoft.com/oldnewthing/20101109-00/?p=12323
307
+ let workingDirectoryPath = rootDirectoryPath
308
+
287
309
var flags = DWORD ( CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT)
288
310
#if DEBUG
289
311
// Start the process suspended so we can attach a debugger if needed.
290
312
flags |= DWORD ( CREATE_SUSPENDED)
291
313
#endif
292
314
293
- return try commandLine . withCString ( encodedAs: UTF16 . self) { commandLine in
294
- try environ . withCString ( encodedAs: UTF16 . self) { environ in
315
+ return try environ . withCString ( encodedAs: UTF16 . self) { environ in
316
+ try workingDirectoryPath . withCString ( encodedAs: UTF16 . self) { workingDirectoryPath in
295
317
var processInfo = PROCESS_INFORMATION ( )
296
318
297
319
guard CreateProcessW (
298
320
nil ,
299
- . init ( mutating : commandLine ) ,
321
+ commandLineCopy ,
300
322
nil ,
301
323
nil ,
302
324
true , // bInheritHandles
303
325
flags,
304
326
. init( mutating: environ) ,
305
- nil ,
327
+ workingDirectoryPath ,
306
328
startupInfo. pointer ( to: \. StartupInfo) !,
307
329
& processInfo
308
330
) else {
0 commit comments