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 | } |
175 |
Members