github.com/llvm-mirror/llgo@v0.0.0-20190322182713-bf6f0a60fce1/third_party/gotools/go/buildutil/allpackages.go (about)

     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 "llvm.org/llgo/third_party/gotools/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 import 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  //
    25  // The result may include import paths for directories that contain no
    26  // *.go files, such as "archive" (in $GOROOT/src).
    27  //
    28  // All I/O is done via the build.Context file system interface,
    29  // which must be concurrency-safe.
    30  //
    31  func AllPackages(ctxt *build.Context) []string {
    32  	var list []string
    33  	ForEachPackage(ctxt, func(pkg string, _ error) {
    34  		list = append(list, pkg)
    35  	})
    36  	sort.Strings(list)
    37  	return list
    38  }
    39  
    40  // ForEachPackage calls the found function with the import 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  //
    44  // If the package directory exists but could not be read, the second
    45  // argument to the found function provides the error.
    46  //
    47  // All I/O is done via the build.Context file system interface,
    48  // which must be concurrency-safe.
    49  //
    50  func ForEachPackage(ctxt *build.Context, found func(importPath string, err error)) {
    51  	// We use a counting semaphore to limit
    52  	// the number of parallel calls to ReadDir.
    53  	sema := make(chan bool, 20)
    54  
    55  	ch := make(chan item)
    56  
    57  	var wg sync.WaitGroup
    58  	for _, root := range ctxt.SrcDirs() {
    59  		root := root
    60  		wg.Add(1)
    61  		go func() {
    62  			allPackages(ctxt, sema, root, ch)
    63  			wg.Done()
    64  		}()
    65  	}
    66  	go func() {
    67  		wg.Wait()
    68  		close(ch)
    69  	}()
    70  
    71  	// All calls to found occur in the caller's goroutine.
    72  	for i := range ch {
    73  		found(i.importPath, i.err)
    74  	}
    75  }
    76  
    77  type item struct {
    78  	importPath string
    79  	err        error // (optional)
    80  }
    81  
    82  func allPackages(ctxt *build.Context, sema chan bool, root string, ch 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(dir, root))
    96  
    97  		// Prune search if we encounter any of these import paths.
    98  		switch pkg {
    99  		case "builtin":
   100  			return
   101  		}
   102  
   103  		sema <- true
   104  		files, err := ReadDir(ctxt, dir)
   105  		<-sema
   106  		if pkg != "" || err != nil {
   107  			ch <- item{pkg, err}
   108  		}
   109  		for _, fi := range files {
   110  			fi := fi
   111  			if fi.IsDir() {
   112  				wg.Add(1)
   113  				go func() {
   114  					walkDir(filepath.Join(dir, fi.Name()))
   115  					wg.Done()
   116  				}()
   117  			}
   118  		}
   119  	}
   120  
   121  	walkDir(root)
   122  	wg.Wait()
   123  }