174 lines
3.7 KiB
Go
174 lines
3.7 KiB
Go
// +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
|
|
}
|