]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/api: use 'go list' to locate transitive dependencies of std
authorBryan C. Mills <bcmills@google.com>
Tue, 5 Mar 2019 21:52:48 +0000 (16:52 -0500)
committerBryan C. Mills <bcmills@google.com>
Fri, 8 Mar 2019 15:14:27 +0000 (15:14 +0000)
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>
src/cmd/api/goapi.go

index 60359229dec7f39f0ec6195db988ada10b7c9f23..1a0242f60c6df5cdf77588899b5647830183267d 100644 (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
@@ -349,12 +351,14 @@ func fileFeatures(filename string) []string {
 var fset = token.NewFileSet()
 
 type Walker struct {
-       context  *build.Context
-       root     string
-       scope    []string
-       current  *types.Package
-       features map[string]bool           // set
-       imported map[string]*types.Package // packages already imported
+       context   *build.Context
+       root      string
+       scope     []string
+       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)
        }