| // Copyright 2014 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // Package buildutil provides utilities related to the go/build |
| // package in the standard library. |
| // |
| // All I/O is done via the build.Context file system interface, which must |
| // be concurrency-safe. |
| package buildutil // import "llvm.org/llgo/third_party/gotools/go/buildutil" |
| |
| import ( |
| "go/build" |
| "os" |
| "path/filepath" |
| "sort" |
| "strings" |
| "sync" |
| ) |
| |
| // AllPackages returns the import path of each Go package in any source |
| // directory of the specified build context (e.g. $GOROOT or an element |
| // of $GOPATH). Errors are ignored. The results are sorted. |
| // |
| // The result may include import paths for directories that contain no |
| // *.go files, such as "archive" (in $GOROOT/src). |
| // |
| // All I/O is done via the build.Context file system interface, |
| // which must be concurrency-safe. |
| // |
| func AllPackages(ctxt *build.Context) []string { |
| var list []string |
| ForEachPackage(ctxt, func(pkg string, _ error) { |
| list = append(list, pkg) |
| }) |
| sort.Strings(list) |
| return list |
| } |
| |
| // ForEachPackage calls the found function with the import path of |
| // each Go package it finds in any source directory of the specified |
| // build context (e.g. $GOROOT or an element of $GOPATH). |
| // |
| // If the package directory exists but could not be read, the second |
| // argument to the found function provides the error. |
| // |
| // All I/O is done via the build.Context file system interface, |
| // which must be concurrency-safe. |
| // |
| func ForEachPackage(ctxt *build.Context, found func(importPath string, err error)) { |
| // We use a counting semaphore to limit |
| // the number of parallel calls to ReadDir. |
| sema := make(chan bool, 20) |
| |
| ch := make(chan item) |
| |
| var wg sync.WaitGroup |
| for _, root := range ctxt.SrcDirs() { |
| root := root |
| wg.Add(1) |
| go func() { |
| allPackages(ctxt, sema, root, ch) |
| wg.Done() |
| }() |
| } |
| go func() { |
| wg.Wait() |
| close(ch) |
| }() |
| |
| // All calls to found occur in the caller's goroutine. |
| for i := range ch { |
| found(i.importPath, i.err) |
| } |
| } |
| |
| type item struct { |
| importPath string |
| err error // (optional) |
| } |
| |
| func allPackages(ctxt *build.Context, sema chan bool, root string, ch chan<- item) { |
| root = filepath.Clean(root) + string(os.PathSeparator) |
| |
| var wg sync.WaitGroup |
| |
| var walkDir func(dir string) |
| walkDir = func(dir string) { |
| // Avoid .foo, _foo, and testdata directory trees. |
| base := filepath.Base(dir) |
| if base == "" || base[0] == '.' || base[0] == '_' || base == "testdata" { |
| return |
| } |
| |
| pkg := filepath.ToSlash(strings.TrimPrefix(dir, root)) |
| |
| // Prune search if we encounter any of these import paths. |
| switch pkg { |
| case "builtin": |
| return |
| } |
| |
| sema <- true |
| files, err := ReadDir(ctxt, dir) |
| <-sema |
| if pkg != "" || err != nil { |
| ch <- item{pkg, err} |
| } |
| for _, fi := range files { |
| fi := fi |
| if fi.IsDir() { |
| wg.Add(1) |
| go func() { |
| walkDir(filepath.Join(dir, fi.Name())) |
| wg.Done() |
| }() |
| } |
| } |
| } |
| |
| walkDir(root) |
| wg.Wait() |
| } |