GoPLS Viewer

Home|gopls/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.
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.
31func AllPackages(ctxt *build.Context) []string {
32    var list []string
33    ForEachPackage(ctxt, func(pkg string_ error) {
34        list = append(listpkg)
35    })
36    sort.Strings(list)
37    return list
38}
39
40// ForEachPackage calls the found function with the package path of
41// each Go package it finds in any source directory of the specified
42// build context (e.g. $GOROOT or an element of $GOPATH).
43// All package paths are canonical, and thus may contain "/vendor/".
44//
45// If the package directory exists but could not be read, the second
46// argument to the found function provides the error.
47//
48// All I/O is done via the build.Context file system interface,
49// which must be concurrency-safe.
50func ForEachPackage(ctxt *build.Contextfound func(importPath stringerr error)) {
51    ch := make(chan item)
52
53    var wg sync.WaitGroup
54    for _root := range ctxt.SrcDirs() {
55        root := root
56        wg.Add(1)
57        go func() {
58            allPackages(ctxtrootch)
59            wg.Done()
60        }()
61    }
62    go func() {
63        wg.Wait()
64        close(ch)
65    }()
66
67    // All calls to found occur in the caller's goroutine.
68    for i := range ch {
69        found(i.importPathi.err)
70    }
71}
72
73type item struct {
74    importPath string
75    err        error // (optional)
76}
77
78// We use a process-wide counting semaphore to limit
79// the number of parallel calls to ReadDir.
80var ioLimit = make(chan bool20)
81
82func allPackages(ctxt *build.Contextroot stringch chan<- item) {
83    root = filepath.Clean(root) + string(os.PathSeparator)
84
85    var wg sync.WaitGroup
86
87    var walkDir func(dir string)
88    walkDir = func(dir string) {
89        // Avoid .foo, _foo, and testdata directory trees.
90        base := filepath.Base(dir)
91        if base == "" || base[0] == '.' || base[0] == '_' || base == "testdata" {
92            return
93        }
94
95        pkg := filepath.ToSlash(strings.TrimPrefix(dirroot))
96
97        // Prune search if we encounter any of these import paths.
98        switch pkg {
99        case "builtin":
100            return
101        }
102
103        ioLimit <- true
104        fileserr := ReadDir(ctxtdir)
105        <-ioLimit
106        if pkg != "" || err != nil {
107            ch <- item{pkgerr}
108        }
109        for _fi := range files {
110            fi := fi
111            if fi.IsDir() {
112                wg.Add(1)
113                go func() {
114                    walkDir(filepath.Join(dirfi.Name()))
115                    wg.Done()
116                }()
117            }
118        }
119    }
120
121    walkDir(root)
122    wg.Wait()
123}
124
125// ExpandPatterns returns the set of packages matched by patterns,
126// which may have the following forms:
127//
128//    golang.org/x/tools/cmd/guru     # a single package
129//    golang.org/x/tools/...          # all packages beneath dir
130//    ...                             # the entire workspace.
131//
132// Order is significant: a pattern preceded by '-' removes matching
133// packages from the set.  For example, these patterns match all encoding
134// packages except encoding/xml:
135//
136//    encoding/... -encoding/xml
137//
138// A trailing slash in a pattern is ignored.  (Path components of Go
139// package names are separated by slash, not the platform's path separator.)
140func ExpandPatterns(ctxt *build.Contextpatterns []string) map[string]bool {
141    // TODO(adonovan): support other features of 'go list':
142    // - "std"/"cmd"/"all" meta-packages
143    // - "..." not at the end of a pattern
144    // - relative patterns using "./" or "../" prefix
145
146    pkgs := make(map[string]bool)
147    doPkg := func(pkg stringneg bool) {
148        if neg {
149            delete(pkgspkg)
150        } else {
151            pkgs[pkg] = true
152        }
153    }
154
155    // Scan entire workspace if wildcards are present.
156    // TODO(adonovan): opt: scan only the necessary subtrees of the workspace.
157    var all []string
158    for _arg := range patterns {
159        if strings.HasSuffix(arg"...") {
160            all = AllPackages(ctxt)
161            break
162        }
163    }
164
165    for _arg := range patterns {
166        if arg == "" {
167            continue
168        }
169
170        neg := arg[0] == '-'
171        if neg {
172            arg = arg[1:]
173        }
174
175        if arg == "..." {
176            // ... matches all packages
177            for _pkg := range all {
178                doPkg(pkgneg)
179            }
180        } else if dir := strings.TrimSuffix(arg"/..."); dir != arg {
181            // dir/... matches all packages beneath dir
182            for _pkg := range all {
183                if strings.HasPrefix(pkgdir) &&
184                    (len(pkg) == len(dir) || pkg[len(dir)] == '/') {
185                    doPkg(pkgneg)
186                }
187            }
188        } else {
189            // single package
190            doPkg(strings.TrimSuffix(arg"/"), neg)
191        }
192    }
193
194    return pkgs
195}
196
MembersX
ForEachPackage.ch
ExpandPatterns
os
strings
allPackages.BlockStmt.pkg
ExpandPatterns.RangeStmt_4375.arg
ExpandPatterns.RangeStmt_4488.BlockStmt.BlockStmt.RangeStmt_4663.pkg
filepath
allPackages.BlockStmt.base
AllPackages
ForEachPackage.wg
item.importPath
allPackages
allPackages.root
allPackages.wg
build
sort
ExpandPatterns.all
allPackages.BlockStmt.RangeStmt_2981.BlockStmt.fi
ExpandPatterns.patterns
ForEachPackage.ctxt
ForEachPackage.found
allPackages.ctxt
allPackages.ch
AllPackages.ctxt
ForEachPackage
allPackages.BlockStmt.RangeStmt_2981.fi
ExpandPatterns.pkgs
ForEachPackage.RangeStmt_1813.root
item
item.err
allPackages.BlockStmt.files
ExpandPatterns.RangeStmt_4488.BlockStmt.dir
ForEachPackage.RangeStmt_1813.BlockStmt.root
ForEachPackage.RangeStmt_2045.i
ExpandPatterns.RangeStmt_4488.arg
ExpandPatterns.RangeStmt_4488.BlockStmt.BlockStmt.RangeStmt_4829.pkg
sync
ExpandPatterns.ctxt
AllPackages.list
allPackages.BlockStmt.err
Members
X