blob: d306e820806b621cc89e7038c53daedc7bae6833 [file] [log] [blame]
// 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
}