mirror of
https://github.com/golang/go.git
synced 2025-05-05 15:43:04 +00:00
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:
parent
3b2989e9ea
commit
eda00ba4ec
@ -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 != ""
|
||||
}
|
||||
|
@ -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
90
imports/mkstdlib.go
Normal 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
8374
imports/zstdlib.go
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user