imports: bake in the standard library again

Fast path for the common case. Avoids scanning GOPATH usually.

LGTM=r, crawshaw
R=david.crawshaw, adg, r, crawshaw
CC=golang-codereviews
https://golang.org/cl/56820043
This commit is contained in:
Brad Fitzpatrick 2014-01-26 09:47:31 -08:00
parent 3b2989e9ea
commit eda00ba4ec
4 changed files with 8526 additions and 0 deletions

View File

@ -271,6 +271,12 @@ func loadExportsGoPath(dir string) map[string]bool {
var findImport = findImportGoPath
func findImportGoPath(pkgName string, symbols map[string]bool) (string, error) {
// Fast path for the standard library.
// In the common case we hopefully never have to scan the GOPATH, which can
// be slow with moving disks.
if pkg, ok := findImportStdlib(pkgName, symbols); ok {
return pkg, nil
}
pkgIndexOnce.Do(loadPkgIndex)
@ -325,3 +331,18 @@ type visitFn func(node ast.Node) ast.Visitor
func (fn visitFn) Visit(node ast.Node) ast.Visitor {
return fn(node)
}
func findImportStdlib(shortPkg string, symbols map[string]bool) (importPath string, ok bool) {
for symbol := range symbols {
path := stdlib[shortPkg+"."+symbol]
if path == "" {
return "", false
}
if importPath != "" && importPath != path {
// Ambiguous. Symbols pointed to different things.
return "", false
}
importPath = path
}
return importPath, importPath != ""
}

View File

@ -10,6 +10,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
"sync"
"testing"
)
@ -508,6 +509,15 @@ func TestFindImportGoPath(t *testing.T) {
t.Fatal(err)
}
defer os.RemoveAll(goroot)
pkgIndexOnce = sync.Once{}
origStdlib := stdlib
defer func() {
stdlib = origStdlib
}()
stdlib = nil
// Test against imaginary bits/bytes package in std lib
bytesDir := filepath.Join(goroot, "src", "pkg", "bits", "bytes")
if err := os.MkdirAll(bytesDir, 0755); err != nil {
@ -547,3 +557,34 @@ type Buffer2 struct {}
t.Errorf(`findImportGoPath("bytes", Missing ...)=%q, want ""`, got)
}
}
func TestFindImportStdlib(t *testing.T) {
tests := []struct {
pkg string
symbols []string
want string
}{
{"http", []string{"Get"}, "net/http"},
{"http", []string{"Get", "Post"}, "net/http"},
{"http", []string{"Get", "Foo"}, ""},
{"bytes", []string{"Buffer"}, "bytes"},
{"ioutil", []string{"Discard"}, "io/ioutil"},
}
for _, tt := range tests {
got, ok := findImportStdlib(tt.pkg, strSet(tt.symbols))
if (got != "") != ok {
t.Error("findImportStdlib return value inconsistent")
}
if got != tt.want {
t.Errorf("findImportStdlib(%q, %q) = %q; want %q", tt.pkg, tt.symbols, got, tt.want)
}
}
}
func strSet(ss []string) map[string]bool {
m := make(map[string]bool)
for _, s := range ss {
m[s] = true
}
return m
}

90
imports/mkstdlib.go Normal file
View File

@ -0,0 +1,90 @@
// +build ignore
// mkstdlib generates the zstdlib.go file, containing the Go standard
// library API symbols. It's baked into the binary to avoid scanning
// GOPATH in the common case.
package main
import (
"bufio"
"bytes"
"fmt"
"go/format"
"io"
"log"
"os"
"path"
"path/filepath"
"regexp"
"sort"
"strings"
)
func mustOpen(name string) io.Reader {
f, err := os.Open(name)
if err != nil {
log.Fatal(err)
}
return f
}
func api(base string) string {
return filepath.Join(os.Getenv("GOROOT"), "api", base)
}
var sym = regexp.MustCompile(`^pkg (\S+).*?, (?:var|func|type|const) ([A-Z]\w*)`)
func main() {
var buf bytes.Buffer
outf := func(format string, args ...interface{}) {
fmt.Fprintf(&buf, format, args...)
}
outf("// AUTO-GENERATED BY mkstdlib.go\n\n")
outf("package imports\n")
outf("var stdlib = map[string]string{\n")
f := io.MultiReader(
mustOpen(api("go1.txt")),
mustOpen(api("go1.1.txt")),
mustOpen(api("go1.2.txt")),
)
sc := bufio.NewScanner(f)
fullImport := map[string]string{} // "zip.NewReader" => "archive/zip"
ambiguous := map[string]bool{}
var keys []string
for sc.Scan() {
l := sc.Text()
has := func(v string) bool { return strings.Contains(l, v) }
if has("struct, ") || has("interface, ") || has(", method (") {
continue
}
if m := sym.FindStringSubmatch(l); m != nil {
full := m[1]
key := path.Base(full) + "." + m[2]
if exist, ok := fullImport[key]; ok {
if exist != full {
ambiguous[key] = true
}
} else {
fullImport[key] = full
keys = append(keys, key)
}
}
}
if err := sc.Err(); err != nil {
log.Fatal(err)
}
sort.Strings(keys)
for _, key := range keys {
if ambiguous[key] {
outf("\t// %q is ambiguous\n", key)
} else {
outf("\t%q: %q,\n", key, fullImport[key])
}
}
outf("}\n")
fmtbuf, err := format.Source(buf.Bytes())
if err != nil {
log.Fatal(err)
}
os.Stdout.Write(fmtbuf)
}

8374
imports/zstdlib.go Normal file

File diff suppressed because it is too large Load Diff