blob: 06747a9df7fc5316f67a263054c9b8515b799fc2 [file]
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package packages
import (
"fmt"
"iter"
"os"
"golang.org/x/tools/internal/moremaps"
)
// Visit visits all the packages in the import graph whose roots are
// pkgs, calling the optional pre function the first time each package
// is encountered (preorder), and the optional post function after a
// package's dependencies have been visited (postorder).
// The boolean result of pre(pkg) determines whether
// the imports of package pkg are visited.
//
// Example:
//
// pkgs, err := Load(...)
// if err != nil { ... }
// Visit(pkgs, nil, func(pkg *Package) {
// log.Println(pkg)
// })
//
// In most cases, it is more convenient to use [Postorder]:
//
// for pkg := range Postorder(pkgs) {
// log.Println(pkg)
// }
func Visit(pkgs []*Package, pre func(*Package) bool, post func(*Package)) {
seen := make(map[*Package]bool)
var visit func(*Package)
visit = func(pkg *Package) {
if !seen[pkg] {
seen[pkg] = true
if pre == nil || pre(pkg) {
for _, imp := range moremaps.Sorted(pkg.Imports) { // for determinism
visit(imp)
}
}
if post != nil {
post(pkg)
}
}
}
for _, pkg := range pkgs {
visit(pkg)
}
}
// PrintErrors prints to os.Stderr the accumulated errors of all
// packages in the import graph rooted at pkgs, dependencies first.
// PrintErrors returns the number of errors printed.
func PrintErrors(pkgs []*Package) int {
var n int
errModules := make(map[*Module]bool)
for pkg := range Postorder(pkgs) {
for _, err := range pkg.Errors {
fmt.Fprintln(os.Stderr, err)
n++
}
// Print pkg.Module.Error once if present.
mod := pkg.Module
if mod != nil && mod.Error != nil && !errModules[mod] {
errModules[mod] = true
fmt.Fprintln(os.Stderr, mod.Error.Err)
n++
}
}
return n
}
// Postorder returns an iterator over the packages in
// the import graph whose roots are pkg.
// Packages are enumerated in dependencies-first order.
func Postorder(pkgs []*Package) iter.Seq[*Package] {
return func(yield func(*Package) bool) {
seen := make(map[*Package]bool)
var visit func(*Package) bool
visit = func(pkg *Package) bool {
if !seen[pkg] {
seen[pkg] = true
for _, imp := range moremaps.Sorted(pkg.Imports) { // for determinism
if !visit(imp) {
return false
}
}
if !yield(pkg) {
return false
}
}
return true
}
for _, pkg := range pkgs {
if !visit(pkg) {
break
}
}
}
}