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