@@ -24,6 +24,7 @@ import TSCExtensions
2424import struct TSCBasic. AbsolutePath
2525import class TSCBasic. Process
2626import struct TSCBasic. ProcessResult
27+ import enum TSCBasic. SystemError
2728#else
2829import BuildServerProtocol
2930import BuildSystemIntegration
@@ -38,6 +39,11 @@ import TSCExtensions
3839import struct TSCBasic. AbsolutePath
3940import class TSCBasic. Process
4041import struct TSCBasic. ProcessResult
42+ import enum TSCBasic. SystemError
43+ #endif
44+
45+ #if os(Windows)
46+ import WinSDK
4147#endif
4248
4349private let updateIndexStoreIDForLogging = AtomicUInt32 ( initialValue: 1 )
@@ -415,7 +421,7 @@ package struct UpdateIndexStoreTaskDescription: IndexTaskDescription {
415421 let result : ProcessResult
416422 do {
417423 result = try await withTimeout ( timeout) {
418- try await Process . run (
424+ try await Process . runUsingResponseFileIfTooManyArguments (
419425 arguments: processArguments,
420426 workingDirectory: workingDirectory,
421427 outputRedirection: . stream(
@@ -464,3 +470,66 @@ package struct UpdateIndexStoreTaskDescription: IndexTaskDescription {
464470 }
465471 }
466472}
473+
474+ fileprivate extension Process {
475+ /// Run a process with the given arguments. If the number of arguments exceeds the maximum number of arguments allows,
476+ /// create a response file and use it to pass the arguments.
477+ static func runUsingResponseFileIfTooManyArguments(
478+ arguments: [ String ] ,
479+ workingDirectory: AbsolutePath ? ,
480+ outputRedirection: OutputRedirection = . collect( redirectStderr: false )
481+ ) async throws -> ProcessResult {
482+ do {
483+ return try await Process . run (
484+ arguments: arguments,
485+ workingDirectory: workingDirectory,
486+ outputRedirection: outputRedirection
487+ )
488+ } catch {
489+ let argumentListTooLong : Bool
490+ #if os(Windows)
491+ if let error = error as? CocoaError {
492+ argumentListTooLong =
493+ error. underlyingErrors. contains ( where: {
494+ return ( $0 as NSError ) . domain == " org.swift.Foundation.WindowsError "
495+ && ( $0 as NSError ) . code == ERROR_FILENAME_EXCED_RANGE
496+ } )
497+ } else {
498+ argumentListTooLong = false
499+ }
500+ #else
501+ if case SystemError . posix_spawn( E2BIG, _) = error {
502+ argumentListTooLong = true
503+ } else {
504+ argumentListTooLong = false
505+ }
506+ #endif
507+
508+ guard argumentListTooLong else {
509+ throw error
510+ }
511+
512+ logger. debug ( " Argument list is too long. Using response file. " )
513+ let responseFile = FileManager . default. temporaryDirectory. appendingPathComponent (
514+ " index-response-file- \( UUID ( ) ) .txt "
515+ )
516+ defer {
517+ orLog ( " Failed to remove temporary response file " ) {
518+ try FileManager . default. removeItem ( at: responseFile)
519+ }
520+ }
521+ FileManager . default. createFile ( atPath: try responseFile. filePath, contents: nil )
522+ let handle = try FileHandle ( forWritingTo: responseFile)
523+ for argument in arguments. dropFirst ( ) {
524+ handle. write ( Data ( ( argument. spm_shellEscaped ( ) + " \n " ) . utf8) )
525+ }
526+ try handle. close ( )
527+
528+ return try await Process . run (
529+ arguments: arguments. prefix ( 1 ) + [ " @ \( responseFile. filePath) " ] ,
530+ workingDirectory: workingDirectory,
531+ outputRedirection: outputRedirection
532+ )
533+ }
534+ }
535+ }
0 commit comments