Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions cache_importer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package depth

import (
"go/build"
"sync"
)

type CachingImporter struct {
mu sync.Mutex
cache map[string]*build.Package
}

func NewCachingImporter() *CachingImporter {
return &CachingImporter{
cache: make(map[string]*build.Package),
}
}

func (c *CachingImporter) Import(path, srcDir string, mode build.ImportMode) (*build.Package, error) {
c.mu.Lock()
defer c.mu.Unlock()

if pkg, ok := c.cache[path]; ok {
return pkg, nil
}
pkg, err := build.Default.Import(path, srcDir, mode)
if err == nil {
if existingPkg, ok := c.cache[path]; ok {
return existingPkg, nil
}
c.cache[path] = pkg
}
return pkg, err
}
89 changes: 63 additions & 26 deletions cmd/depth/depth.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,72 +7,105 @@ import (
"io"
"os"
"strings"
"sync"
"time"

"github.com/KyleBanks/depth"
"github.com/adapap/depth"
)

const (
outputClosedPadding = " "
outputOpenPadding = " "
outputOpenPadding = " "
outputPrefix = "├ "
outputPrefixLast = "└ "
)

var outputJSON bool
var explainPkg string

type summary struct {
numInternal int
numExternal int
numTesting int
maxDepth int
}

func main() {
t, pkgs := parse(os.Args[1:])
if err := handlePkgs(t, pkgs, outputJSON, explainPkg); err != nil {
os.Exit(1)
if len(os.Args) < 2 {
fmt.Println("Usage: depth [options] <packages>")
return
}

t, options := parse(os.Args[1:])
if err := handlePkgs(t, options); err != nil {
return
}
}

// parse constructs a depth.Tree from command-line arguments, and returns the
// remaining user-supplied package names
func parse(args []string) (*depth.Tree, []string) {
func parse(args []string) (*depth.Tree, *depth.Options) {
f := flag.NewFlagSet(os.Args[0], flag.ExitOnError)

var t depth.Tree
// constructs a depth.Tree from command-line arguments, and returns the
// remaining user-supplied package names
t := &depth.Tree{
Mutex: sync.Mutex{},
}
var options depth.Options

var includePattern string
var excludePattern string

// Import options.
f.BoolVar(&t.ResolveInternal, "internal", false, "If set, resolves dependencies of internal (stdlib) packages.")
f.BoolVar(&t.ResolveTest, "test", false, "If set, resolves dependencies used for testing.")
f.IntVar(&t.MaxDepth, "max", 0, "Sets the maximum depth of dependencies to resolve.")
f.BoolVar(&outputJSON, "json", false, "If set, outputs the depencies in JSON format.")
f.StringVar(&explainPkg, "explain", "", "If set, show which packages import the specified target")
f.Parse(args)
f.StringVar(&includePattern, "include", "", "If set, use the given pattern(s) as a prefix filter of package names to keep.")
f.StringVar(&excludePattern, "exclude", "", "If set, use the given pattern(s) as a prefix filter of package names to ignore.")
f.BoolVar(&t.Verbose, "verbose", false, "If set, print verbose output.")

// Output options.
f.BoolVar(&options.OutputJSON, "json", false, "If set, outputs the depencies in JSON format.")
f.StringVar(&options.ExplainPkg, "explain", "", "If set, show which packages import the specified target")

_ = f.Parse(args)

if includePattern != "" {
t.IncludePatterns = strings.Split(includePattern, ",")
}
if excludePattern != "" {
t.ExcludePatterns = strings.Split(excludePattern, ",")
}

return &t, f.Args()
options.PackageNames = f.Args()

return t, &options
}

// handlePkgs takes a slice of package names, resolves a Tree on them,
// and outputs each Tree to Stdout.
func handlePkgs(t *depth.Tree, pkgs []string, outputJSON bool, explainPkg string) error {
for _, pkg := range pkgs {
func handlePkgs(t *depth.Tree, options *depth.Options) error {
for _, pkg := range options.PackageNames {

start := time.Now()
err := t.Resolve(pkg)
if err != nil {
fmt.Printf("'%v': FATAL: %v\n", pkg, err)
return err
}
elapsed := time.Since(start)

if outputJSON {
writePkgJSON(os.Stdout, *t.Root)
if options.OutputJSON {
if err := writePkgJSON(os.Stdout, *t.Root); err != nil {
return err
}
continue
}

if explainPkg != "" {
writeExplain(os.Stdout, *t.Root, []string{}, explainPkg)
if options.ExplainPkg != "" {
writeExplain(os.Stdout, *t.Root, []string{}, options.ExplainPkg)
continue
}

writePkg(os.Stdout, *t.Root)
writePkgSummary(os.Stdout, *t.Root)
fmt.Printf("Resolved <%s> in %s\n", pkg, elapsed)
}
return nil
}
Expand All @@ -84,11 +117,12 @@ func writePkgSummary(w io.Writer, pkg depth.Pkg) {
for _, p := range pkg.Deps {
collectSummary(&sum, p, set)
}
fmt.Fprintf(w, "%d dependencies (%d internal, %d external, %d testing).\n",
fmt.Fprintf(w, "%d dependencies (%d internal, %d external, %d testing) | max depth: %d\n",
sum.numInternal+sum.numExternal,
sum.numInternal,
sum.numExternal,
sum.numTesting)
sum.numTesting,
sum.maxDepth)
}

func collectSummary(sum *summary, pkg depth.Pkg, nameSet map[string]struct{}) {
Expand All @@ -102,17 +136,20 @@ func collectSummary(sum *summary, pkg depth.Pkg, nameSet map[string]struct{}) {
if pkg.Test {
sum.numTesting++
}
if pkg.Depth > sum.maxDepth {
sum.maxDepth = pkg.Depth
}
for _, p := range pkg.Deps {
collectSummary(sum, p, nameSet)
}
}
}

// writePkgJSON writes the full Pkg as JSON to the provided Writer.
func writePkgJSON(w io.Writer, p depth.Pkg) {
func writePkgJSON(w io.Writer, p depth.Pkg) error {
e := json.NewEncoder(w)
e.SetIndent("", " ")
e.Encode(p)
return e.Encode(p)
}

func writePkg(w io.Writer, p depth.Pkg) {
Expand Down
90 changes: 51 additions & 39 deletions cmd/depth/depth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import (
"fmt"
"testing"

"github.com/KyleBanks/depth"
"github.com/adapap/depth"
"github.com/stretchr/testify/assert"
)

func Test_parse(t *testing.T) {
Expand All @@ -19,62 +20,61 @@ func Test_parse(t *testing.T) {
{false, false, 10, false, ""},
{true, false, 10, false, ""},
{false, true, 5, true, ""},
{false, true, 5, true, "github.com/KyleBanks/depth"},
{false, true, 5, true, "github.com/adapap/depth"},
{false, true, 5, true, ""},
}

for idx, tt := range tests {
tr, _ := parse([]string{
fmt.Sprintf("-internal=%v", tt.internal),
fmt.Sprintf("-test=%v", tt.test),
fmt.Sprintf("-max=%v", tt.depth),
fmt.Sprintf("-json=%v", tt.json),
fmt.Sprintf("-explain=%v", tt.explain),
for _, tc := range tests {
tr, options := parse([]string{
fmt.Sprintf("-internal=%v", tc.internal),
fmt.Sprintf("-test=%v", tc.test),
fmt.Sprintf("-max=%v", tc.depth),
fmt.Sprintf("-json=%v", tc.json),
fmt.Sprintf("-explain=%v", tc.explain),
})

if tr.ResolveInternal != tt.internal {
t.Fatalf("[%v] Unexpected ResolveInternal, expected=%v, got=%v", idx, tt.internal, tr.ResolveInternal)
} else if tr.ResolveTest != tt.test {
t.Fatalf("[%v] Unexpected ResolveTest, expected=%v, got=%v", idx, tt.test, tr.ResolveTest)
} else if tr.MaxDepth != tt.depth {
t.Fatalf("[%v] Unexpected MaxDepth, expected=%v, got=%v", idx, tt.depth, tr.MaxDepth)
} else if outputJSON != tt.json {
t.Fatalf("[%v] Unexpected outputJSON, expected=%v, got=%v", idx, tt.json, outputJSON)
} else if explainPkg != tt.explain {
t.Fatalf("[%v] Unexpected explainPkg, expected=%v, got=%v", idx, tt.explain, explainPkg)
}
assert.Equal(t, tc.internal, tr.ResolveInternal)
assert.Equal(t, tc.test, tr.ResolveTest)
assert.Equal(t, tc.depth, tr.MaxDepth)
assert.Equal(t, tc.json, options.OutputJSON)
assert.Equal(t, tc.explain, options.ExplainPkg)
}
}

func Example_handlePkgsStrings() {
var t depth.Tree
var tree depth.Tree

handlePkgs(&t, []string{"strings"}, false, "")
_ = handlePkgs(&tree, &depth.Options{PackageNames: []string{"strings"}})
// Output:
// strings
// ├ errors
// ├ internal/abi
// ├ internal/bytealg
// ├ internal/stringslite
// ├ io
// ├ sync
// ├ unicode
// ├ unicode/utf8
// └ unsafe
// 7 dependencies (7 internal, 0 external, 0 testing).
// 9 dependencies (9 internal, 0 external, 0 testing).
}

func Example_handlePkgsTestStrings() {
var t depth.Tree
t.ResolveTest = true
var tree depth.Tree
tree.ResolveTest = true

handlePkgs(&t, []string{"strings"}, false, "")
_ = handlePkgs(&tree, &depth.Options{PackageNames: []string{"strings"}})
// Output:
// strings
// ├ bytes
// ├ errors
// ├ fmt
// ├ internal/abi
// ├ internal/bytealg
// ├ internal/stringslite
// ├ internal/testenv
// ├ io
// ├ math
// ├ math/rand
// ├ reflect
// ├ strconv
Expand All @@ -83,22 +83,22 @@ func Example_handlePkgsTestStrings() {
// ├ unicode
// ├ unicode/utf8
// └ unsafe
// 14 dependencies (14 internal, 0 external, 7 testing).
// 17 dependencies (17 internal, 0 external, 8 testing).
}

func Example_handlePkgsDepth() {
var t depth.Tree
var tree depth.Tree

handlePkgs(&t, []string{"github.com/KyleBanks/depth/cmd/depth"}, false, "")
_ = handlePkgs(&tree, &depth.Options{PackageNames: []string{"github.com/adapap/depth/cmd/depth"}})
// Output:
// github.com/KyleBanks/depth/cmd/depth
// github.com/adapap/depth/cmd/depth
// ├ encoding/json
// ├ flag
// ├ fmt
// ├ io
// ├ os
// ├ strings
// └ github.com/KyleBanks/depth
// └ github.com/adapap/depth
// ├ bytes
// ├ errors
// ├ go/build
Expand All @@ -110,16 +110,16 @@ func Example_handlePkgsDepth() {
}

func Example_handlePkgsUnknown() {
var t depth.Tree
var tree depth.Tree

handlePkgs(&t, []string{"notreal"}, false, "")
_ = handlePkgs(&tree, &depth.Options{PackageNames: []string{"notreal"}})
// Output:
// 'notreal': FATAL: unable to resolve root package
}

func Example_handlePkgsJson() {
var t depth.Tree
handlePkgs(&t, []string{"strings"}, true, "")
var tree depth.Tree
_ = handlePkgs(&tree, &depth.Options{PackageNames: []string{"strings"}, OutputJSON: true})

// Output:
// {
Expand All @@ -134,12 +134,24 @@ func Example_handlePkgsJson() {
// "deps": null
// },
// {
// "name": "internal/abi",
// "internal": true,
// "resolved": true,
// "deps": null
// },
// {
// "name": "internal/bytealg",
// "internal": true,
// "resolved": true,
// "deps": null
// },
// {
// "name": "internal/stringslite",
// "internal": true,
// "resolved": true,
// "deps": null
// },
// {
// "name": "io",
// "internal": true,
// "resolved": true,
Expand Down Expand Up @@ -175,10 +187,10 @@ func Example_handlePkgsJson() {
}

func Example_handlePkgsExplain() {
var t depth.Tree
var tree depth.Tree

handlePkgs(&t, []string{"github.com/KyleBanks/depth/cmd/depth"}, false, "strings")
_ = handlePkgs(&tree, &depth.Options{PackageNames: []string{"github.com/adapap/depth/cmd/depth"}, ExplainPkg: "strings"})
// Output:
// github.com/KyleBanks/depth/cmd/depth -> strings
// github.com/KyleBanks/depth/cmd/depth -> github.com/KyleBanks/depth -> strings
// github.com/adapap/depth/cmd/depth -> strings
// github.com/adapap/depth/cmd/depth -> github.com/adapap/depth -> strings
}
Loading