1
0
mirror of https://github.com/golang/go synced 2024-11-17 08:24:43 -07:00

cmd/go: rewrite collectDeps to only depend on imports' deps

Change-Id: I0cac9f32855e49e9899709a2f4421083aa0e75cc
Reviewed-on: https://go-review.googlesource.com/c/go/+/483515
Reviewed-by: Bryan Mills <bcmills@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Michael Matloob <matloob@golang.org>
Reviewed-by: Michael Matloob <matloob@golang.org>
This commit is contained in:
Michael Matloob 2023-04-10 16:13:14 -04:00
parent 7830180dcd
commit eb57c09054
2 changed files with 126 additions and 35 deletions

View File

@ -598,8 +598,6 @@ func runList(ctx context.Context, cmd *base.Command, args []string) {
base.Fatalf("go list -export cannot be used with -find")
}
suppressDeps := !listJsonFields.needAny("Deps", "DepsErrors")
pkgOpts := load.PackageOpts{
IgnoreImports: *listFind,
ModResolveTests: *listTest,
@ -767,15 +765,28 @@ func runList(ctx context.Context, cmd *base.Command, args []string) {
}
}
if !suppressDeps {
if listJsonFields.needAny("Deps", "DepsErrors") {
all := pkgs
// Make sure we iterate through packages in a postorder traversal,
// which load.PackageList guarantees. If *listDeps, then all is
// already in PackageList order. Otherwise, calling load.PackageList
// provides the guarantee. In the case of an import cycle, the last package
// visited in the cycle, importing the first encountered package in the cycle,
// is visited first. The cycle import error will be bubbled up in the traversal
// order up to the first package in the cycle, covering all the packages
// in the cycle.
if !*listDeps {
// if *listDeps, then all is already in PackageList order.
all = load.PackageList(pkgs)
}
// Recompute deps lists using new strings, from the leaves up.
for _, p := range all {
collectDeps(p)
if listJsonFields.needAny("Deps") {
for _, p := range all {
collectDeps(p)
}
}
if listJsonFields.needAny("DepsErrors") {
for _, p := range all {
collectDepsErrors(p)
}
}
}
@ -878,29 +889,15 @@ func loadPackageList(roots []*load.Package) []*load.Package {
return pkgs
}
// collectDeps populates p.Deps and p.DepsErrors by iterating over
// p.Internal.Imports.
//
// TODO(jayconrod): collectDeps iterates over transitive imports for every
// package. We should only need to visit direct imports.
// collectDeps populates p.Deps by iterating over p.Internal.Imports.
// collectDeps must be called on all of p's Imports before being called on p.
func collectDeps(p *load.Package) {
deps := make(map[string]*load.Package)
var q []*load.Package
q = append(q, p.Internal.Imports...)
for i := 0; i < len(q); i++ {
p1 := q[i]
path := p1.ImportPath
// The same import path could produce an error or not,
// depending on what tries to import it.
// Prefer to record entries with errors, so we can report them.
p0 := deps[path]
if p0 == nil || p1.Error != nil && (p0.Error == nil || len(p0.Error.ImportStack) > len(p1.Error.ImportStack)) {
deps[path] = p1
for _, p2 := range p1.Internal.Imports {
if deps[p2.ImportPath] != p2 {
q = append(q, p2)
}
}
deps := make(map[string]bool)
for _, p := range p.Internal.Imports {
deps[p.ImportPath] = true
for _, q := range p.Deps {
deps[q] = true
}
}
@ -909,15 +906,34 @@ func collectDeps(p *load.Package) {
p.Deps = append(p.Deps, dep)
}
sort.Strings(p.Deps)
for _, dep := range p.Deps {
p1 := deps[dep]
if p1 == nil {
panic("impossible: missing entry in package cache for " + dep + " imported by " + p.ImportPath)
}
// collectDeps populates p.DepsErrors by iterating over p.Internal.Imports.
// collectDepsErrors must be called on all of p's Imports before being called on p.
func collectDepsErrors(p *load.Package) {
depsErrors := make(map[*load.PackageError]bool)
for _, p := range p.Internal.Imports {
if p.Error != nil {
depsErrors[p.Error] = true
}
if p1.Error != nil {
p.DepsErrors = append(p.DepsErrors, p1.Error)
for _, q := range p.DepsErrors {
depsErrors[q] = true
}
}
p.DepsErrors = make([]*load.PackageError, 0, len(depsErrors))
for deperr := range depsErrors {
p.DepsErrors = append(p.DepsErrors, deperr)
}
// Sort packages by the package on the top of the stack, which should be
// the package the error was produced for. Each package can have at most
// one error set on it.
sort.Slice(p.DepsErrors, func(i, j int) bool {
stki, stkj := p.DepsErrors[i].ImportStack, p.DepsErrors[j].ImportStack
pathi, pathj := stki[len(stki)-1], stkj[len(stkj)-1]
return pathi < pathj
})
}
// TrackingWriter tracks the last byte written on every write so

View File

@ -0,0 +1,75 @@
go list -e -deps -json=ImportPath,Error,DepsErrors m/a
cmp stdout want
-- want --
{
"ImportPath": "m/c",
"DepsErrors": [
{
"ImportStack": [
"m/a",
"m/b",
"m/c",
"m/a"
],
"Pos": "",
"Err": "import cycle not allowed"
}
]
}
{
"ImportPath": "m/b",
"DepsErrors": [
{
"ImportStack": [
"m/a",
"m/b",
"m/c",
"m/a"
],
"Pos": "",
"Err": "import cycle not allowed"
}
]
}
{
"ImportPath": "m/a",
"Error": {
"ImportStack": [
"m/a",
"m/b",
"m/c",
"m/a"
],
"Pos": "",
"Err": "import cycle not allowed"
},
"DepsErrors": [
{
"ImportStack": [
"m/a",
"m/b",
"m/c",
"m/a"
],
"Pos": "",
"Err": "import cycle not allowed"
}
]
}
-- go.mod --
module m
go 1.21
-- a/a.go --
package a
import _ "m/b"
-- b/b.go --
package b
import _ "m/c"
-- c/c.go --
package c
import _ "m/a"