1package packages
2
3import (
4	"fmt"
5	"os"
6	"sort"
7)
8
9// Visit visits all the packages in the import graph whose roots are
10// pkgs, calling the optional pre function the first time each package
11// is encountered (preorder), and the optional post function after a
12// package's dependencies have been visited (postorder).
13// The boolean result of pre(pkg) determines whether
14// the imports of package pkg are visited.
15func Visit(pkgs []*Package, pre func(*Package) bool, post func(*Package)) {
16	seen := make(map[*Package]bool)
17	var visit func(*Package)
18	visit = func(pkg *Package) {
19		if !seen[pkg] {
20			seen[pkg] = true
21
22			if pre == nil || pre(pkg) {
23				paths := make([]string, 0, len(pkg.Imports))
24				for path := range pkg.Imports {
25					paths = append(paths, path)
26				}
27				sort.Strings(paths) // Imports is a map, this makes visit stable
28				for _, path := range paths {
29					visit(pkg.Imports[path])
30				}
31			}
32
33			if post != nil {
34				post(pkg)
35			}
36		}
37	}
38	for _, pkg := range pkgs {
39		visit(pkg)
40	}
41}
42
43// PrintErrors prints to os.Stderr the accumulated errors of all
44// packages in the import graph rooted at pkgs, dependencies first.
45// PrintErrors returns the number of errors printed.
46func PrintErrors(pkgs []*Package) int {
47	var n int
48	Visit(pkgs, nil, func(pkg *Package) {
49		for _, err := range pkg.Errors {
50			fmt.Fprintln(os.Stderr, err)
51			n++
52		}
53	})
54	return n
55}
56