@@ -9,6 +9,7 @@ package packages
9
9
import (
10
10
"context"
11
11
"encoding/json"
12
+ "errors"
12
13
"fmt"
13
14
"go/ast"
14
15
"go/parser"
@@ -24,6 +25,8 @@ import (
24
25
"sync"
25
26
"time"
26
27
28
+ "golang.org/x/sync/errgroup"
29
+
27
30
"golang.org/x/tools/go/gcexportdata"
28
31
"golang.org/x/tools/internal/gocommand"
29
32
"golang.org/x/tools/internal/packagesinternal"
@@ -255,8 +258,27 @@ func Load(cfg *Config, patterns ...string) ([]*Package, error) {
255
258
// defaultDriver will fall back to the go list driver.
256
259
// The boolean result indicates that an external driver handled the request.
257
260
func defaultDriver (cfg * Config , patterns ... string ) (* DriverResponse , bool , error ) {
261
+ const (
262
+ // windowsArgMax specifies the maximum command line length for
263
+ // the Windows' CreateProcess function.
264
+ windowsArgMax = 32767
265
+ // maxEnvSize is a very rough estimation of the maximum environment
266
+ // size of a user.
267
+ maxEnvSize = 16384
268
+ // safeArgMax specifies the maximum safe command line length to use
269
+ // by the underlying driver excl. the environment. We choose the Windows'
270
+ // ARG_MAX as the starting point because it's one of the lowest ARG_MAX
271
+ // constants out of the different supported platforms,
272
+ // e.g., https://www.in-ulm.de/~mascheck/various/argmax/#results.
273
+ safeArgMax = windowsArgMax - maxEnvSize
274
+ )
275
+ chunks , err := splitIntoChunks (patterns , safeArgMax )
276
+ if err != nil {
277
+ return nil , false , err
278
+ }
279
+
258
280
if driver := findExternalDriver (cfg ); driver != nil {
259
- response , err := driver ( cfg , patterns ... )
281
+ response , err := callDriverOnChunks ( driver , cfg , chunks )
260
282
if err != nil {
261
283
return nil , false , err
262
284
} else if ! response .NotHandled {
@@ -265,11 +287,82 @@ func defaultDriver(cfg *Config, patterns ...string) (*DriverResponse, bool, erro
265
287
// (fall through)
266
288
}
267
289
268
- response , err := goListDriver ( cfg , patterns ... )
290
+ response , err := callDriverOnChunks ( goListDriver , cfg , chunks )
269
291
if err != nil {
270
292
return nil , false , err
271
293
}
272
- return response , false , nil
294
+ return response , false , err
295
+ }
296
+
297
+ // splitIntoChunks chunks the slice so that the total number of characters
298
+ // in a chunk is no longer than argMax.
299
+ func splitIntoChunks (patterns []string , argMax int ) ([][]string , error ) {
300
+ if argMax <= 0 {
301
+ return nil , errors .New ("failed to split patterns into chunks, negative safe argMax value" )
302
+ }
303
+ var chunks [][]string
304
+ charsInChunk := 0
305
+ nextChunkStart := 0
306
+ for i , v := range patterns {
307
+ vChars := len (v )
308
+ if vChars > argMax {
309
+ // a single pattern is longer than the maximum safe ARG_MAX, hardly should happen
310
+ return nil , errors .New ("failed to split patterns into chunks, a pattern is too long" )
311
+ }
312
+ charsInChunk += vChars + 1 // +1 is for a whitespace between patterns that has to be counted too
313
+ if charsInChunk > argMax {
314
+ chunks = append (chunks , patterns [nextChunkStart :i ])
315
+ nextChunkStart = i
316
+ charsInChunk = vChars
317
+ }
318
+ }
319
+ // add the last chunk
320
+ if nextChunkStart < len (patterns ) {
321
+ chunks = append (chunks , patterns [nextChunkStart :])
322
+ }
323
+ return chunks , nil
324
+ }
325
+
326
+ func callDriverOnChunks (driver driver , cfg * Config , chunks [][]string ) (* DriverResponse , error ) {
327
+ if len (chunks ) == 0 {
328
+ return driver (cfg )
329
+ }
330
+ responses := make ([]* DriverResponse , len (chunks ))
331
+ errNotHandled := errors .New ("driver returned NotHandled" )
332
+ var g errgroup.Group
333
+ for i , chunk := range chunks {
334
+ i := i
335
+ chunk := chunk
336
+ g .Go (func () (err error ) {
337
+ responses [i ], err = driver (cfg , chunk ... )
338
+ if responses [i ] != nil && responses [i ].NotHandled {
339
+ err = errNotHandled
340
+ }
341
+ return err
342
+ })
343
+ }
344
+ if err := g .Wait (); err != nil {
345
+ if errors .Is (err , errNotHandled ) {
346
+ return & DriverResponse {NotHandled : true }, nil
347
+ }
348
+ return nil , err
349
+ }
350
+ return mergeResponses (responses ... ), nil
351
+ }
352
+
353
+ func mergeResponses (responses ... * DriverResponse ) * DriverResponse {
354
+ if len (responses ) == 0 {
355
+ return nil
356
+ }
357
+ response := newDeduper ()
358
+ response .dr .NotHandled = false
359
+ response .dr .Compiler = responses [0 ].Compiler
360
+ response .dr .Arch = responses [0 ].Arch
361
+ response .dr .GoVersion = responses [0 ].GoVersion
362
+ for _ , v := range responses {
363
+ response .addAll (v )
364
+ }
365
+ return response .dr
273
366
}
274
367
275
368
// A Package describes a loaded Go package.
0 commit comments