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//go:build ignore 6// +build ignore 7 8// Addmod adds a module as a txtar archive to the testdata/mod directory. 9// 10// Usage: 11// 12// go run addmod.go path@version... 13// 14// It should only be used for very small modules - we do not want to check 15// very large files into testdata/mod. 16// 17// It is acceptable to edit the archive afterward to remove or shorten files. 18// See mod/README for more information. 19package main 20 21import ( 22 "bytes" 23 "cmd/go/internal/str" 24 "flag" 25 "fmt" 26 "internal/txtar" 27 "io/fs" 28 "log" 29 "os" 30 "os/exec" 31 "path/filepath" 32 "strings" 33) 34 35func usage() { 36 fmt.Fprintf(os.Stderr, "usage: go run addmod.go path@version...\n") 37 os.Exit(2) 38} 39 40var tmpdir string 41 42func fatalf(format string, args ...any) { 43 os.RemoveAll(tmpdir) 44 log.Fatalf(format, args...) 45} 46 47const goCmd = "go" 48 49func main() { 50 flag.Usage = usage 51 flag.Parse() 52 if flag.NArg() == 0 { 53 usage() 54 } 55 56 log.SetPrefix("addmod: ") 57 log.SetFlags(0) 58 59 var err error 60 tmpdir, err = os.MkdirTemp("", "addmod-") 61 if err != nil { 62 log.Fatal(err) 63 } 64 65 run := func(command string, args ...string) string { 66 cmd := exec.Command(command, args...) 67 cmd.Dir = tmpdir 68 var stderr bytes.Buffer 69 cmd.Stderr = &stderr 70 out, err := cmd.Output() 71 if err != nil { 72 fatalf("%s %s: %v\n%s", command, strings.Join(args, " "), err, stderr.Bytes()) 73 } 74 return string(out) 75 } 76 77 gopath := strings.TrimSpace(run("go", "env", "GOPATH")) 78 if gopath == "" { 79 fatalf("cannot find GOPATH") 80 } 81 82 exitCode := 0 83 for _, arg := range flag.Args() { 84 if err := os.WriteFile(filepath.Join(tmpdir, "go.mod"), []byte("module m\n"), 0666); err != nil { 85 fatalf("%v", err) 86 } 87 run(goCmd, "get", "-d", arg) 88 path := arg 89 if i := strings.Index(path, "@"); i >= 0 { 90 path = path[:i] 91 } 92 out := run(goCmd, "list", "-m", "-f={{.Path}} {{.Version}} {{.Dir}}", path) 93 f := strings.Fields(out) 94 if len(f) != 3 { 95 log.Printf("go list -m %s: unexpected output %q", arg, out) 96 exitCode = 1 97 continue 98 } 99 path, vers, dir := f[0], f[1], f[2] 100 mod, err := os.ReadFile(filepath.Join(gopath, "pkg/mod/cache/download", path, "@v", vers+".mod")) 101 if err != nil { 102 log.Printf("%s: %v", arg, err) 103 exitCode = 1 104 continue 105 } 106 info, err := os.ReadFile(filepath.Join(gopath, "pkg/mod/cache/download", path, "@v", vers+".info")) 107 if err != nil { 108 log.Printf("%s: %v", arg, err) 109 exitCode = 1 110 continue 111 } 112 113 a := new(txtar.Archive) 114 title := arg 115 if !strings.Contains(arg, "@") { 116 title += "@" + vers 117 } 118 a.Comment = []byte(fmt.Sprintf("module %s\n\n", title)) 119 a.Files = []txtar.File{ 120 {Name: ".mod", Data: mod}, 121 {Name: ".info", Data: info}, 122 } 123 dir = filepath.Clean(dir) 124 err = filepath.WalkDir(dir, func(path string, info fs.DirEntry, err error) error { 125 if !info.Type().IsRegular() { 126 return nil 127 } 128 name := info.Name() 129 if name == "go.mod" || strings.HasSuffix(name, ".go") { 130 data, err := os.ReadFile(path) 131 if err != nil { 132 return err 133 } 134 a.Files = append(a.Files, txtar.File{Name: str.TrimFilePathPrefix(path, dir), Data: data}) 135 } 136 return nil 137 }) 138 if err != nil { 139 log.Printf("%s: %v", arg, err) 140 exitCode = 1 141 continue 142 } 143 144 data := txtar.Format(a) 145 target := filepath.Join("mod", strings.ReplaceAll(path, "/", "_")+"_"+vers+".txt") 146 if err := os.WriteFile(target, data, 0666); err != nil { 147 log.Printf("%s: %v", arg, err) 148 exitCode = 1 149 continue 150 } 151 } 152 os.RemoveAll(tmpdir) 153 os.Exit(exitCode) 154} 155