| // +build ignore |
| |
| // Copyright 2013 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // Command mkindex creates the file "pkgindex.go" containing an index of the Go |
| // standard library. The file is intended to be built as part of the imports |
| // package, so that the package may be used in environments where a GOROOT is |
| // not available (such as App Engine). |
| package imports |
| |
| import ( |
| "bytes" |
| "fmt" |
| "go/ast" |
| "go/build" |
| "go/format" |
| "go/parser" |
| "go/token" |
| "io/ioutil" |
| "log" |
| "os" |
| "path" |
| "path/filepath" |
| "strings" |
| ) |
| |
| var ( |
| pkgIndex = make(map[string][]pkg) |
| exports = make(map[string]map[string]bool) |
| ) |
| |
| func main() { |
| // Don't use GOPATH. |
| ctx := build.Default |
| ctx.GOPATH = "" |
| |
| // Populate pkgIndex global from GOROOT. |
| for _, path := range ctx.SrcDirs() { |
| f, err := os.Open(path) |
| if err != nil { |
| log.Print(err) |
| continue |
| } |
| children, err := f.Readdir(-1) |
| f.Close() |
| if err != nil { |
| log.Print(err) |
| continue |
| } |
| for _, child := range children { |
| if child.IsDir() { |
| loadPkg(path, child.Name()) |
| } |
| } |
| } |
| // Populate exports global. |
| for _, ps := range pkgIndex { |
| for _, p := range ps { |
| e := loadExports(p.dir) |
| if e != nil { |
| exports[p.dir] = e |
| } |
| } |
| } |
| |
| // Construct source file. |
| var buf bytes.Buffer |
| fmt.Fprint(&buf, pkgIndexHead) |
| fmt.Fprintf(&buf, "var pkgIndexMaster = %#v\n", pkgIndex) |
| fmt.Fprintf(&buf, "var exportsMaster = %#v\n", exports) |
| src := buf.Bytes() |
| |
| // Replace main.pkg type name with pkg. |
| src = bytes.Replace(src, []byte("main.pkg"), []byte("pkg"), -1) |
| // Replace actual GOROOT with "/go". |
| src = bytes.Replace(src, []byte(ctx.GOROOT), []byte("/go"), -1) |
| // Add some line wrapping. |
| src = bytes.Replace(src, []byte("}, "), []byte("},\n"), -1) |
| src = bytes.Replace(src, []byte("true, "), []byte("true,\n"), -1) |
| |
| var err error |
| src, err = format.Source(src) |
| if err != nil { |
| log.Fatal(err) |
| } |
| |
| // Write out source file. |
| err = ioutil.WriteFile("pkgindex.go", src, 0644) |
| if err != nil { |
| log.Fatal(err) |
| } |
| } |
| |
| const pkgIndexHead = `package imports |
| |
| func init() { |
| pkgIndexOnce.Do(func() { |
| pkgIndex.m = pkgIndexMaster |
| }) |
| loadExports = func(dir string) map[string]bool { |
| return exportsMaster[dir] |
| } |
| } |
| ` |
| |
| type pkg struct { |
| importpath string // full pkg import path, e.g. "net/http" |
| dir string // absolute file path to pkg directory e.g. "/usr/lib/go/src/fmt" |
| } |
| |
| var fset = token.NewFileSet() |
| |
| func loadPkg(root, importpath string) { |
| shortName := path.Base(importpath) |
| if shortName == "testdata" { |
| return |
| } |
| |
| dir := filepath.Join(root, importpath) |
| pkgIndex[shortName] = append(pkgIndex[shortName], pkg{ |
| importpath: importpath, |
| dir: dir, |
| }) |
| |
| pkgDir, err := os.Open(dir) |
| if err != nil { |
| return |
| } |
| children, err := pkgDir.Readdir(-1) |
| pkgDir.Close() |
| if err != nil { |
| return |
| } |
| for _, child := range children { |
| name := child.Name() |
| if name == "" { |
| continue |
| } |
| if c := name[0]; c == '.' || ('0' <= c && c <= '9') { |
| continue |
| } |
| if child.IsDir() { |
| loadPkg(root, filepath.Join(importpath, name)) |
| } |
| } |
| } |
| |
| func loadExports(dir string) map[string]bool { |
| exports := make(map[string]bool) |
| buildPkg, err := build.ImportDir(dir, 0) |
| if err != nil { |
| if strings.Contains(err.Error(), "no buildable Go source files in") { |
| return nil |
| } |
| log.Printf("could not import %q: %v", dir, err) |
| return nil |
| } |
| for _, file := range buildPkg.GoFiles { |
| f, err := parser.ParseFile(fset, filepath.Join(dir, file), nil, 0) |
| if err != nil { |
| log.Printf("could not parse %q: %v", file, err) |
| continue |
| } |
| for name := range f.Scope.Objects { |
| if ast.IsExported(name) { |
| exports[name] = true |
| } |
| } |
| } |
| return exports |
| } |