mkindex.go 3.7 KB

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