github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/internal/imports/mkindex.go (about) 1 //go:build ignore 2 // +build ignore 3 4 // Copyright 2013 The Go Authors. All rights reserved. 5 // Use of this source code is governed by a BSD-style 6 // license that can be found in the LICENSE file. 7 8 // Command mkindex creates the file "pkgindex.go" containing an index of the Go 9 // standard library. The file is intended to be built as part of the imports 10 // package, so that the package may be used in environments where a GOROOT is 11 // not available (such as App Engine). 12 package imports 13 14 import ( 15 "bytes" 16 "fmt" 17 "go/ast" 18 "go/build" 19 "go/format" 20 "go/parser" 21 "go/token" 22 "io/ioutil" 23 "log" 24 "os" 25 "path" 26 "path/filepath" 27 "strings" 28 ) 29 30 var ( 31 pkgIndex = make(map[string][]pkg) 32 exports = make(map[string]map[string]bool) 33 ) 34 35 func main() { 36 // Don't use GOPATH. 37 ctx := build.Default 38 ctx.GOPATH = "" 39 40 // Populate pkgIndex global from GOROOT. 41 for _, path := range ctx.SrcDirs() { 42 f, err := os.Open(path) 43 if err != nil { 44 log.Print(err) 45 continue 46 } 47 children, err := f.Readdir(-1) 48 f.Close() 49 if err != nil { 50 log.Print(err) 51 continue 52 } 53 for _, child := range children { 54 if child.IsDir() { 55 loadPkg(path, child.Name()) 56 } 57 } 58 } 59 // Populate exports global. 60 for _, ps := range pkgIndex { 61 for _, p := range ps { 62 e := loadExports(p.dir) 63 if e != nil { 64 exports[p.dir] = e 65 } 66 } 67 } 68 69 // Construct source file. 70 var buf bytes.Buffer 71 fmt.Fprint(&buf, pkgIndexHead) 72 fmt.Fprintf(&buf, "var pkgIndexMaster = %#v\n", pkgIndex) 73 fmt.Fprintf(&buf, "var exportsMaster = %#v\n", exports) 74 src := buf.Bytes() 75 76 // Replace main.pkg type name with pkg. 77 src = bytes.Replace(src, []byte("main.pkg"), []byte("pkg"), -1) 78 // Replace actual GOROOT with "/go". 79 src = bytes.Replace(src, []byte(ctx.GOROOT), []byte("/go"), -1) 80 // Add some line wrapping. 81 src = bytes.Replace(src, []byte("}, "), []byte("},\n"), -1) 82 src = bytes.Replace(src, []byte("true, "), []byte("true,\n"), -1) 83 84 var err error 85 src, err = format.Source(src) 86 if err != nil { 87 log.Fatal(err) 88 } 89 90 // Write out source file. 91 err = ioutil.WriteFile("pkgindex.go", src, 0644) 92 if err != nil { 93 log.Fatal(err) 94 } 95 } 96 97 const pkgIndexHead = `package imports 98 99 func init() { 100 pkgIndexOnce.Do(func() { 101 pkgIndex.m = pkgIndexMaster 102 }) 103 loadExports = func(dir string) map[string]bool { 104 return exportsMaster[dir] 105 } 106 } 107 ` 108 109 type pkg struct { 110 importpath string // full pkg import path, e.g. "net/http" 111 dir string // absolute file path to pkg directory e.g. "/usr/lib/go/src/fmt" 112 } 113 114 var fset = token.NewFileSet() 115 116 func loadPkg(root, importpath string) { 117 shortName := path.Base(importpath) 118 if shortName == "testdata" { 119 return 120 } 121 122 dir := filepath.Join(root, importpath) 123 pkgIndex[shortName] = append(pkgIndex[shortName], pkg{ 124 importpath: importpath, 125 dir: dir, 126 }) 127 128 pkgDir, err := os.Open(dir) 129 if err != nil { 130 return 131 } 132 children, err := pkgDir.Readdir(-1) 133 pkgDir.Close() 134 if err != nil { 135 return 136 } 137 for _, child := range children { 138 name := child.Name() 139 if name == "" { 140 continue 141 } 142 if c := name[0]; c == '.' || ('0' <= c && c <= '9') { 143 continue 144 } 145 if child.IsDir() { 146 loadPkg(root, filepath.Join(importpath, name)) 147 } 148 } 149 } 150 151 func loadExports(dir string) map[string]bool { 152 exports := make(map[string]bool) 153 buildPkg, err := build.ImportDir(dir, 0) 154 if err != nil { 155 if strings.Contains(err.Error(), "no buildable Go source files in") { 156 return nil 157 } 158 log.Printf("could not import %q: %v", dir, err) 159 return nil 160 } 161 for _, file := range buildPkg.GoFiles { 162 f, err := parser.ParseFile(fset, filepath.Join(dir, file), nil, 0) 163 if err != nil { 164 log.Printf("could not parse %q: %v", file, err) 165 continue 166 } 167 for name := range f.Scope.Objects { 168 if ast.IsExported(name) { 169 exports[name] = true 170 } 171 } 172 } 173 return exports 174 }