@@ -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 )
@@ -418,7 +424,7 @@ package struct UpdateIndexStoreTaskDescription: IndexTaskDescription {
418424 let result : ProcessResult
419425 do {
420426 result = try await withTimeout ( timeout) {
421- try await Process . run (
427+ try await Process . runUsingResponseFileIfTooManyArguments (
422428 arguments: processArguments,
423429 workingDirectory: workingDirectory,
424430 outputRedirection: . stream(
@@ -475,3 +481,66 @@ package struct UpdateIndexStoreTaskDescription: IndexTaskDescription {
475481 }
476482 }
477483}
484+
485+ fileprivate extension Process {
486+ /// Run a process with the given arguments. If the number of arguments exceeds the maximum number of arguments allows,
487+ /// create a response file and use it to pass the arguments.
488+ static func runUsingResponseFileIfTooManyArguments(
489+ arguments: [ String ] ,
490+ workingDirectory: AbsolutePath ? ,
491+ outputRedirection: OutputRedirection = . collect( redirectStderr: false )
492+ ) async throws -> ProcessResult {
493+ do {
494+ return try await Process . run (
495+ arguments: arguments,
496+ workingDirectory: workingDirectory,
497+ outputRedirection: outputRedirection
498+ )
499+ } catch {
500+ let argumentListTooLong : Bool
501+ #if os(Windows)
502+ if let error = error as? CocoaError {
503+ argumentListTooLong =
504+ error. underlyingErrors. contains ( where: {
505+ return ( $0 as NSError ) . domain == " org.swift.Foundation.WindowsError "
506+ && ( $0 as NSError ) . code == ERROR_FILENAME_EXCED_RANGE
507+ } )
508+ } else {
509+ argumentListTooLong = false
510+ }
511+ #else
512+ if case SystemError . posix_spawn( E2BIG, _) = error {
513+ argumentListTooLong = true
514+ } else {
515+ argumentListTooLong = false
516+ }
517+ #endif
518+
519+ guard argumentListTooLong else {
520+ throw error
521+ }
522+
523+ logger. debug ( " Argument list is too long. Using response file. " )
524+ let responseFile = FileManager . default. temporaryDirectory. appendingPathComponent (
525+ " index-response-file- \( UUID ( ) ) .txt "
526+ )
527+ defer {
528+ orLog ( " Failed to remove temporary response file " ) {
529+ try FileManager . default. removeItem ( at: responseFile)
530+ }
531+ }
532+ FileManager . default. createFile ( atPath: try responseFile. filePath, contents: nil )
533+ let handle = try FileHandle ( forWritingTo: responseFile)
534+ for argument in arguments. dropFirst ( ) {
535+ handle. write ( Data ( ( argument. spm_shellEscaped ( ) + " \n " ) . utf8) )
536+ }
537+ try handle. close ( )
538+
539+ return try await Process . run (
540+ arguments: arguments. prefix ( 1 ) + [ " @ \( responseFile. filePath) " ] ,
541+ workingDirectory: workingDirectory,
542+ outputRedirection: outputRedirection
543+ )
544+ }
545+ }
546+ }
0 commit comments