github.com/rsc/tmp@v0.0.0-20240517235954-6deaab19748b/diffmod/diffmod.go (about) 1 // Copyright 2018 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Diffmod identifies differences in the dependencies 6 // implied by each of a set of go.mod files. 7 // 8 // Usage: 9 // 10 // diffmod go.mod other/go.mod ... 11 // 12 // If the version of any dependency used by one go.mod file 13 // is different from the version used by another go.mod file 14 // (ignoring those that don't use the dependency at all), 15 // then diffmod prints a stanza of the form: 16 // 17 // module/path 18 // go.mod: version used in go.mod 19 // other/go.mod: version used in other/go.mod 20 // ... 21 // 22 // Diffmod prints one stanza for each dependency that differs 23 // across the set of go.mod files. 24 // 25 package main 26 27 import ( 28 "bytes" 29 "encoding/json" 30 "flag" 31 "fmt" 32 "io" 33 "log" 34 "os" 35 "os/exec" 36 "path/filepath" 37 "sort" 38 ) 39 40 func usage() { 41 fmt.Fprintf(os.Stderr, "usage: diffmod go.mod other/go.mod...\n") 42 os.Exit(2) 43 } 44 45 var deps = make(map[string]map[string]string) // go.mod -> module path -> Module 46 47 type Module struct { 48 Path string 49 Main bool 50 Version string 51 Replace Replacement 52 } 53 54 func (m Module) VersionString() string { 55 var s string 56 if m.Version != "" { 57 s += " " + m.Version 58 } 59 if m.Replace.Dir != "" { 60 s += " => " + m.Replace.Dir 61 } else if m.Replace.Path != "" { 62 s += " => " + m.Replace.Path + " " + m.Replace.Version 63 } 64 if s == "" { 65 return "" 66 } 67 return s[1:] 68 } 69 70 type Replacement struct { 71 Path string 72 Version string 73 Dir string 74 } 75 76 func main() { 77 log.SetPrefix("syncmod: ") 78 log.SetFlags(0) 79 flag.Usage = usage 80 flag.Parse() 81 gomods := flag.Args() 82 if len(gomods) < 2 { 83 usage() 84 } 85 86 havePath := make(map[string]bool) 87 var paths []string 88 89 for _, gomod := range flag.Args() { 90 if filepath.Base(gomod) != "go.mod" { 91 log.Fatalf("not a go.mod: %s", gomod) 92 } 93 deps[gomod] = make(map[string]string) 94 cmd := exec.Command("vgo", "list", "-m", "-json", "all") 95 cmd.Dir = filepath.Dir(gomod) 96 out, err := cmd.Output() 97 if err != nil { 98 log.Fatal(err) 99 } 100 dec := json.NewDecoder(bytes.NewReader(out)) 101 for { 102 var m Module 103 err := dec.Decode(&m) 104 if err == io.EOF { 105 break 106 } 107 if err != nil { 108 log.Fatalf("%s: parsing json: %v", gomod, err) 109 } 110 if m.Main { 111 continue 112 } 113 if !havePath[m.Path] { 114 havePath[m.Path] = true 115 paths = append(paths, m.Path) 116 } 117 deps[gomod][m.Path] = m.VersionString() 118 } 119 } 120 121 sort.Strings(paths) 122 for _, path := range paths { 123 ok := true 124 var m1 string 125 for _, gomod := range gomods { 126 m := deps[gomod][path] 127 if m1 == "" { 128 m1 = m 129 } 130 if m != "" && m != m1 { 131 ok = false 132 } 133 } 134 if ok { 135 continue 136 } 137 fmt.Printf("%s\n", path) 138 for _, gomod := range gomods { 139 m := deps[gomod][path] 140 if m != "" { 141 fmt.Printf("\t%s: %s\n", gomod, m) 142 } 143 } 144 } 145 }