| // Copyright 2015 The Vanadium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package main |
| |
| import ( |
| "fmt" |
| "go/build" |
| "io" |
| "sort" |
| "strings" |
| |
| "v.io/jiri" |
| "v.io/x/devtools/internal/goutil" |
| "v.io/x/lib/cmdline" |
| "v.io/x/lib/set" |
| ) |
| |
| var ( |
| pseudoPackageC = &build.Package{ImportPath: "C", Goroot: true} |
| pseudoPackageUnsafe = &build.Package{ImportPath: "unsafe", Goroot: true} |
| pkgCache = map[string]*build.Package{"C": pseudoPackageC, "unsafe": pseudoPackageUnsafe} |
| ) |
| |
| func isPseudoPackage(p *build.Package) bool { |
| return p == pseudoPackageUnsafe || p == pseudoPackageC |
| } |
| |
| func listPackagePaths(env *cmdline.Env, args ...string) ([]string, error) { |
| jirix, err := jiri.NewX(env) |
| if err != nil { |
| return nil, err |
| } |
| return goutil.List(jirix, []string{"--merge-policies=" + mergePoliciesFlag.String()}, args...) |
| } |
| |
| // importPackage loads and returns the package with the given package path. |
| func importPackage(path string) (*build.Package, error) { |
| if p, ok := pkgCache[path]; ok { |
| return p, nil |
| } |
| p, err := build.Import(path, "", build.AllowBinary) |
| if err != nil { |
| return nil, err |
| } |
| pkgCache[path] = p |
| return p, nil |
| } |
| |
| // depOpts holds options for computing package dependencies. |
| type depOpts struct { |
| DirectOnly bool // Only compute direct (rather than transitive) deps. |
| IncludeGoroot bool // Include $GOROOT packages. |
| IncludeTest bool // Also include TestImports |
| IncludeXTest bool // Also include TestImports and XTestImports. |
| } |
| |
| // Paths returns the initial package paths to use when computing dependencies. |
| func (x depOpts) Paths(pkg *build.Package) []string { |
| uniq := map[string]struct{}{} |
| set.String.Union(uniq, set.String.FromSlice(pkg.Imports)) |
| if x.IncludeTest || x.IncludeXTest { |
| set.String.Union(uniq, set.String.FromSlice(pkg.TestImports)) |
| } |
| if x.IncludeXTest { |
| set.String.Union(uniq, set.String.FromSlice(pkg.XTestImports)) |
| } |
| // Don't include the package itself; it was added by XTestImports. |
| delete(uniq, pkg.ImportPath) |
| paths := set.String.ToSlice(uniq) |
| sort.Strings(paths) |
| return paths |
| } |
| |
| // PrintIdent prints pkg and its dependencies to w, with fancy indentation. |
| func (x depOpts) PrintIndent(w io.Writer, pkg *build.Package) error { |
| fmt.Fprintln(w, "#"+pkg.ImportPath) |
| return x.printIndentHelper(w, x.Paths(pkg), 0) |
| } |
| |
| func (x depOpts) printIndentHelper(w io.Writer, paths []string, depth int) error { |
| for _, path := range paths { |
| pkg, err := importPackage(path) |
| if err != nil { |
| return err |
| } |
| if !x.IncludeGoroot && pkg.Goroot { |
| continue |
| } |
| fmt.Fprintln(w, strings.Repeat(" │", depth)+" ├─"+pkg.ImportPath) |
| if !x.DirectOnly { |
| if err := x.printIndentHelper(w, pkg.Imports, depth+1); err != nil { |
| return err |
| } |
| } |
| } |
| return nil |
| } |
| |
| // Deps fills deps with the dependencies of pkg. If directOnly is true, only |
| // direct dependencies are printed, not transitive dependencies. |
| func (x depOpts) Deps(pkg *build.Package, deps map[string]*build.Package) error { |
| return x.depsHelper(x.Paths(pkg), deps) |
| } |
| |
| func (x depOpts) depsHelper(paths []string, deps map[string]*build.Package) error { |
| for _, path := range paths { |
| if deps[path] != nil { |
| continue |
| } |
| pkg, err := importPackage(path) |
| if err != nil { |
| return err |
| } |
| if !x.IncludeGoroot && pkg.Goroot { |
| continue |
| } |
| deps[path] = pkg |
| if !x.DirectOnly { |
| if err := x.depsHelper(pkg.Imports, deps); err != nil { |
| return err |
| } |
| } |
| } |
| return nil |
| } |
| |
| func hasOverlap(a, b map[string]*build.Package) bool { |
| if len(a) > len(b) { |
| a, b = b, a |
| } |
| for key, _ := range a { |
| if b[key] != nil { |
| return true |
| } |
| } |
| return false |
| } |
| |
| type pkgSorter []*build.Package |
| |
| func (s pkgSorter) Len() int { return len(s) } |
| func (s pkgSorter) Less(i, j int) bool { return s[i].ImportPath < s[j].ImportPath } |
| func (s pkgSorter) Swap(i, j int) { s[i], s[j] = s[j], s[i] } |
| |
| // sortPackages returns the packages in pkgs, sorting all GOROOT packages first, |
| // followed by sorted non-GOROOT packages. |
| func sortPackages(pkgs map[string]*build.Package) []*build.Package { |
| var roots, nonroots pkgSorter |
| for _, pkg := range pkgs { |
| if pkg.Goroot { |
| roots = append(roots, pkg) |
| } else { |
| nonroots = append(nonroots, pkg) |
| } |
| } |
| sort.Sort(roots) |
| sort.Sort(nonroots) |
| var result []*build.Package |
| result = append(result, roots...) |
| result = append(result, nonroots...) |
| return result |
| } |