summaryrefslogtreecommitdiffstats
path: root/dependencies/pkg/mod/golang.org/x/exp@v0.0.0-20220613132600-b0d781184e0d/cmd/modgraphviz
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dependencies/pkg/mod/golang.org/x/exp@v0.0.0-20220613132600-b0d781184e0d/cmd/modgraphviz/main.go166
-rw-r--r--dependencies/pkg/mod/golang.org/x/exp@v0.0.0-20220613132600-b0d781184e0d/cmd/modgraphviz/main_test.go116
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)
+ }
+ })
+ }
+}