github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/tools/go/buildutil/allpackages.go (about) 1 // Copyright 2014 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 buildutil provides utilities related to the go/build 6 // package in the standard library. 7 // 8 // All I/O is done via the build.Context file system interface, which must 9 // be concurrency-safe. 10 package buildutil // import "golang.org/x/tools/go/buildutil" 11 12 import ( 13 "go/build" 14 "os" 15 "path/filepath" 16 "sort" 17 "strings" 18 "sync" 19 ) 20 21 // AllPackages returns the package path of each Go package in any source 22 // directory of the specified build context (e.g. $GOROOT or an element 23 // of $GOPATH). Errors are ignored. The results are sorted. 24 // All package paths are canonical, and thus may contain "/vendor/". 25 // 26 // The result may include import paths for directories that contain no 27 // *.go files, such as "archive" (in $GOROOT/src). 28 // 29 // All I/O is done via the build.Context file system interface, 30 // which must be concurrency-safe. 31 // 32 func AllPackages(ctxt *build.Context) []string { 33 var list []string 34 ForEachPackage(ctxt, func(pkg string, _ error) { 35 list = append(list, pkg) 36 }) 37 sort.Strings(list) 38 return list 39 } 40 41 // ForEachPackage calls the found function with the package path of 42 // each Go package it finds in any source directory of the specified 43 // build context (e.g. $GOROOT or an element of $GOPATH). 44 // All package paths are canonical, and thus may contain "/vendor/". 45 // 46 // If the package directory exists but could not be read, the second 47 // argument to the found function provides the error. 48 // 49 // All I/O is done via the build.Context file system interface, 50 // which must be concurrency-safe. 51 // 52 func ForEachPackage(ctxt *build.Context, found func(importPath string, err error)) { 53 ch := make(chan item) 54 55 var wg sync.WaitGroup 56 for _, root := range ctxt.SrcDirs() { 57 root := root 58 wg.Add(1) 59 go func() { 60 allPackages(ctxt, root, ch) 61 wg.Done() 62 }() 63 } 64 go func() { 65 wg.Wait() 66 close(ch) 67 }() 68 69 // All calls to found occur in the caller's goroutine. 70 for i := range ch { 71 found(i.importPath, i.err) 72 } 73 } 74 75 type item struct { 76 importPath string 77 err error // (optional) 78 } 79 80 // We use a process-wide counting semaphore to limit 81 // the number of parallel calls to ReadDir. 82 var ioLimit = make(chan bool, 20) 83 84 func allPackages(ctxt *build.Context, root string, ch chan<- item) { 85 root = filepath.Clean(root) + string(os.PathSeparator) 86 87 var wg sync.WaitGroup 88 89 var walkDir func(dir string) 90 walkDir = func(dir string) { 91 // Avoid .foo, _foo, and testdata directory trees. 92 base := filepath.Base(dir) 93 if base == "" || base[0] == '.' || base[0] == '_' || base == "testdata" { 94 return 95 } 96 97 pkg := filepath.ToSlash(strings.TrimPrefix(dir, root)) 98 99 // Prune search if we encounter any of these import paths. 100 switch pkg { 101 case "builtin": 102 return 103 } 104 105 ioLimit <- true 106 files, err := ReadDir(ctxt, dir) 107 <-ioLimit 108 if pkg != "" || err != nil { 109 ch <- item{pkg, err} 110 } 111 for _, fi := range files { 112 fi := fi 113 if fi.IsDir() { 114 wg.Add(1) 115 go func() { 116 walkDir(filepath.Join(dir, fi.Name())) 117 wg.Done() 118 }() 119 } 120 } 121 } 122 123 walkDir(root) 124 wg.Wait() 125 }