1
0
mirror of https://github.com/golang/go synced 2024-09-30 01:14:29 -06:00

cmd/api: use 'go list' to locate transitive dependencies of std

With standard-library modules and vendoring, the mapping from import
path to directory within the standard library is no longer entirely
trivial. Fortunately, 'go list' makes that mapping straightforward to
compute.

Updates #30241
Updates #30228

Change-Id: Iddd77c21a527b7acdb30c17bec8b4bbd43e23756
Reviewed-on: https://go-review.googlesource.com/c/go/+/165497
Run-TryBot: Bryan C. Mills <bcmills@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Jay Conrod <jayconrod@google.com>
This commit is contained in:
Bryan C. Mills 2019-03-05 16:52:48 -05:00
parent 49662bc6b0
commit 361a01983f

View File

@ -8,6 +8,7 @@ package main
import (
"bufio"
"bytes"
"encoding/json"
"flag"
"fmt"
"go/ast"
@ -153,6 +154,7 @@ func main() {
var featureCtx = make(map[string]map[string]bool) // feature -> context name -> true
for _, context := range contexts {
w := NewWalker(context, filepath.Join(build.Default.GOROOT, "src"))
w.loadImports(pkgNames, w.context)
for _, name := range pkgNames {
// Vendored packages do not contribute to our
@ -355,6 +357,8 @@ type Walker struct {
current *types.Package
features map[string]bool // set
imported map[string]*types.Package // packages already imported
importMap map[string]map[string]string // importer dir -> import path -> canonical path
importDir map[string]string // canonical import path -> dir
}
func NewWalker(context *build.Context, root string) *Walker {
@ -434,11 +438,74 @@ func tagKey(dir string, context *build.Context, tags []string) string {
return key
}
func (w *Walker) loadImports(paths []string, context *build.Context) {
if context == nil {
context = &build.Default
}
var (
tags = context.BuildTags
cgoEnabled = "0"
)
if context.CgoEnabled {
tags = append(tags[:len(tags):len(tags)], "cgo")
cgoEnabled = "1"
}
// TODO(golang.org/issue/29666): Request only the fields that we need.
cmd := exec.Command(goCmd(), "list", "-e", "-deps", "-json")
if len(tags) > 0 {
cmd.Args = append(cmd.Args, "-tags", strings.Join(tags, " "))
}
cmd.Args = append(cmd.Args, paths...)
cmd.Env = append(os.Environ(),
"GOOS="+context.GOOS,
"GOARCH="+context.GOARCH,
"CGO_ENABLED="+cgoEnabled,
)
stdout := new(bytes.Buffer)
cmd.Stdout = stdout
cmd.Stderr = new(strings.Builder)
err := cmd.Run()
if err != nil {
log.Fatalf("%s failed: %v\n%s", strings.Join(cmd.Args, " "), err, cmd.Stderr)
}
w.importDir = make(map[string]string)
w.importMap = make(map[string]map[string]string)
dec := json.NewDecoder(stdout)
for {
var pkg struct {
ImportPath, Dir string
ImportMap map[string]string
}
if err := dec.Decode(&pkg); err == io.EOF {
break
} else if err != nil {
log.Fatalf("%s: invalid output: %v", strings.Join(cmd.Args, " "), err)
}
w.importDir[pkg.ImportPath] = pkg.Dir
w.importMap[pkg.Dir] = pkg.ImportMap
}
}
// Importing is a sentinel taking the place in Walker.imported
// for a package that is in the process of being imported.
var importing types.Package
func (w *Walker) Import(name string) (*types.Package, error) {
return w.ImportFrom(name, "", 0)
}
func (w *Walker) ImportFrom(fromPath, fromDir string, mode types.ImportMode) (*types.Package, error) {
name := fromPath
if canonical, ok := w.importMap[fromDir][fromPath]; ok {
name = canonical
}
pkg := w.imported[name]
if pkg != nil {
if pkg == &importing {
@ -449,7 +516,10 @@ func (w *Walker) Import(name string) (*types.Package, error) {
w.imported[name] = &importing
// Determine package files.
dir := filepath.Join(w.root, filepath.FromSlash(name))
dir := w.importDir[name]
if dir == "" {
dir = filepath.Join(w.root, filepath.FromSlash(name))
}
if fi, err := os.Stat(dir); err != nil || !fi.IsDir() {
log.Fatalf("no source in tree for import %q: %v", name, err)
}