diff options
Diffstat (limited to 'vendor/golang.org/x/tools/go/buildutil/allpackages.go')
-rw-r--r-- | vendor/golang.org/x/tools/go/buildutil/allpackages.go | 198 |
1 files changed, 198 insertions, 0 deletions
diff --git a/vendor/golang.org/x/tools/go/buildutil/allpackages.go b/vendor/golang.org/x/tools/go/buildutil/allpackages.go new file mode 100644 index 0000000..c0cb03e --- /dev/null +++ b/vendor/golang.org/x/tools/go/buildutil/allpackages.go | |||
@@ -0,0 +1,198 @@ | |||
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 | } | ||