1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
|
// Copyright 2018 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.
// The txtar-addmod command adds a module as a txtar archive to the a testdata module directory
// as understood by the goproxytest package (see https://godoc.org/github.com/rogpeppe/go-internal/goproxytest).
//
// Usage:
//
// txtar-addmod dir path@version...
//
// where dir is the directory to add the module to.
//
// In general, it's intended to be used only for very small modules - we do not want to check
// very large files into testdata/mod.
//
// It is acceptable to edit the archive afterward to remove or shorten files.
package main
import (
"bytes"
"flag"
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
"strings"
"golang.org/x/mod/module"
"golang.org/x/tools/txtar"
)
func usage() {
fmt.Fprintf(os.Stderr, "usage: txtar-addmod dir path@version...\n")
flag.PrintDefaults()
fmt.Fprintf(os.Stderr, `
The txtar-addmod command adds a module as a txtar archive to the
testdata module directory as understood by the goproxytest package
(see https://godoc.org/github.com/rogpeppe/go-internal/goproxytest).
The dir argument names to directory to add the module to. If dir is "-",
the result will instead be written to the standard output in a form
suitable for embedding directly into a testscript txtar file, with each
file prefixed with the ".gomodproxy" directory.
In general, txtar-addmod is intended to be used only for very small
modules - we do not want to check very large files into testdata/mod.
It is acceptable to edit the archive afterward to remove or shorten files.
`)
os.Exit(2)
}
var tmpdir string
func fatalf(format string, args ...interface{}) {
os.RemoveAll(tmpdir)
log.Fatalf(format, args...)
}
const goCmd = "go"
func main() {
os.Exit(main1())
}
var allFiles = flag.Bool("all", false, "include all source files")
func main1() int {
flag.Usage = usage
flag.Parse()
if flag.NArg() < 2 {
usage()
}
targetDir := flag.Arg(0)
modules := flag.Args()[1:]
log.SetPrefix("txtar-addmod: ")
log.SetFlags(0)
var err error
tmpdir, err = os.MkdirTemp("", "txtar-addmod-")
if err != nil {
log.Fatal(err)
}
run := func(command string, args ...string) string {
cmd := exec.Command(command, args...)
cmd.Dir = tmpdir
var stderr bytes.Buffer
cmd.Stderr = &stderr
out, err := cmd.Output()
if err != nil {
fatalf("%s %s: %v\n%s", command, strings.Join(args, " "), err, stderr.Bytes())
}
return string(out)
}
gopath := strings.TrimSpace(run("go", "env", "GOPATH"))
if gopath == "" {
fatalf("cannot find GOPATH")
}
exitCode := 0
for _, arg := range modules {
if err := os.WriteFile(filepath.Join(tmpdir, "go.mod"), []byte("module m\n"), 0o666); err != nil {
fatalf("%v", err)
}
run(goCmd, "get", "-d", arg)
path := arg
if i := strings.Index(path, "@"); i >= 0 {
path = path[:i]
}
out := run(goCmd, "list", "-m", "-f={{.Path}} {{.Version}} {{.Dir}}", path)
f := strings.Fields(out)
if len(f) != 3 {
log.Printf("go list -m %s: unexpected output %q", arg, out)
exitCode = 1
continue
}
path, vers, dir := f[0], f[1], f[2]
encpath, err := module.EscapePath(path)
if err != nil {
log.Printf("failed to encode path %q: %v", path, err)
continue
}
path = encpath
mod, err := os.ReadFile(filepath.Join(gopath, "pkg/mod/cache/download", path, "@v", vers+".mod"))
if err != nil {
log.Printf("%s: %v", arg, err)
exitCode = 1
continue
}
info, err := os.ReadFile(filepath.Join(gopath, "pkg/mod/cache/download", path, "@v", vers+".info"))
if err != nil {
log.Printf("%s: %v", arg, err)
exitCode = 1
continue
}
a := new(txtar.Archive)
title := arg
if !strings.Contains(arg, "@") {
title += "@" + vers
}
dir = filepath.Clean(dir)
modDir := strings.ReplaceAll(path, "/", "_") + "_" + vers
filePrefix := ""
if targetDir == "-" {
filePrefix = ".gomodproxy/" + modDir + "/"
} else {
// No comment if we're writing to stdout.
a.Comment = []byte(fmt.Sprintf("module %s\n\n", title))
}
a.Files = []txtar.File{
{Name: filePrefix + ".mod", Data: mod},
{Name: filePrefix + ".info", Data: info},
}
err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.Mode().IsRegular() {
return nil
}
// TODO: skip dirs like "testdata" or "_foo" unless -all
// is given?
name := info.Name()
switch {
case *allFiles:
case name == "go.mod":
case strings.HasSuffix(name, ".go"):
default:
// the name is not in the whitelist, and we're
// not including all files via -all
return nil
}
data, err := os.ReadFile(path)
if err != nil {
return err
}
a.Files = append(a.Files, txtar.File{
Name: filePrefix + strings.TrimPrefix(path, dir+string(filepath.Separator)),
Data: data,
})
return nil
})
if err != nil {
log.Printf("%s: %v", arg, err)
exitCode = 1
continue
}
data := txtar.Format(a)
if targetDir == "-" {
if _, err := os.Stdout.Write(data); err != nil {
log.Printf("cannot write output: %v", err)
exitCode = 1
break
}
} else {
if err := os.WriteFile(filepath.Join(targetDir, modDir+".txtar"), data, 0o666); err != nil {
log.Printf("%s: %v", arg, err)
exitCode = 1
continue
}
}
}
os.RemoveAll(tmpdir)
return exitCode
}
|