aboutsummaryrefslogtreecommitdiff
path: root/vendor/golang.org/x/tools/go/buildutil/allpackages.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/golang.org/x/tools/go/buildutil/allpackages.go')
-rw-r--r--vendor/golang.org/x/tools/go/buildutil/allpackages.go198
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.
10package buildutil // import "golang.org/x/tools/go/buildutil"
11
12import (
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//
32func 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//
52func 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
75type 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.
82var ioLimit = make(chan bool, 20)
83
84func 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//
143func 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}