|
|
// +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 main
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 }
|