Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201105173854-bc9fc8d8c4bc / go / buildutil / allpackages.go
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 }