diff options
Diffstat (limited to '')
2 files changed, 282 insertions, 0 deletions
diff --git a/dependencies/pkg/mod/golang.org/x/exp@v0.0.0-20220613132600-b0d781184e0d/cmd/modgraphviz/main.go b/dependencies/pkg/mod/golang.org/x/exp@v0.0.0-20220613132600-b0d781184e0d/cmd/modgraphviz/main.go new file mode 100644 index 0000000..a0351dc --- /dev/null +++ b/dependencies/pkg/mod/golang.org/x/exp@v0.0.0-20220613132600-b0d781184e0d/cmd/modgraphviz/main.go @@ -0,0 +1,166 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Modgraphviz converts “go mod graph” output into Graphviz's DOT language, +// for use with Graphviz visualization and analysis tools like dot, dotty, and sccmap. +// +// Usage: +// +// go mod graph | modgraphviz > graph.dot +// go mod graph | modgraphviz | dot -Tpng -o graph.png +// +// Modgraphviz takes no options or arguments; it reads a graph in the format +// generated by “go mod graph” on standard input and writes DOT language +// on standard output. +// +// For each module, the node representing the greatest version (i.e., the +// version chosen by Go's minimal version selection algorithm) is colored green. +// Other nodes, which aren't in the final build list, are colored grey. +// +// See http://www.graphviz.org/doc/info/lang.html for details of the DOT language +// and http://www.graphviz.org/about/ for Graphviz itself. +// +// See also golang.org/x/tools/cmd/digraph for general queries and analysis +// of “go mod graph” output. +package main + +import ( + "bufio" + "bytes" + "flag" + "fmt" + "io" + "log" + "os" + "sort" + "strings" + + "golang.org/x/mod/semver" +) + +func usage() { + fmt.Fprintf(os.Stderr, `Usage: go mod graph | modgraphviz | dot -Tpng -o graph.png + +For each module, the node representing the greatest version (i.e., the +version chosen by Go's minimal version selection algorithm) is colored green. +Other nodes, which aren't in the final build list, are colored grey. +`) + os.Exit(2) +} + +func main() { + log.SetFlags(0) + log.SetPrefix("modgraphviz: ") + + flag.Usage = usage + flag.Parse() + if flag.NArg() != 0 { + usage() + } + + if err := modgraphviz(os.Stdin, os.Stdout); err != nil { + log.Fatal(err) + } +} + +func modgraphviz(in io.Reader, out io.Writer) error { + graph, err := convert(in) + if err != nil { + return err + } + + fmt.Fprintf(out, "digraph gomodgraph {\n") + fmt.Fprintf(out, "\tnode [ shape=rectangle fontsize=12 ]\n") + out.Write(graph.edgesAsDOT()) + for _, n := range graph.mvsPicked { + fmt.Fprintf(out, "\t%q [style = filled, fillcolor = green]\n", n) + } + for _, n := range graph.mvsUnpicked { + fmt.Fprintf(out, "\t%q [style = filled, fillcolor = gray]\n", n) + } + fmt.Fprintf(out, "}\n") + + return nil +} + +type edge struct{ from, to string } +type graph struct { + edges []edge + mvsPicked []string + mvsUnpicked []string +} + +// convert reads “go mod graph” output from r and returns a graph, recording +// MVS picked and unpicked nodes along the way. +func convert(r io.Reader) (*graph, error) { + scanner := bufio.NewScanner(r) + var g graph + seen := map[string]bool{} + mvsPicked := map[string]string{} // module name -> module version + + for scanner.Scan() { + l := scanner.Text() + if l == "" { + continue + } + parts := strings.Fields(l) + if len(parts) != 2 { + return nil, fmt.Errorf("expected 2 words in line, but got %d: %s", len(parts), l) + } + from := parts[0] + to := parts[1] + g.edges = append(g.edges, edge{from: from, to: to}) + + for _, node := range []string{from, to} { + if _, ok := seen[node]; ok { + // Skip over nodes we've already seen. + continue + } + seen[node] = true + + var m, v string + if i := strings.IndexByte(node, '@'); i >= 0 { + m, v = node[:i], node[i+1:] + } else { + // Root node doesn't have a version. + continue + } + + if maxV, ok := mvsPicked[m]; ok { + if semver.Compare(maxV, v) < 0 { + // This version is higher - replace it and consign the old + // max to the unpicked list. + g.mvsUnpicked = append(g.mvsUnpicked, m+"@"+maxV) + mvsPicked[m] = v + } else { + // Other version is higher - stick this version in the + // unpicked list. + g.mvsUnpicked = append(g.mvsUnpicked, node) + } + } else { + mvsPicked[m] = v + } + } + } + if err := scanner.Err(); err != nil { + return nil, err + } + + for m, v := range mvsPicked { + g.mvsPicked = append(g.mvsPicked, m+"@"+v) + } + + // Make this function deterministic. + sort.Strings(g.mvsPicked) + return &g, nil +} + +// edgesAsDOT returns the edges in DOT notation. +func (g *graph) edgesAsDOT() []byte { + var buf bytes.Buffer + for _, e := range g.edges { + fmt.Fprintf(&buf, "\t%q -> %q\n", e.from, e.to) + } + return buf.Bytes() +} diff --git a/dependencies/pkg/mod/golang.org/x/exp@v0.0.0-20220613132600-b0d781184e0d/cmd/modgraphviz/main_test.go b/dependencies/pkg/mod/golang.org/x/exp@v0.0.0-20220613132600-b0d781184e0d/cmd/modgraphviz/main_test.go new file mode 100644 index 0000000..bec88cf --- /dev/null +++ b/dependencies/pkg/mod/golang.org/x/exp@v0.0.0-20220613132600-b0d781184e0d/cmd/modgraphviz/main_test.go @@ -0,0 +1,116 @@ +// Copyright 2019 The Go 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 ( + "bytes" + "fmt" + "reflect" + "testing" +) + +func TestRun(t *testing.T) { + out := &bytes.Buffer{} + in := bytes.NewBuffer([]byte(` +test.com/A@v1.0.0 test.com/B@v1.2.3 +test.com/B@v1.0.0 test.com/C@v4.5.6 +`)) + if err := modgraphviz(in, out); err != nil { + t.Fatal(err) + } + + gotGraph := string(out.Bytes()) + wantGraph := `digraph gomodgraph { + node [ shape=rectangle fontsize=12 ] + "test.com/A@v1.0.0" -> "test.com/B@v1.2.3" + "test.com/B@v1.0.0" -> "test.com/C@v4.5.6" + "test.com/A@v1.0.0" [style = filled, fillcolor = green] + "test.com/B@v1.2.3" [style = filled, fillcolor = green] + "test.com/C@v4.5.6" [style = filled, fillcolor = green] + "test.com/B@v1.0.0" [style = filled, fillcolor = gray] +} +` + if gotGraph != wantGraph { + t.Fatalf("\ngot: %s\nwant: %s", gotGraph, wantGraph) + } +} + +func TestMVSPicking(t *testing.T) { + for _, tc := range []struct { + name string + in []string + wantPicked []string + wantUnpicked []string + }{ + { + name: "single node", + in: []string{"foo@v0.0.1"}, + wantPicked: []string{"foo@v0.0.1"}, + wantUnpicked: nil, + }, + { + name: "duplicate same node", + in: []string{"foo@v0.0.1", "foo@v0.0.1"}, + wantPicked: []string{"foo@v0.0.1"}, + wantUnpicked: nil, + }, + { + name: "multiple semver - same major", + in: []string{"foo@v1.0.0", "foo@v1.3.7", "foo@v1.2.0", "foo@v1.0.1"}, + wantPicked: []string{"foo@v1.3.7"}, + wantUnpicked: []string{"foo@v1.0.0", "foo@v1.2.0", "foo@v1.0.1"}, + }, + { + name: "multiple semver - multiple major", + in: []string{"foo@v1.0.0", "foo@v1.3.7", "foo/v2@v2.2.0", "foo/v2@v2.0.1", "foo@v1.1.1"}, + wantPicked: []string{"foo/v2@v2.2.0", "foo@v1.3.7"}, + wantUnpicked: []string{"foo@v1.0.0", "foo/v2@v2.0.1", "foo@v1.1.1"}, + }, + { + name: "semver and pseudo version", + in: []string{"foo@v1.0.0", "foo@v1.3.7", "foo/v2@v2.2.0", "foo/v2@v2.0.1", "foo@v1.1.1", "foo@v0.0.0-20190311183353-d8887717615a"}, + wantPicked: []string{"foo/v2@v2.2.0", "foo@v1.3.7"}, + wantUnpicked: []string{"foo@v1.0.0", "foo/v2@v2.0.1", "foo@v1.1.1", "foo@v0.0.0-20190311183353-d8887717615a"}, + }, + { + name: "multiple pseudo version", + in: []string{ + "foo@v0.0.0-20190311183353-d8887717615a", + "foo@v0.0.0-20190227222117-0694c2d4d067", + "foo@v0.0.0-20190312151545-0bb0c0a6e846", + }, + wantPicked: []string{"foo@v0.0.0-20190312151545-0bb0c0a6e846"}, + wantUnpicked: []string{ + "foo@v0.0.0-20190227222117-0694c2d4d067", + "foo@v0.0.0-20190311183353-d8887717615a", + }, + }, + { + name: "semver and suffix", + in: []string{"foo@v1.0.0", "foo@v1.3.8-rc1", "foo@v1.3.7"}, + wantPicked: []string{"foo@v1.3.8-rc1"}, + wantUnpicked: []string{"foo@v1.0.0", "foo@v1.3.7"}, + }, + } { + t.Run(tc.name, func(t *testing.T) { + buf := bytes.Buffer{} + for _, node := range tc.in { + fmt.Fprintf(&buf, "A %s\n", node) + } + + g, err := convert(&buf) + if err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(g.mvsPicked, tc.wantPicked) { + t.Fatalf("picked: got %v, want %v", g.mvsPicked, tc.wantPicked) + } + if !reflect.DeepEqual(g.mvsUnpicked, tc.wantUnpicked) { + t.Fatalf("unpicked: got %v, want %v", g.mvsUnpicked, tc.wantUnpicked) + } + }) + } +} |