github.com/pankona/gometalinter@v2.0.11+incompatible/_linters/src/golang.org/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 } 126 127 // ExpandPatterns returns the set of packages matched by patterns, 128 // which may have the following forms: 129 // 130 // golang.org/x/tools/cmd/guru # a single package 131 // golang.org/x/tools/... # all packages beneath dir 132 // ... # the entire workspace. 133 // 134 // Order is significant: a pattern preceded by '-' removes matching 135 // packages from the set. For example, these patterns match all encoding 136 // packages except encoding/xml: 137 // 138 // encoding/... -encoding/xml 139 // 140 // A trailing slash in a pattern is ignored. (Path components of Go 141 // package names are separated by slash, not the platform's path separator.) 142 // 143 func ExpandPatterns(ctxt *build.Context, patterns []string) map[string]bool { 144 // TODO(adonovan): support other features of 'go list': 145 // - "std"/"cmd"/"all" meta-packages 146 // - "..." not at the end of a pattern 147 // - relative patterns using "./" or "../" prefix 148 149 pkgs := make(map[string]bool) 150 doPkg := func(pkg string, neg bool) { 151 if neg { 152 delete(pkgs, pkg) 153 } else { 154 pkgs[pkg] = true 155 } 156 } 157 158 // Scan entire workspace if wildcards are present. 159 // TODO(adonovan): opt: scan only the necessary subtrees of the workspace. 160 var all []string 161 for _, arg := range patterns { 162 if strings.HasSuffix(arg, "...") { 163 all = AllPackages(ctxt) 164 break 165 } 166 } 167 168 for _, arg := range patterns { 169 if arg == "" { 170 continue 171 } 172 173 neg := arg[0] == '-' 174 if neg { 175 arg = arg[1:] 176 } 177 178 if arg == "..." { 179 // ... matches all packages 180 for _, pkg := range all { 181 doPkg(pkg, neg) 182 } 183 } else if dir := strings.TrimSuffix(arg, "/..."); dir != arg { 184 // dir/... matches all packages beneath dir 185 for _, pkg := range all { 186 if strings.HasPrefix(pkg, dir) && 187 (len(pkg) == len(dir) || pkg[len(dir)] == '/') { 188 doPkg(pkg, neg) 189 } 190 } 191 } else { 192 // single package 193 doPkg(strings.TrimSuffix(arg, "/"), neg) 194 } 195 } 196 197 return pkgs 198 }