Skip to content

Commit

Permalink
graph: use pkggraph
Browse files Browse the repository at this point in the history
  • Loading branch information
egonelbre committed Dec 28, 2020
1 parent 0d4b6bf commit 2c5cce8
Show file tree
Hide file tree
Showing 6 changed files with 219 additions and 320 deletions.
2 changes: 1 addition & 1 deletion exec/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func (*Command) Synopsis() string { return "Run command with extended statistics
func (*Command) Usage() string {
return `calc <command>:
Run command with extended statistics.
Example:
go build -toolexec "goda exec" .
Expand Down
430 changes: 160 additions & 270 deletions graph.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
56 changes: 25 additions & 31 deletions graph/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import (
"text/template"

"github.com/google/subcommands"
"golang.org/x/tools/go/packages"

"github.com/loov/goda/pkggraph"
"github.com/loov/goda/pkgset"
"github.com/loov/goda/templates"
)
Expand Down Expand Up @@ -49,7 +49,7 @@ func (cmd *Command) SetFlags(f *flag.FlagSet) {
f.StringVar(&cmd.docs, "docs", "https://pkg.go.dev/", "override the docs url to use")

f.StringVar(&cmd.outputType, "type", "dot", "output type")
f.StringVar(&cmd.labelFormat, "f", "{{.ID}}\\l{{LineCount .}} / {{SourceSize .}}\\l", "label formatting")
f.StringVar(&cmd.labelFormat, "f", "{{.ID}}\\l{{ .Stat.GoFiles.Lines }} / {{ .Stat.GoFiles.Size }}\\l", "label formatting")

f.BoolVar(&cmd.clusters, "cluster", false, "create clusters")
f.BoolVar(&cmd.shortID, "short", false, "use short package id-s")
Expand Down Expand Up @@ -90,11 +90,11 @@ func (cmd *Command) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface
label: label,
}

pkgs := result.Sorted()
graph := pkggraph.FromSet(result)
if cmd.clusters {
dot.WriteClusters(result, pkgs)
dot.WriteClusters(graph)
} else {
dot.WriteRegular(result, pkgs)
dot.WriteRegular(graph)
}

return subcommands.ExitSuccess
Expand All @@ -111,7 +111,7 @@ type Dot struct {
label *template.Template
}

func (ctx *Dot) Label(p *packages.Package) string {
func (ctx *Dot) Label(p *pkggraph.Node) string {
var labelText strings.Builder
err := ctx.label.Execute(&labelText, p)
if err != nil {
Expand All @@ -120,7 +120,7 @@ func (ctx *Dot) Label(p *packages.Package) string {
return labelText.String()
}

func (ctx *Dot) ClusterLabel(tree *pkgset.Tree, parentPrinted bool) string {
func (ctx *Dot) ClusterLabel(tree *pkggraph.Tree, parentPrinted bool) string {
var suffix = ""
if parentPrinted && tree.Parent != nil && tree.Parent.Path != "" {
suffix = "./" + strings.TrimPrefix(tree.Path, tree.Parent.Path+"/")
Expand All @@ -132,7 +132,7 @@ func (ctx *Dot) ClusterLabel(tree *pkgset.Tree, parentPrinted bool) string {
return tree.Path
}

func (ctx *Dot) TreeLabel(tree *pkgset.Tree, parentPrinted bool) string {
func (ctx *Dot) TreeLabel(tree *pkggraph.Tree, parentPrinted bool) string {
var suffix = ""
if parentPrinted && tree.Parent != nil && tree.Parent.Path != "" {
suffix = strings.TrimPrefix(tree.Path, tree.Parent.Path+"/")
Expand All @@ -158,11 +158,11 @@ func (ctx *Dot) TreeLabel(tree *pkgset.Tree, parentPrinted bool) string {
return labelText.String()
}

func (ctx *Dot) Ref(p *packages.Package) string {
func (ctx *Dot) Ref(p *pkggraph.Node) string {
return fmt.Sprintf(`href=%q `, ctx.docs+p.ID)
}

func (ctx *Dot) TreeRef(tree *pkgset.Tree) string {
func (ctx *Dot) TreeRef(tree *pkggraph.Tree) string {
return fmt.Sprintf(`href=%q `, ctx.docs+tree.Path)
}

Expand All @@ -182,35 +182,33 @@ func (ctx *Dot) writeGraphProperties() {
fmt.Fprintf(ctx.out, " quantum=\"0.5\";\n")
}

func (ctx *Dot) WriteRegular(result pkgset.Set, pkgs []*packages.Package) {
func (ctx *Dot) WriteRegular(graph *pkggraph.Graph) {
fmt.Fprintf(ctx.out, "digraph G {\n")
ctx.writeGraphProperties()
defer fmt.Fprintf(ctx.out, "}\n")

for _, p := range pkgs {
fmt.Fprintf(ctx.out, " %v [label=\"%v\" %v %v];\n", pkgID(p), ctx.Label(p), ctx.Ref(p), ctx.colorOf(p))
for _, n := range graph.Sorted {
fmt.Fprintf(ctx.out, " %v [label=\"%v\" %v %v];\n", pkgID(n), ctx.Label(n), ctx.Ref(n), ctx.colorOf(n))
}

for _, src := range pkgs {
for _, dst := range src.Imports {
if _, ok := result[dst.ID]; ok {
fmt.Fprintf(ctx.out, " %v -> %v [%v];\n", pkgID(src), pkgID(dst), ctx.colorOf(dst))
}
for _, src := range graph.Sorted {
for _, dst := range src.DirectNodes {
fmt.Fprintf(ctx.out, " %v -> %v [%v];\n", pkgID(src), pkgID(dst), ctx.colorOf(dst))
}
}
}

func (ctx *Dot) WriteClusters(result pkgset.Set, pkgs []*packages.Package) {
func (ctx *Dot) WriteClusters(graph *pkggraph.Graph) {
fmt.Fprintf(ctx.out, "digraph G {\n")
ctx.writeGraphProperties()
defer fmt.Fprintf(ctx.out, "}\n")

var walk func(bool, *pkgset.Tree)
root := result.Tree()
var walk func(bool, *pkggraph.Tree)
root := graph.Tree()
lookup := root.LookupTable()
isCluster := map[*packages.Package]bool{}
isCluster := map[*pkggraph.Node]bool{}

walk = func(parentPrinted bool, tree *pkgset.Tree) {
walk = func(parentPrinted bool, tree *pkggraph.Tree) {
p := tree.Package
if len(tree.Children) == 0 {
label := ctx.TreeLabel(tree, parentPrinted)
Expand Down Expand Up @@ -256,13 +254,9 @@ func (ctx *Dot) WriteClusters(result pkgset.Set, pkgs []*packages.Package) {
}
walk(false, root)

for _, src := range pkgs {
for _, src := range graph.Sorted {
srctree := lookup[src]
for _, dst := range src.Imports {
if _, ok := result[dst.ID]; !ok {
continue
}

for _, dst := range src.DirectNodes {
dstid := pkgID(dst)
dsttree := lookup[dst]
tooltip := src.ID + " -> " + dst.ID
Expand All @@ -276,7 +270,7 @@ func (ctx *Dot) WriteClusters(result pkgset.Set, pkgs []*packages.Package) {
}
}

func (ctx *Dot) colorOf(p *packages.Package) string {
func (ctx *Dot) colorOf(p *pkggraph.Node) string {
if ctx.nocolor {
return ""
}
Expand All @@ -286,7 +280,7 @@ func (ctx *Dot) colorOf(p *packages.Package) string {
return "color=" + hslahex(hue, 0.9, 0.3, 0.7)
}

func pkgID(p *packages.Package) string {
func pkgID(p *pkggraph.Node) string {
return escapeID(p.ID)
}

Expand Down
17 changes: 17 additions & 0 deletions pkggraph/graph.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ type Graph struct {
func (g *Graph) AddNode(n *Node) { g.Packages[n.ID] = n }

type Node struct {
DirectNodes []*Node

ImportsNodes []*Node
ImportedByNodes []*Node

Expand Down Expand Up @@ -72,6 +74,21 @@ func From(pkgs map[string]*packages.Package) *Graph {
}

for _, n := range g.Packages {
for id := range n.Package.Imports {
direct, ok := g.Packages[id]
if !ok {
// TODO:
// should we include dependencies where Y is hidden?
// X -> [Y] -> Z
continue
}

n.DirectNodes = append(n.DirectNodes, direct)
}
}

for _, n := range g.Packages {
SortNodes(n.DirectNodes)
SortNodes(n.ImportsNodes)
SortNodes(n.ImportedByNodes)
}
Expand Down
24 changes: 16 additions & 8 deletions pkgset/tree.go → pkggraph/tree.go
Original file line number Diff line number Diff line change
@@ -1,23 +1,31 @@
package pkgset
package pkggraph

import (
"path"
"sort"
"strings"

"golang.org/x/tools/go/packages"
)

type Tree struct {
Path string
Package *packages.Package
Package *Node

Child map[string]*Tree

Parent *Tree
Children []*Tree
}

// Tree returns Node tree.
func (graph *Graph) Tree() *Tree {
tree := NewTree(nil, "")
for _, pkg := range graph.Sorted {
tree.Add(pkg)
}
tree.Sort()
return tree
}

func NewTree(parent *Tree, path string) *Tree {
return &Tree{
Path: path,
Expand All @@ -26,11 +34,11 @@ func NewTree(parent *Tree, path string) *Tree {
}
}

func (tree *Tree) Add(pkg *packages.Package) {
func (tree *Tree) Add(pkg *Node) {
tree.Insert([]string{}, strings.Split(pkg.PkgPath, "/"), pkg)
}

func (tree *Tree) Insert(prefix, suffix []string, pkg *packages.Package) {
func (tree *Tree) Insert(prefix, suffix []string, pkg *Node) {
if len(suffix) == 0 {
tree.Package = pkg
return
Expand Down Expand Up @@ -58,8 +66,8 @@ func (tree *Tree) HasParent(parent *Tree) bool {
return strings.HasPrefix(tree.Path, parent.Path+"/")
}

func (tree *Tree) LookupTable() map[*packages.Package]*Tree {
table := map[*packages.Package]*Tree{}
func (tree *Tree) LookupTable() map[*Node]*Tree {
table := map[*Node]*Tree{}
tree.Walk(func(x *Tree) {
if x.Package != nil {
table[x.Package] = x
Expand Down
10 changes: 0 additions & 10 deletions pkgset/set.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,6 @@ func (set Set) Sorted() []*packages.Package {
return list
}

// Tree returns package tree
func (set Set) Tree() *Tree {
tree := NewTree(nil, "")
for _, pkg := range set {
tree.Add(pkg)
}
tree.Sort()
return tree
}

func (set Set) Walk(fn func(*packages.Package)) {
for _, p := range set {
fn(p)
Expand Down

0 comments on commit 2c5cce8

Please sign in to comment.