github.com/megatontech/mynoteforgo@v0.0.0-20200507084910-5d0c6ea6e890/源码/cmd/doc/dirs.go (about) 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 "bytes" 9 "log" 10 "os" 11 "os/exec" 12 "path/filepath" 13 "strings" 14 "sync" 15 ) 16 17 // A Dir describes a directory holding code by specifying 18 // the expected import path and the file system directory. 19 type Dir struct { 20 importPath string // import path for that dir 21 dir string // file system directory 22 } 23 24 // Dirs is a structure for scanning the directory tree. 25 // Its Next method returns the next Go source directory it finds. 26 // Although it can be used to scan the tree multiple times, it 27 // only walks the tree once, caching the data it finds. 28 type Dirs struct { 29 scan chan Dir // Directories generated by walk. 30 hist []Dir // History of reported Dirs. 31 offset int // Counter for Next. 32 } 33 34 var dirs Dirs 35 36 // dirsInit starts the scanning of package directories in GOROOT and GOPATH. Any 37 // extra paths passed to it are included in the channel. 38 func dirsInit(extra ...Dir) { 39 dirs.hist = make([]Dir, 0, 1000) 40 dirs.hist = append(dirs.hist, extra...) 41 dirs.scan = make(chan Dir) 42 go dirs.walk(codeRoots()) 43 } 44 45 // Reset puts the scan back at the beginning. 46 func (d *Dirs) Reset() { 47 d.offset = 0 48 } 49 50 // Next returns the next directory in the scan. The boolean 51 // is false when the scan is done. 52 func (d *Dirs) Next() (Dir, bool) { 53 if d.offset < len(d.hist) { 54 dir := d.hist[d.offset] 55 d.offset++ 56 return dir, true 57 } 58 dir, ok := <-d.scan 59 if !ok { 60 return Dir{}, false 61 } 62 d.hist = append(d.hist, dir) 63 d.offset++ 64 return dir, ok 65 } 66 67 // walk walks the trees in GOROOT and GOPATH. 68 func (d *Dirs) walk(roots []Dir) { 69 for _, root := range roots { 70 d.bfsWalkRoot(root) 71 } 72 close(d.scan) 73 } 74 75 // bfsWalkRoot walks a single directory hierarchy in breadth-first lexical order. 76 // Each Go source directory it finds is delivered on d.scan. 77 func (d *Dirs) bfsWalkRoot(root Dir) { 78 root.dir = filepath.Clean(root.dir) // because filepath.Join will do it anyway 79 80 // this is the queue of directories to examine in this pass. 81 this := []string{} 82 // next is the queue of directories to examine in the next pass. 83 next := []string{root.dir} 84 85 for len(next) > 0 { 86 this, next = next, this[0:0] 87 for _, dir := range this { 88 fd, err := os.Open(dir) 89 if err != nil { 90 log.Print(err) 91 continue 92 } 93 entries, err := fd.Readdir(0) 94 fd.Close() 95 if err != nil { 96 log.Print(err) 97 continue 98 } 99 hasGoFiles := false 100 for _, entry := range entries { 101 name := entry.Name() 102 // For plain files, remember if this directory contains any .go 103 // source files, but ignore them otherwise. 104 if !entry.IsDir() { 105 if !hasGoFiles && strings.HasSuffix(name, ".go") { 106 hasGoFiles = true 107 } 108 continue 109 } 110 // Entry is a directory. 111 112 // The go tool ignores directories starting with ., _, or named "testdata". 113 if name[0] == '.' || name[0] == '_' || name == "testdata" { 114 continue 115 } 116 // Ignore vendor when using modules. 117 if usingModules && name == "vendor" { 118 continue 119 } 120 // Remember this (fully qualified) directory for the next pass. 121 next = append(next, filepath.Join(dir, name)) 122 } 123 if hasGoFiles { 124 // It's a candidate. 125 importPath := root.importPath 126 if len(dir) > len(root.dir) { 127 if importPath != "" { 128 importPath += "/" 129 } 130 importPath += filepath.ToSlash(dir[len(root.dir)+1:]) 131 } 132 d.scan <- Dir{importPath, dir} 133 } 134 } 135 136 } 137 } 138 139 var testGOPATH = false // force GOPATH use for testing 140 141 // codeRoots returns the code roots to search for packages. 142 // In GOPATH mode this is GOROOT/src and GOPATH/src, with empty import paths. 143 // In module mode, this is each module root, with an import path set to its module path. 144 func codeRoots() []Dir { 145 codeRootsCache.once.Do(func() { 146 codeRootsCache.roots = findCodeRoots() 147 }) 148 return codeRootsCache.roots 149 } 150 151 var codeRootsCache struct { 152 once sync.Once 153 roots []Dir 154 } 155 156 var usingModules bool 157 158 func findCodeRoots() []Dir { 159 list := []Dir{{"", filepath.Join(buildCtx.GOROOT, "src")}} 160 161 if !testGOPATH { 162 // Check for use of modules by 'go env GOMOD', 163 // which reports a go.mod file path if modules are enabled. 164 stdout, _ := exec.Command("go", "env", "GOMOD").Output() 165 usingModules = len(bytes.TrimSpace(stdout)) > 0 166 } 167 168 if !usingModules { 169 for _, root := range splitGopath() { 170 list = append(list, Dir{"", filepath.Join(root, "src")}) 171 } 172 return list 173 } 174 175 // Find module root directories from go list. 176 // Eventually we want golang.org/x/tools/go/packages 177 // to handle the entire file system search and become go/packages, 178 // but for now enumerating the module roots lets us fit modules 179 // into the current code with as few changes as possible. 180 cmd := exec.Command("go", "list", "-m", "-f={{.Path}}\t{{.Dir}}", "all") 181 cmd.Stderr = os.Stderr 182 out, _ := cmd.Output() 183 for _, line := range strings.Split(string(out), "\n") { 184 i := strings.Index(line, "\t") 185 if i < 0 { 186 continue 187 } 188 path, dir := line[:i], line[i+1:] 189 if dir != "" { 190 list = append(list, Dir{path, dir}) 191 } 192 } 193 194 return list 195 }