Skip to content

Commit f015199

Browse files
committed
go/packages: go list -deps=false if no NeedDeps
Don't load deps by go list if NeedDeps wasn't set.
1 parent 3540c02 commit f015199

File tree

4 files changed

+193
-96
lines changed

4 files changed

+193
-96
lines changed

go/packages/golist.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ func goListDriver(cfg *Config, patterns ...string) (*driverResponse, error) {
101101
var sizes types.Sizes
102102
var sizeserr error
103103
var sizeswg sync.WaitGroup
104-
if cfg.Mode&NeedTypesSizes != 0 || cfg.Mode&NeedTypes != 0 {
104+
if cfg.Mode&NeedTypesSizes != 0 {
105105
sizeswg.Add(1)
106106
go func() {
107107
sizes, sizeserr = getSizes(cfg)
@@ -825,13 +825,16 @@ func absJoin(dir string, fileses ...[]string) (res []string) {
825825
}
826826

827827
func golistargs(cfg *Config, words []string) []string {
828-
const findFlags = NeedImports | NeedTypes | NeedSyntax | NeedTypesInfo
828+
const findFlags = NeedImports | NeedTypes | NeedSyntax
829829
fullargs := []string{
830830
"list", "-e", "-json",
831831
fmt.Sprintf("-compiled=%t", cfg.Mode&(NeedCompiledGoFiles|NeedSyntax|NeedTypesInfo|NeedTypesSizes) != 0),
832832
fmt.Sprintf("-test=%t", cfg.Tests),
833833
fmt.Sprintf("-export=%t", usesExportData(cfg)),
834-
fmt.Sprintf("-deps=%t", cfg.Mode&NeedImports != 0),
834+
835+
// Obtain package information about each dependency if needed
836+
fmt.Sprintf("-deps=%t", loadsDeps(cfg)),
837+
835838
// go list doesn't let you pass -test and -find together,
836839
// probably because you'd just get the TestMain.
837840
fmt.Sprintf("-find=%t", !cfg.Tests && cfg.Mode&findFlags == 0),

go/packages/packages.go

Lines changed: 139 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -398,12 +398,13 @@ func (p *Package) String() string { return p.ID }
398398
// loaderPackage augments Package with state used during the loading phase
399399
type loaderPackage struct {
400400
*Package
401-
importErrors map[string]error // maps each bad import to its error
402-
loadOnce sync.Once
403-
color uint8 // for cycle detection
404-
needsrc bool // load from source (Mode >= LoadTypes)
405-
needtypes bool // type information is either requested or depended on
406-
initial bool // package was matched by a pattern
401+
importErrors map[string]error // maps each bad import to its error
402+
loadOnce sync.Once
403+
color uint8 // for cycle detection
404+
needsyntax bool // fill syntax trees
405+
needtypes bool // basic type information is either requested or depended on (export data is enough)
406+
needtypesinfo bool // full type information is either requested or depended on (need to load from source)
407+
initial bool // package was matched by a pattern
407408
}
408409

409410
// loader holds the working state of a single call to load.
@@ -415,7 +416,7 @@ type loader struct {
415416
parseCacheMu sync.Mutex
416417
exportMu sync.Mutex // enforces mutual exclusion of exportdata operations
417418

418-
// Config.Mode contains the implied mode (see implyLoadMode).
419+
// Config.Mode contains the implied mode (see impliedLoadMode).
419420
// Implied mode contains all the fields we need the data for.
420421
// In requestedMode there are the actually requested fields.
421422
// We'll zero them out before returning packages to the user.
@@ -463,6 +464,10 @@ func newLoader(cfg *Config) *loader {
463464
}
464465
}
465466

467+
// Save the actually requested fields. We'll zero them out before returning packages to the user.
468+
ld.requestedMode = ld.Mode
469+
ld.Mode = impliedLoadMode(ld.Mode)
470+
466471
if ld.Mode&NeedTypes != 0 {
467472
if ld.Fset == nil {
468473
ld.Fset = token.NewFileSet()
@@ -478,9 +483,6 @@ func newLoader(cfg *Config) *loader {
478483
}
479484
}
480485

481-
// Save the actually requested fields. We'll zero them out before returning packages to the user.
482-
ld.requestedMode = ld.Mode
483-
ld.implyLoadMode()
484486
return ld
485487
}
486488

@@ -499,12 +501,26 @@ func (ld *loader) refine(roots []string, list ...*Package) ([]*Package, error) {
499501
if i, found := rootMap[pkg.ID]; found {
500502
rootIndex = i
501503
}
504+
505+
// For root packages (rootIndex >= 0) load types if they were requested by NeedTypes.
506+
// For all other packages (dependencies) load types only if types were requested (NeedTypes) for dependecies (NeedDeps).
507+
explicitlyNeedTypes := ld.Mode&NeedTypes != 0 && (rootIndex >= 0 || ld.Mode&NeedDeps != 0)
508+
509+
explicitlyNeedTypesInfo := ld.Mode&NeedTypesInfo != 0 && (rootIndex >= 0 || // load from source all root packages
510+
ld.Mode&NeedDeps != 0) // load from source all dependencies if needed
511+
hasValidExportData := (pkg.ExportFile != "" || pkg.PkgPath == "unsafe") &&
512+
// overlays can invalidate export data. TODO(matloob): make this check fine-grained based on dependencies on overlaid files
513+
len(ld.Overlay) == 0
514+
needTypesInfo := explicitlyNeedTypesInfo || (ld.Mode&NeedTypes != 0 && !hasValidExportData)
515+
516+
explicitlyNeedSyntax := ld.Mode&NeedSyntax != 0 && (rootIndex >= 0 || ld.Mode&NeedDeps != 0)
517+
needSyntax := explicitlyNeedSyntax || needTypesInfo // types info loading requires syntax trees building
518+
502519
lpkg := &loaderPackage{
503-
Package: pkg,
504-
needtypes: (ld.Mode&(NeedTypes|NeedTypesInfo) != 0 && ld.Mode&NeedDeps != 0 && rootIndex < 0) || rootIndex >= 0,
505-
needsrc: (ld.Mode&(NeedSyntax|NeedTypesInfo) != 0 && ld.Mode&NeedDeps != 0 && rootIndex < 0) || rootIndex >= 0 ||
506-
len(ld.Overlay) > 0 || // Overlays can invalidate export data. TODO(matloob): make this check fine-grained based on dependencies on overlaid files
507-
pkg.ExportFile == "" && pkg.PkgPath != "unsafe",
520+
Package: pkg,
521+
needtypes: explicitlyNeedTypes,
522+
needtypesinfo: needTypesInfo,
523+
needsyntax: needSyntax,
508524
}
509525
ld.pkgs[lpkg.ID] = lpkg
510526
if rootIndex >= 0 {
@@ -518,6 +534,19 @@ func (ld *loader) refine(roots []string, list ...*Package) ([]*Package, error) {
518534
}
519535
}
520536

537+
// Build loader packages for imported packages when no deeps are needed
538+
if ld.Mode&NeedDeps == 0 {
539+
for _, pkg := range list {
540+
for _, ipkg := range pkg.Imports {
541+
if imp := ld.pkgs[ipkg.ID]; imp == nil {
542+
ld.pkgs[ipkg.ID] = &loaderPackage{
543+
Package: ipkg,
544+
}
545+
}
546+
}
547+
}
548+
}
549+
521550
// Materialize the import graph.
522551

523552
const (
@@ -533,16 +562,16 @@ func (ld *loader) refine(roots []string, list ...*Package) ([]*Package, error) {
533562
// Invalid imports (cycles and missing nodes) are saved in the importErrors map.
534563
// Thus, even in the presence of both kinds of errors, the Import graph remains a DAG.
535564
//
536-
// visit returns whether the package needs src or has a transitive
565+
// visit returns whether the package needs types info or has a transitive
537566
// dependency on a package that does. These are the only packages
538567
// for which we load source code.
539568
var stack []*loaderPackage
540569
var visit func(lpkg *loaderPackage) bool
541-
var srcPkgs []*loaderPackage
570+
var typesInfoPkgs []*loaderPackage
542571
visit = func(lpkg *loaderPackage) bool {
543572
switch lpkg.color {
544573
case black:
545-
return lpkg.needsrc
574+
return lpkg.needtypesinfo
546575
case grey:
547576
panic("internal error: grey node")
548577
}
@@ -569,22 +598,26 @@ func (ld *loader) refine(roots []string, list ...*Package) ([]*Package, error) {
569598
continue
570599
}
571600

572-
if visit(imp) {
573-
lpkg.needsrc = true
601+
// If don't need deps, just fill Imports for the root. No need to recurse further.
602+
if loadsDeps(&ld.Config) {
603+
if visit(imp) {
604+
lpkg.needtypesinfo = true
605+
lpkg.needsyntax = true // types info loading (needtypesinfo) requires syntax trees building
606+
}
574607
}
575-
lpkg.Imports[importPath] = imp.Package
608+
lpkg.Imports[importPath] = imp.Package // deduplicate imported package
576609
}
577610
}
578-
if lpkg.needsrc {
579-
srcPkgs = append(srcPkgs, lpkg)
611+
if lpkg.needtypesinfo {
612+
typesInfoPkgs = append(typesInfoPkgs, lpkg)
580613
}
581614
if ld.Mode&NeedTypesSizes != 0 {
582615
lpkg.TypesSizes = ld.sizes
583616
}
584617
stack = stack[:len(stack)-1] // pop
585618
lpkg.color = black
586619

587-
return lpkg.needsrc
620+
return lpkg.needtypesinfo
588621
}
589622

590623
if ld.Mode&NeedImports == 0 {
@@ -598,16 +631,18 @@ func (ld *loader) refine(roots []string, list ...*Package) ([]*Package, error) {
598631
visit(lpkg)
599632
}
600633
}
601-
if ld.Mode&NeedImports != 0 && ld.Mode&NeedTypes != 0 {
602-
for _, lpkg := range srcPkgs {
603-
// Complete type information is required for the
604-
// immediate dependencies of each source package.
605-
for _, ipkg := range lpkg.Imports {
606-
imp := ld.pkgs[ipkg.ID]
607-
imp.needtypes = true
608-
}
634+
635+
// Set needtypes for immediate dependencies if need types info
636+
for _, lpkg := range typesInfoPkgs {
637+
// Complete type information is required for the
638+
// immediate dependencies of packages for which
639+
// we need types info.
640+
for _, ipkg := range lpkg.Imports {
641+
imp := ld.pkgs[ipkg.ID]
642+
imp.needtypes = true
609643
}
610644
}
645+
611646
// Load type data if needed, starting at
612647
// the initial packages (roots of the import DAG).
613648
if ld.Mode&NeedTypes != 0 {
@@ -712,13 +747,6 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
712747
// which would then require that such created packages be explicitly
713748
// inserted back into the Import graph as a final step after export data loading.
714749
// The Diamond test exercises this case.
715-
if !lpkg.needtypes {
716-
return
717-
}
718-
if !lpkg.needsrc {
719-
ld.loadFromExportData(lpkg)
720-
return // not a source package, don't get syntax trees
721-
}
722750

723751
appendError := func(err error) {
724752
// Convert various error types into the one true Error.
@@ -769,12 +797,35 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
769797
lpkg.Errors = append(lpkg.Errors, errs...)
770798
}
771799

772-
files, errs := ld.parseFiles(lpkg.CompiledGoFiles)
773-
for _, err := range errs {
774-
appendError(err)
800+
if lpkg.needsyntax {
801+
files, errs := ld.parseFiles(lpkg.CompiledGoFiles)
802+
for _, err := range errs {
803+
appendError(err)
804+
}
805+
806+
lpkg.Syntax = files
807+
} else if lpkg.needtypesinfo {
808+
log.Fatalf("Internal error: can't load package %s types info without loading syntax trees", lpkg.ID)
775809
}
776810

777-
lpkg.Syntax = files
811+
if !lpkg.needtypesinfo {
812+
if !lpkg.needtypes {
813+
// Need just syntax trees
814+
return
815+
}
816+
817+
_, err := ld.loadFromExportData(lpkg)
818+
if err == nil {
819+
// Successfully types loaded from export data
820+
return
821+
}
822+
823+
log.Fatalf("Failed to load package %s from export data: %s", lpkg.ID, err)
824+
}
825+
826+
if !lpkg.needtypes {
827+
log.Fatal("Internal error: types will be loaded with types info")
828+
}
778829

779830
lpkg.TypesInfo = &types.Info{
780831
Types: make(map[ast.Expr]types.TypeAndValue),
@@ -815,7 +866,7 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
815866
tc := &types.Config{
816867
Importer: importer,
817868

818-
// Type-check bodies of functions only in non-initial packages.
869+
// Type-check bodies of functions only in initial packages.
819870
// Example: for import graph A->B->C and initial packages {A,C},
820871
// we can ignore function bodies in B.
821872
IgnoreFuncBodies: ld.Mode&NeedDeps == 0 && !lpkg.initial,
@@ -1080,25 +1131,54 @@ func (ld *loader) loadFromExportData(lpkg *loaderPackage) (*types.Package, error
10801131
return tpkg, nil
10811132
}
10821133

1083-
// implyLoadMode adds dependencies for choosed LoadMode in ld.Mode
1084-
func (ld *loader) implyLoadMode() {
1085-
if ld.Mode&NeedTypesInfo != 0 && ld.Mode&NeedImports == 0 {
1086-
// If NeedTypesInfo, go/packages needs to do typechecking itself so it can
1087-
// associate type info with the AST. To do so, we need the export data
1088-
// for dependencies, which means we need to ask for the direct dependencies.
1089-
// NeedImports is used to ask for the direct dependencies.
1090-
ld.Mode |= NeedImports
1091-
ld.Logf("Added load mode dependency of NeedTypesInfo: NeedImports")
1134+
func usesExportData(cfg *Config) bool {
1135+
return cfg.Mode&NeedExportsFile != 0 ||
1136+
// If NeedTypes but not NeedTypesInfo we won't typecheck using sources, so we need export data.
1137+
(cfg.Mode&NeedTypes != 0 && cfg.Mode&NeedTypesInfo == 0) ||
1138+
// If NeedTypesInfo but not NeedDeps, we're typechecking a package using its sources plus its dependencies' export data
1139+
(cfg.Mode&NeedTypesInfo != 0 && cfg.Mode&NeedDeps == 0)
1140+
}
1141+
1142+
func loadsDeps(cfg *Config) bool {
1143+
return cfg.Mode&NeedDeps != 0 ||
1144+
// Immediate dependencies information (at least, export data) is required to do typechecking
1145+
// on sources, which is required for the TypesInfo. In such cases we could load packages
1146+
// without deps and then call go list again for immediate dependecies, but it's typically
1147+
// much slower than running go list -deps=true once.
1148+
cfg.Mode&NeedTypesInfo != 0
1149+
}
1150+
1151+
// impliedLoadMode returns loadMode with it's dependencies
1152+
func impliedLoadMode(loadMode LoadMode) LoadMode {
1153+
if loadMode&NeedTypesInfo != 0 && loadMode&NeedSyntax == 0 {
1154+
// When NeedTypesInfo is set we load types info from source code.
1155+
// For parsing the source code we need NeedSyntax.
1156+
loadMode |= NeedSyntax
1157+
}
1158+
1159+
if loadMode&NeedTypesInfo != 0 && loadMode&NeedImports == 0 {
1160+
// When NeedTypesInfo is set we load types info from source code.
1161+
// We need immediate dependencies types information for that.
1162+
// NeedImports handles processing of immediate dependencies.
1163+
loadMode |= NeedImports
10921164
}
10931165

1094-
if ld.Mode&NeedDeps != 0 && ld.Mode&NeedImports == 0 {
1166+
if loadMode&NeedTypesInfo != 0 && loadMode&NeedTypes == 0 {
1167+
// When NeedTypesInfo is set we load types info from source code,
1168+
// this procedure also fills types.
1169+
loadMode |= NeedTypes
1170+
}
1171+
1172+
if loadMode&NeedTypesInfo != 0 && loadMode&NeedTypesSizes == 0 {
1173+
// Types loading requires types sizes (set in types.Config).
1174+
loadMode |= NeedTypesSizes
1175+
}
1176+
1177+
if loadMode&NeedDeps != 0 && loadMode&NeedImports == 0 {
10951178
// With NeedDeps we need to load at least direct dependencies.
10961179
// NeedImports is used to ask for the direct dependencies.
1097-
ld.Mode |= NeedImports
1098-
ld.Logf("Added load mode dependency of NeedDeps: NeedImports")
1180+
loadMode |= NeedImports
10991181
}
1100-
}
11011182

1102-
func usesExportData(cfg *Config) bool {
1103-
return cfg.Mode&NeedExportsFile != 0 || cfg.Mode&NeedTypes != 0 && cfg.Mode&NeedDeps == 0
1183+
return loadMode
11041184
}

0 commit comments

Comments
 (0)