@@ -25,6 +25,8 @@ import (
25
25
"go/types"
26
26
"io/ioutil"
27
27
"os"
28
+ "path"
29
+ "path/filepath"
28
30
"sync"
29
31
30
32
"golang.org/x/tools/go/packages"
@@ -329,7 +331,7 @@ func LoadRoots(roots ...string) ([]*Package, error) {
329
331
//
330
332
// This is generally only useful for use in testing when you need to modify
331
333
// loading settings to load from a fake location.
332
- func LoadRootsWithConfig (cfg * packages.Config , roots ... string ) ([]* Package , error ) {
334
+ func LoadRootsWithConfig (cfg * packages.Config , roots ... string ) (pkgs []* Package , retErr error ) {
333
335
l := & loader {
334
336
cfg : cfg ,
335
337
packages : make (map [* packages.Package ]* Package ),
@@ -341,13 +343,77 @@ func LoadRootsWithConfig(cfg *packages.Config, roots ...string) ([]*Package, err
341
343
// put our build flags first so that callers can override them
342
344
l .cfg .BuildFlags = append ([]string {"-tags" , "ignore_autogenerated" }, l .cfg .BuildFlags ... )
343
345
344
- rawPkgs , err := packages .Load (l .cfg , roots ... )
345
- if err != nil {
346
+ // check each root to see if it should be expanded to include nested modules
347
+ var goModDirs []string
348
+ findGoModules := func (p string , d os.DirEntry , err error ) error {
349
+ if err != nil {
350
+ return err
351
+ }
352
+ if ! d .IsDir () && path .Base (p ) == "go.mod" {
353
+ absPath , err := filepath .Abs (p )
354
+ if err != nil {
355
+ return err
356
+ }
357
+ goModDirs = append (goModDirs , path .Join (path .Dir (absPath ), "..." ))
358
+ }
359
+ return nil
360
+ }
361
+ for i , r := range roots {
362
+ // skip roots whose last path element is not four dot characters
363
+ if filepath .Base (r ) != "...." {
364
+ continue
365
+ }
366
+ // update the root to no longer descend into nested modules
367
+ roots [i ] = r [:len (r )- 1 ]
368
+ // add any nested modules to the list of Go module directories to
369
+ // process later
370
+ if err := filepath .WalkDir (r [:len (r )- 4 ], findGoModules ); err != nil {
371
+ return nil , err
372
+ }
373
+ }
374
+
375
+ // loadRoots parses the provided file paths and returns their load packages
376
+ loadRoots := func (roots ... string ) error {
377
+ rawPkgs , err := packages .Load (l .cfg , roots ... )
378
+ if err != nil {
379
+ return err
380
+ }
381
+ for _ , rawPkg := range rawPkgs {
382
+ l .Roots = append (l .Roots , l .packageFor (rawPkg ))
383
+ }
384
+ return nil
385
+ }
386
+
387
+ // load the packages from the main module
388
+ if err := loadRoots (roots ... ); err != nil {
346
389
return nil , err
347
390
}
348
391
349
- for _ , rawPkg := range rawPkgs {
350
- l .Roots = append (l .Roots , l .packageFor (rawPkg ))
392
+ if len (goModDirs ) > 0 {
393
+ // ensure the working directory is updated back to its original location
394
+ // as we switch into the directory of each Go module to process them to
395
+ // accommodate the package loader
396
+ workingDir , err := os .Getwd ()
397
+ if err != nil {
398
+ return nil , err
399
+ }
400
+ defer func () {
401
+ retErr = os .Chdir (workingDir )
402
+ }()
403
+
404
+ // load the packages from the nested modules
405
+ for _ , p := range goModDirs {
406
+ // change the working directory to the root of the nested module
407
+ // so the package loader does not complain about processing a
408
+ // directory that is not a member of the root module
409
+ if err := os .Chdir (path .Dir (p )); err != nil {
410
+ return nil , err
411
+ }
412
+ // load the packages from the nested module
413
+ if err := loadRoots ("./..." ); err != nil {
414
+ return nil , err
415
+ }
416
+ }
351
417
}
352
418
353
419
return l .Roots , nil
0 commit comments