Home | History | Annotate | Download | only in doc
      1 // Copyright 2015 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 main
      6 
      7 import (
      8 	"go/build"
      9 	"log"
     10 	"os"
     11 	"path"
     12 	"path/filepath"
     13 	"strings"
     14 )
     15 
     16 // Dirs is a structure for scanning the directory tree.
     17 // Its Next method returns the next Go source directory it finds.
     18 // Although it can be used to scan the tree multiple times, it
     19 // only walks the tree once, caching the data it finds.
     20 type Dirs struct {
     21 	scan   chan string // directories generated by walk.
     22 	paths  []string    // Cache of known paths.
     23 	offset int         // Counter for Next.
     24 }
     25 
     26 var dirs Dirs
     27 
     28 func init() {
     29 	dirs.paths = make([]string, 0, 1000)
     30 	dirs.scan = make(chan string)
     31 	go dirs.walk()
     32 }
     33 
     34 // Reset puts the scan back at the beginning.
     35 func (d *Dirs) Reset() {
     36 	d.offset = 0
     37 }
     38 
     39 // Next returns the next directory in the scan. The boolean
     40 // is false when the scan is done.
     41 func (d *Dirs) Next() (string, bool) {
     42 	if d.offset < len(d.paths) {
     43 		path := d.paths[d.offset]
     44 		d.offset++
     45 		return path, true
     46 	}
     47 	path, ok := <-d.scan
     48 	if !ok {
     49 		return "", false
     50 	}
     51 	d.paths = append(d.paths, path)
     52 	d.offset++
     53 	return path, ok
     54 }
     55 
     56 // walk walks the trees in GOROOT and GOPATH.
     57 func (d *Dirs) walk() {
     58 	d.bfsWalkRoot(build.Default.GOROOT)
     59 	for _, root := range splitGopath() {
     60 		d.bfsWalkRoot(root)
     61 	}
     62 	close(d.scan)
     63 }
     64 
     65 // bfsWalkRoot walks a single directory hierarchy in breadth-first lexical order.
     66 // Each Go source directory it finds is delivered on d.scan.
     67 func (d *Dirs) bfsWalkRoot(root string) {
     68 	root = path.Join(root, "src")
     69 
     70 	// this is the queue of directories to examine in this pass.
     71 	this := []string{}
     72 	// next is the queue of directories to examine in the next pass.
     73 	next := []string{root}
     74 
     75 	for len(next) > 0 {
     76 		this, next = next, this[0:0]
     77 		for _, dir := range this {
     78 			fd, err := os.Open(dir)
     79 			if err != nil {
     80 				log.Print(err)
     81 				continue
     82 			}
     83 			entries, err := fd.Readdir(0)
     84 			fd.Close()
     85 			if err != nil {
     86 				log.Print(err)
     87 				continue
     88 			}
     89 			hasGoFiles := false
     90 			for _, entry := range entries {
     91 				name := entry.Name()
     92 				// For plain files, remember if this directory contains any .go
     93 				// source files, but ignore them otherwise.
     94 				if !entry.IsDir() {
     95 					if !hasGoFiles && strings.HasSuffix(name, ".go") {
     96 						hasGoFiles = true
     97 					}
     98 					continue
     99 				}
    100 				// Entry is a directory.
    101 				// No .git or other dot nonsense please.
    102 				if strings.HasPrefix(name, ".") {
    103 					continue
    104 				}
    105 				// Remember this (fully qualified) directory for the next pass.
    106 				next = append(next, filepath.Join(dir, name))
    107 			}
    108 			if hasGoFiles {
    109 				// It's a candidate.
    110 				d.scan <- dir
    111 			}
    112 		}
    113 
    114 	}
    115 }
    116