visit.go (2577B)
1 // Copyright 2018 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package packages 6 7 import ( 8 "fmt" 9 "iter" 10 "os" 11 12 "golang.org/x/tools/internal/moremaps" 13 ) 14 15 // Visit visits all the packages in the import graph whose roots are 16 // pkgs, calling the optional pre function the first time each package 17 // is encountered (preorder), and the optional post function after a 18 // package's dependencies have been visited (postorder). 19 // The boolean result of pre(pkg) determines whether 20 // the imports of package pkg are visited. 21 // 22 // Example: 23 // 24 // pkgs, err := Load(...) 25 // if err != nil { ... } 26 // Visit(pkgs, nil, func(pkg *Package) { 27 // log.Println(pkg) 28 // }) 29 // 30 // In most cases, it is more convenient to use [Postorder]: 31 // 32 // for pkg := range Postorder(pkgs) { 33 // log.Println(pkg) 34 // } 35 func Visit(pkgs []*Package, pre func(*Package) bool, post func(*Package)) { 36 seen := make(map[*Package]bool) 37 var visit func(*Package) 38 visit = func(pkg *Package) { 39 if !seen[pkg] { 40 seen[pkg] = true 41 42 if pre == nil || pre(pkg) { 43 for _, imp := range moremaps.Sorted(pkg.Imports) { // for determinism 44 visit(imp) 45 } 46 } 47 48 if post != nil { 49 post(pkg) 50 } 51 } 52 } 53 for _, pkg := range pkgs { 54 visit(pkg) 55 } 56 } 57 58 // PrintErrors prints to os.Stderr the accumulated errors of all 59 // packages in the import graph rooted at pkgs, dependencies first. 60 // PrintErrors returns the number of errors printed. 61 func PrintErrors(pkgs []*Package) int { 62 var n int 63 errModules := make(map[*Module]bool) 64 for pkg := range Postorder(pkgs) { 65 for _, err := range pkg.Errors { 66 fmt.Fprintln(os.Stderr, err) 67 n++ 68 } 69 70 // Print pkg.Module.Error once if present. 71 mod := pkg.Module 72 if mod != nil && mod.Error != nil && !errModules[mod] { 73 errModules[mod] = true 74 fmt.Fprintln(os.Stderr, mod.Error.Err) 75 n++ 76 } 77 } 78 return n 79 } 80 81 // Postorder returns an iterator over the packages in 82 // the import graph whose roots are pkg. 83 // Packages are enumerated in dependencies-first order. 84 func Postorder(pkgs []*Package) iter.Seq[*Package] { 85 return func(yield func(*Package) bool) { 86 seen := make(map[*Package]bool) 87 var visit func(*Package) bool 88 visit = func(pkg *Package) bool { 89 if !seen[pkg] { 90 seen[pkg] = true 91 for _, imp := range moremaps.Sorted(pkg.Imports) { // for determinism 92 if !visit(imp) { 93 return false 94 } 95 } 96 if !yield(pkg) { 97 return false 98 } 99 } 100 return true 101 } 102 for _, pkg := range pkgs { 103 if !visit(pkg) { 104 break 105 } 106 } 107 } 108 }