cmd/go/internal/vgo: main vgo drivers
diff --git a/vendor/cmd/go/internal/vgo/build.go b/vendor/cmd/go/internal/vgo/build.go new file mode 100644 index 0000000..37d14f3 --- /dev/null +++ b/vendor/cmd/go/internal/vgo/build.go
@@ -0,0 +1,79 @@ +// 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. + +package vgo + +import ( + "bytes" + "cmd/go/internal/base" + "cmd/go/internal/module" + "cmd/go/internal/search" + "encoding/hex" + "fmt" +) + +var ( + infoStart, _ = hex.DecodeString("3077af0c9274080241e1c107e6d618e6") + infoEnd, _ = hex.DecodeString("f932433186182072008242104116d8f2") +) + +func PackageModuleInfo(path string, deps []string) string { + if search.IsStandardImportPath(path) { + return "" + } + target := findModule(path, path) + mdeps := make(map[module.Version]bool) + for _, dep := range deps { + if !search.IsStandardImportPath(dep) { + mdeps[findModule(path, dep)] = true + } + } + var mods []module.Version + delete(mdeps, target) + for mod := range mdeps { + mods = append(mods, mod) + } + sortModules(mods) + + var buf bytes.Buffer + fmt.Fprintf(&buf, "path\t%s\n", path) + tv := target.Version + if tv == "" { + tv = "(devel)" + } + fmt.Fprintf(&buf, "mod\t%s\t%s\t%s\n", target.Path, tv, findModHash(target)) + for _, mod := range mods { + mv := mod.Version + if mv == "" { + mv = "(devel)" + } + fmt.Fprintf(&buf, "dep\t%s\t%s\t%s\n", mod.Path, mod.Version, findModHash(mod)) + } + return buf.String() +} + +func findModule(target, path string) module.Version { + if path == "." { + return buildList[0] + } + for _, mod := range buildList { + if importPathInModule(path, mod.Path) { + return mod + } + } + base.Fatalf("build %v: cannot find module for path %v", target, path) + panic("unreachable") +} + +func ModInfoProg(info string) []byte { + return []byte(fmt.Sprintf(` + package main + import _ "unsafe" + //go:linkname __debug_modinfo__ runtime/debug.modinfo + var __debug_modinfo__ string + func init() { + __debug_modinfo__ = %q + } + `, string(infoStart)+info+string(infoEnd))) +}
diff --git a/vendor/cmd/go/internal/vgo/fetch.go b/vendor/cmd/go/internal/vgo/fetch.go new file mode 100644 index 0000000..b76eb3d --- /dev/null +++ b/vendor/cmd/go/internal/vgo/fetch.go
@@ -0,0 +1,231 @@ +// 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. + +package vgo + +import ( + "archive/zip" + "bytes" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "sort" + "strings" + + "cmd/go/internal/base" + "cmd/go/internal/dirhash" + "cmd/go/internal/modfetch" + "cmd/go/internal/module" + "cmd/go/internal/semver" +) + +// fetch returns the directory in the local download cache +// holding the root of mod's source tree. +// It downloads the module if needed. +func fetch(mod module.Version) (dir string, err error) { + if r := replaced(mod); r != nil { + if r.New.Version == "" { + dir = r.New.Path + if !filepath.IsAbs(dir) { + dir = filepath.Join(ModRoot, dir) + } + return dir, nil + } + mod = r.New + } + + modpath := mod.Path + "@" + mod.Version + dir = filepath.Join(srcV, modpath) + if files, _ := ioutil.ReadDir(dir); len(files) == 0 { + zipfile := filepath.Join(srcV, "cache", mod.Path, "@v", mod.Version+".zip") + if _, err := os.Stat(zipfile); err == nil { + // Use it. + // This should only happen if the v/cache directory is preinitialized + // or if src/v/modpath was removed but not src/v/cache. + fmt.Fprintf(os.Stderr, "vgo: extracting %s %s\n", mod.Path, mod.Version) + } else { + if err := os.MkdirAll(filepath.Join(srcV, "cache", mod.Path, "@v"), 0777); err != nil { + return "", err + } + fmt.Fprintf(os.Stderr, "vgo: downloading %s %s\n", mod.Path, mod.Version) + if err := downloadZip(mod, zipfile); err != nil { + return "", err + } + } + if err := modfetch.Unzip(dir, zipfile, modpath, 0); err != nil { + fmt.Fprintf(os.Stderr, "-> %s\n", err) + return "", err + } + } + checkModHash(mod) + return dir, nil +} + +func downloadZip(mod module.Version, target string) error { + repo, err := modfetch.Lookup(mod.Path) + if err != nil { + return err + } + tmpfile, err := repo.Zip(mod.Version, os.TempDir()) + if err != nil { + return err + } + defer os.Remove(tmpfile) + + // Double-check zip file looks OK. + z, err := zip.OpenReader(tmpfile) + if err != nil { + z.Close() + return err + } + prefix := mod.Path + "@" + mod.Version + for _, f := range z.File { + if !strings.HasPrefix(f.Name, prefix) { + z.Close() + return fmt.Errorf("zip for %s has unexpected file %s", prefix[:len(prefix)-1], f.Name) + } + } + z.Close() + + hash, err := dirhash.HashZip(tmpfile, dirhash.DefaultHash) + if err != nil { + return err + } + r, err := os.Open(tmpfile) + if err != nil { + return err + } + defer r.Close() + w, err := os.Create(target) + if err != nil { + return err + } + if _, err := io.Copy(w, r); err != nil { + w.Close() + return fmt.Errorf("copying: %v", err) + } + if err := w.Close(); err != nil { + return err + } + if err := ioutil.WriteFile(target+"hash", []byte(hash), 0666); err != nil { + return err + } + return nil +} + +var useModHash = false +var modHash map[module.Version][]string + +func initModHash() { + if modHash != nil { + return + } + modHash = make(map[module.Version][]string) + file := filepath.Join(ModRoot, "go.modverify") + data, err := ioutil.ReadFile(file) + if err != nil && os.IsNotExist(err) { + return + } + if err != nil { + base.Fatalf("vgo: %v", err) + } + useModHash = true + lineno := 0 + for len(data) > 0 { + var line []byte + lineno++ + i := bytes.IndexByte(data, '\n') + if i < 0 { + line, data = data, nil + } else { + line, data = data[:i], data[i+1:] + } + f := strings.Fields(string(line)) + if len(f) != 3 { + base.Fatalf("vgo: malformed go.modverify:\n%s:%d: wrong number of fields", file, lineno) + } + mod := module.Version{Path: f[0], Version: f[1]} + modHash[mod] = append(modHash[mod], f[2]) + } +} + +func checkModHash(mod module.Version) { + initModHash() + if !useModHash { + return + } + + data, err := ioutil.ReadFile(filepath.Join(srcV, "cache", mod.Path, "@v", mod.Version+".ziphash")) + if err != nil { + base.Fatalf("vgo: verifying %s %s: %v", mod.Path, mod.Version, err) + } + h := strings.TrimSpace(string(data)) + if !strings.HasPrefix(h, "h1:") { + base.Fatalf("vgo: verifying %s %s: unexpected ziphash: %q", mod.Path, mod.Version, h) + } + + for _, vh := range modHash[mod] { + if h == vh { + return + } + if strings.HasPrefix(vh, "h1:") { + base.Fatalf("vgo: verifying %s %s: module hash mismatch\n\tdownloaded: %v\n\tgo.modverify: %v", mod.Path, mod.Version, h, vh) + } + } + if len(modHash[mod]) > 0 { + fmt.Fprintf(os.Stderr, "warning: verifying %s %s: unknown hashes in go.modverify: %v; adding %v", mod.Path, mod.Version, strings.Join(modHash[mod], ", "), h) + } + modHash[mod] = append(modHash[mod], h) +} + +func findModHash(mod module.Version) string { + data, err := ioutil.ReadFile(filepath.Join(srcV, "cache", mod.Path, "@v", mod.Version+".ziphash")) + if err != nil { + return "" + } + return strings.TrimSpace(string(data)) +} + +func writeModHash() { + if !useModHash { + return + } + + var mods []module.Version + for m := range modHash { + mods = append(mods, m) + } + sortModules(mods) + var buf bytes.Buffer + for _, m := range mods { + list := modHash[m] + sort.Strings(list) + for _, h := range list { + fmt.Fprintf(&buf, "%s %s %s\n", m.Path, m.Version, h) + } + } + + file := filepath.Join(ModRoot, "go.modverify") + data, _ := ioutil.ReadFile(filepath.Join(ModRoot, "go.modverify")) + if bytes.Equal(data, buf.Bytes()) { + return + } + + if err := ioutil.WriteFile(file, buf.Bytes(), 0666); err != nil { + base.Fatalf("vgo: writing go.modverify: %v", err) + } +} + +func sortModules(mods []module.Version) { + sort.Slice(mods, func(i, j int) bool { + mi := mods[i] + mj := mods[j] + if mi.Path != mj.Path { + return mi.Path < mj.Path + } + return semver.Compare(mi.Version, mj.Version) < 0 + }) +}
diff --git a/vendor/cmd/go/internal/vgo/get.go b/vendor/cmd/go/internal/vgo/get.go new file mode 100644 index 0000000..1ed5d19 --- /dev/null +++ b/vendor/cmd/go/internal/vgo/get.go
@@ -0,0 +1,139 @@ +// 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. + +package vgo + +import ( + "strings" + + "cmd/go/internal/base" + "cmd/go/internal/modfetch" + "cmd/go/internal/module" + "cmd/go/internal/mvs" + "cmd/go/internal/semver" +) + +var CmdGet = &base.Command{ + UsageLine: "get [build flags] [packages]", + Short: "TODO", + Long: ` +TODO + `, +} + +var getU = CmdGet.Flag.Bool("u", false, "") + +func init() { + CmdGet.Run = runGet // break init loop +} + +func runGet(cmd *base.Command, args []string) { + if *getU && len(args) > 0 { + base.Fatalf("vgo get: -u not supported with argument list") + } + if !*getU && len(args) == 0 { + base.Fatalf("vgo get: need arguments or -u") + } + + if *getU { + isGetU = true + ImportPaths([]string{"."}) + return + } + + Init() + InitMod() + var upgrade []module.Version + var downgrade []module.Version + var newPkgs []string + for _, pkg := range args { + var path, vers string + /* OLD CODE + if n := strings.Count(pkg, "(") + strings.Count(pkg, ")"); n > 0 { + i := strings.Index(pkg, "(") + j := strings.Index(pkg, ")") + if n != 2 || i < 0 || j <= i+1 || j != len(pkg)-1 && pkg[j+1] != '/' { + base.Errorf("vgo get: invalid module version syntax: %s", pkg) + continue + } + path, vers = pkg[:i], pkg[i+1:j] + pkg = pkg[:i] + pkg[j+1:] + */ + if i := strings.Index(pkg, "@"); i >= 0 { + path, pkg, vers = pkg[:i], pkg[:i], pkg[i+1:] + if strings.Contains(vers, "@") { + base.Errorf("vgo get: invalid module version syntax: %s", pkg) + continue + } + } else { + path = pkg + vers = "latest" + } + if vers == "none" { + downgrade = append(downgrade, module.Version{path, ""}) + } else { + info, err := modfetch.Query(path, vers, allowed) + if err != nil { + base.Errorf("vgo get %v: %v", pkg, err) + continue + } + upgrade = append(upgrade, module.Version{Path: path, Version: info.Version}) + newPkgs = append(newPkgs, pkg) + } + } + args = newPkgs + + // Upgrade. + var err error + buildList, err = mvs.Upgrade(Target, newReqs(), upgrade...) + if err != nil { + base.Fatalf("vgo get: %v", err) + } + + importPaths([]string{"."}) + + // Downgrade anything that went too far. + version := make(map[string]string) + for _, mod := range buildList { + version[mod.Path] = mod.Version + } + for _, mod := range upgrade { + if semver.Compare(mod.Version, version[mod.Path]) < 0 { + downgrade = append(downgrade, mod) + } + } + + if len(downgrade) > 0 { + buildList, err = mvs.Downgrade(Target, newReqs(buildList[1:]...), downgrade...) + if err != nil { + base.Fatalf("vgo get: %v", err) + } + + // TODO: Check that everything we need to import is still available. + /* + local := v.matchPackages("all", v.Reqs[:1]) + for _, path := range local { + dir, err := v.importDir(path) + if err != nil { + return err // TODO + } + imports, testImports, err := imports.ScanDir(dir, v.Tags) + for _, path := range imports { + xxx + } + for _, path := range testImports { + xxx + } + } + */ + } + writeGoMod() + + if len(args) > 0 { + InstallHook(args) + } +} + +// Call into "go install". Set by internal/work, which imports us. +var InstallHook func([]string)
diff --git a/vendor/cmd/go/internal/vgo/init.go b/vendor/cmd/go/internal/vgo/init.go new file mode 100644 index 0000000..c4e843d --- /dev/null +++ b/vendor/cmd/go/internal/vgo/init.go
@@ -0,0 +1,331 @@ +// 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. + +package vgo + +import ( + "bytes" + "cmd/go/internal/base" + "cmd/go/internal/cfg" + "cmd/go/internal/modconv" + "cmd/go/internal/modfetch" + "cmd/go/internal/modfile" + "cmd/go/internal/module" + "cmd/go/internal/mvs" + "cmd/go/internal/search" + "encoding/json" + "flag" + "fmt" + "io/ioutil" + "os" + "path" + "path/filepath" + "regexp" + "strconv" + "strings" +) + +var ( + cwd string + enabled = MustBeVgo + MustBeVgo = mustBeVgo() + initialized bool + + ModRoot string + modFile *modfile.File + excluded map[module.Version]bool + Target module.Version + + gopath string + srcV string +) + +func init() { + flag.BoolVar(&MustBeVgo, "vgo", MustBeVgo, "require use of modules") +} + +// mustBeVgo reports whether we are invoked as vgo +// (as opposed to go). +// If so, we only support builds with go.mod files. +func mustBeVgo() bool { + name := os.Args[0] + name = name[strings.LastIndex(name, "/")+1:] + name = name[strings.LastIndex(name, `\`)+1:] + return strings.HasPrefix(name, "vgo") +} + +func Init() { + if initialized { + return + } + initialized = true + + // If this is testgo - the test binary during cmd/go tests - then + // do not let it look for a go.mod. Only use vgo support if the + // global -vgo flag has been passed on the command line. + if base := filepath.Base(os.Args[0]); (base == "testgo" || base == "testgo.exe") && !MustBeVgo { + return + } + + var err error + cwd, err = os.Getwd() + if err != nil { + base.Fatalf("go: %v", err) + } + + root, _ := FindModuleRoot(cwd, "", MustBeVgo) + if root == "" { + // If invoked as vgo, insist on a mod file. + if MustBeVgo { + base.Fatalf("cannot determine module root; please create a go.mod file there") + } + return + } + enabled = true + ModRoot = root + search.SetModRoot(root) +} + +func Enabled() bool { + if !initialized { + panic("vgo: Enabled called before Init") + } + return enabled +} + +func InitMod() { + if Init(); !Enabled() || modFile != nil { + return + } + + list := filepath.SplitList(cfg.BuildContext.GOPATH) + if len(list) == 0 || list[0] == "" { + base.Fatalf("missing $GOPATH") + } + gopath = list[0] + srcV = filepath.Join(list[0], "src/v") + + gomod := filepath.Join(ModRoot, "go.mod") + data, err := ioutil.ReadFile(gomod) + if err != nil { + legacyModInit() + return + } + + f, err := modfile.Parse(gomod, data) + if err != nil { + // Errors returned by modfile.Parse begin with file:line. + base.Fatalf("vgo: errors parsing go.mod:\n%s\n", err) + } + modFile = f + + if len(f.Syntax.Stmt) == 0 { + // Empty mod file. Must add module path. + path, err := FindModulePath(ModRoot) + if err != nil { + base.Fatalf("vgo: %v", err) + } + f.AddModuleStmt(path) + } + + excluded = make(map[module.Version]bool) + for _, x := range f.Exclude { + excluded[x.Mod] = true + } + Target = f.Module.Mod + writeGoMod() +} + +func allowed(m module.Version) bool { + return !excluded[m] +} + +func legacyModInit() { + path, err := FindModulePath(ModRoot) + if err != nil { + base.Fatalf("vgo: %v", err) + } + modFile = new(modfile.File) + modFile.AddModuleStmt(path) + Target = module.Version{Path: path} + + for _, name := range altConfigs { + cfg := filepath.Join(ModRoot, name) + data, err := ioutil.ReadFile(cfg) + if err == nil { + convert := modconv.Converters[name] + if convert == nil { + return + } + if err := modfetch.ConvertLegacyConfig(modFile, cfg, data); err != nil { + base.Fatalf("vgo: %v", err) + } + return + } + } + + base.Fatalf("vgo: internal error: cannot find legacy config file (it was here a minute ago!)") +} + +var altConfigs = []string{ + "Gopkg.lock", + + "GLOCKFILE", + "Godeps/Godeps.json", + "dependencies.tsv", + "glide.lock", + "vendor.conf", + "vendor.yml", + "vendor/manifest", + "vendor/vendor.json", + + ".git/config", +} + +// Exported only for testing. +func FindModuleRoot(dir, limit string, legacyConfigOK bool) (root, file string) { + dir = filepath.Clean(dir) + dir1 := dir + limit = filepath.Clean(limit) + + // Look for enclosing go.mod. + for { + if _, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil { + return dir, "go.mod" + } + if dir == limit { + break + } + d := filepath.Dir(dir) + if d == dir { + break + } + dir = d + } + + // Failing that, look for enclosing alternate version config. + if legacyConfigOK { + dir = dir1 + for { + for _, name := range altConfigs { + if _, err := os.Stat(filepath.Join(dir, name)); err == nil { + return dir, name + } + } + if dir == limit { + break + } + d := filepath.Dir(dir) + if d == dir { + break + } + dir = d + } + } + + return "", "" +} + +// Exported only for testing. +func FindModulePath(dir string) (string, error) { + for _, dir := range filepath.SplitList(cfg.BuildContext.GOPATH) { + src := filepath.Join(dir, "src") + string(filepath.Separator) + if strings.HasPrefix(dir, src) { + return filepath.ToSlash(dir[len(src):]), nil + } + } + + // Cast about for import comments, + // first in top-level directory, then in subdirectories. + list, _ := ioutil.ReadDir(dir) + for _, info := range list { + if info.Mode().IsRegular() && strings.HasSuffix(info.Name(), ".go") { + if com := findImportComment(filepath.Join(dir, info.Name())); com != "" { + return com, nil + } + } + } + for _, info1 := range list { + if info1.IsDir() { + files, _ := ioutil.ReadDir(filepath.Join(dir, info1.Name())) + for _, info2 := range files { + if info2.Mode().IsRegular() && strings.HasSuffix(info2.Name(), ".go") { + if com := findImportComment(filepath.Join(dir, info1.Name(), info2.Name())); com != "" { + return path.Dir(com), nil + } + } + } + } + } + + // Look for Godeps.json declaring import path. + data, _ := ioutil.ReadFile(filepath.Join(dir, "Godeps/Godeps.json")) + var cfg struct{ ImportPath string } + json.Unmarshal(data, &cfg) + if cfg.ImportPath != "" { + return cfg.ImportPath, nil + } + + // Look for vendor.json declaring import path. + data, _ = ioutil.ReadFile(filepath.Join(dir, "vendor/vendor.json")) + var cfg2 struct{ RootPath string } + json.Unmarshal(data, &cfg2) + if cfg2.RootPath != "" { + return cfg2.RootPath, nil + } + + // Look for .git/config with github origin as last resort. + data, _ = ioutil.ReadFile(filepath.Join(dir, ".git/config")) + if m := gitOriginRE.FindSubmatch(data); m != nil { + return "github.com/" + string(m[1]), nil + } + + return "", fmt.Errorf("cannot determine module path for legacy source directory %s (outside GOROOT, no import comments)", dir) +} + +var ( + gitOriginRE = regexp.MustCompile(`(?m)^\[remote "origin"\]\n\turl = (?:https://github.com/|git@github.com:|gh:)([^/]+/[^/]+?)(\.git)?\n`) + importCommentRE = regexp.MustCompile(`(?m)^package[ \t]+[^ \t\n/]+[ \t]+//[ \t]+import[ \t]+(\"[^"]+\")[ \t]*\n`) +) + +func findImportComment(file string) string { + data, err := ioutil.ReadFile(file) + if err != nil { + return "" + } + m := importCommentRE.FindSubmatch(data) + if m == nil { + return "" + } + path, err := strconv.Unquote(string(m[1])) + if err != nil { + return "" + } + return path +} + +func writeGoMod() { + writeModHash() + + if buildList != nil { + min, err := mvs.Req(Target, buildList, newReqs()) + if err != nil { + base.Fatalf("vgo: %v", err) + } + modFile.SetRequire(min) + } + + file := filepath.Join(ModRoot, "go.mod") + old, _ := ioutil.ReadFile(file) + new, err := modFile.Format() + if err != nil { + base.Fatalf("vgo: %v", err) + } + if bytes.Equal(old, new) { + return + } + if err := ioutil.WriteFile(file, new, 0666); err != nil { + base.Fatalf("vgo: %v", err) + } +}
diff --git a/vendor/cmd/go/internal/vgo/list.go b/vendor/cmd/go/internal/vgo/list.go new file mode 100644 index 0000000..c6656c2 --- /dev/null +++ b/vendor/cmd/go/internal/vgo/list.go
@@ -0,0 +1,153 @@ +// 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. + +package vgo + +import ( + "bufio" + "fmt" + "io" + "os" + "regexp" + "unicode/utf8" + + "cmd/go/internal/base" + "cmd/go/internal/modfetch" + "cmd/go/internal/module" +) + +func ListT(pkgs []string) { + if Init(); !Enabled() { + base.Fatalf("go list: cannot use -t outside module") + } + InitMod() + + if len(pkgs) == 0 { + base.Fatalf("vgo list -t: need list of modules") + } + + for _, pkg := range pkgs { + repo, err := modfetch.Lookup(pkg) + if err != nil { + base.Errorf("vgo list -t: %v", err) + continue + } + path := repo.ModulePath() + fmt.Printf("%s\n", path) + tags, err := repo.Versions("") + if err != nil { + base.Errorf("vgo list -t: %v", err) + continue + } + for _, t := range tags { + if excluded[module.Version{Path: path, Version: t}] { + t += " # excluded" + } + fmt.Printf("\t%s\n", t) + } + } +} + +func ListM() { + if Init(); !Enabled() { + base.Fatalf("go list: cannot use -m outside module") + } + InitMod() + iterate(func(*loader) {}) + printListM(os.Stdout) +} + +func printListM(w io.Writer) { + var rows [][]string + rows = append(rows, []string{"MODULE", "VERSION"}) + for _, mod := range buildList { + v := mod.Version + if v == "" { + v = "-" + } + rows = append(rows, []string{mod.Path, v}) + if r := replaced(mod); r != nil { + rows = append(rows, []string{" => " + r.New.Path, r.New.Version}) + } + } + printTable(w, rows) +} + +func ListMU() { + if Init(); !Enabled() { + base.Fatalf("go list: cannot use -m outside module") + } + InitMod() + + quietLookup = true // do not chatter in v.Lookup + iterate(func(*loader) {}) + + var rows [][]string + rows = append(rows, []string{"MODULE", "VERSION", "LATEST"}) + for _, mod := range buildList { + var latest string + v := mod.Version + if v == "" { + v = "-" + latest = "-" + } else { + info, err := modfetch.Query(mod.Path, "latest", allowed) + if err != nil { + latest = "ERR: " + err.Error() + } else { + latest = info.Version + if !isPseudoVersion(latest) && !info.Time.IsZero() { + latest += info.Time.Local().Format(" (2006-01-02 15:04)") + } + } + if !isPseudoVersion(mod.Version) { + if info, err := modfetch.Query(mod.Path, mod.Version, nil); err == nil && !info.Time.IsZero() { + v += info.Time.Local().Format(" (2006-01-02 15:04)") + } + } + } + if latest == v { + latest = "-" + } + rows = append(rows, []string{mod.Path, v, latest}) + } + printTable(os.Stdout, rows) +} + +var pseudoVersionRE = regexp.MustCompile(`^v[0-9]+\.0\.0-[0-9]{14}-[A-Za-z0-9]+$`) + +func isPseudoVersion(v string) bool { + return pseudoVersionRE.MatchString(v) +} + +func printTable(w io.Writer, rows [][]string) { + var max []int + for _, row := range rows { + for i, c := range row { + n := utf8.RuneCountInString(c) + if i >= len(max) { + max = append(max, n) + } else if max[i] < n { + max[i] = n + } + } + } + + b := bufio.NewWriter(w) + for _, row := range rows { + for len(row) > 0 && row[len(row)-1] == "" { + row = row[:len(row)-1] + } + for i, c := range row { + b.WriteString(c) + if i+1 < len(row) { + for j := utf8.RuneCountInString(c); j < max[i]+2; j++ { + b.WriteRune(' ') + } + } + } + b.WriteRune('\n') + } + b.Flush() +}
diff --git a/vendor/cmd/go/internal/vgo/load.go b/vendor/cmd/go/internal/vgo/load.go new file mode 100644 index 0000000..548f0ed --- /dev/null +++ b/vendor/cmd/go/internal/vgo/load.go
@@ -0,0 +1,559 @@ +// 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. + +package vgo + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "sort" + "strings" + + "cmd/go/internal/base" + "cmd/go/internal/cfg" + "cmd/go/internal/imports" + "cmd/go/internal/modconv" + "cmd/go/internal/modfetch" + "cmd/go/internal/modfile" + "cmd/go/internal/module" + "cmd/go/internal/mvs" + "cmd/go/internal/search" + "cmd/go/internal/semver" +) + +type importLevel int + +const ( + levelNone importLevel = 0 + levelBuild importLevel = 1 + levelTest importLevel = 2 + levelTestRecursive importLevel = 3 +) + +var ( + buildList []module.Version + tags map[string]bool + importmap map[string]string + pkgdir map[string]string + isGetU bool +) + +func AddImports(gofiles []string) { + if Init(); !Enabled() { + return + } + InitMod() + + imports, testImports, err := imports.ScanFiles(gofiles, tags) + if err != nil { + base.Fatalf("vgo: %v", err) + } + + iterate(func(ld *loader) { + ld.importList(imports, levelBuild) + ld.importList(testImports, levelBuild) + }) + writeGoMod() +} + +func ImportPaths(args []string) []string { + if Init(); !Enabled() { + return search.ImportPaths(args) + } + InitMod() + + paths := importPaths(args) + writeGoMod() + return paths +} + +func importPaths(args []string) []string { + cleaned := search.CleanImportPaths(args) + iterate(func(ld *loader) { + args = expandImportPaths(cleaned) + for i, pkg := range args { + if pkg == "." || pkg == ".." || strings.HasPrefix(pkg, "./") || strings.HasPrefix(pkg, "../") { + dir := filepath.Join(cwd, pkg) + if dir == ModRoot { + pkg = Target.Path + } else if strings.HasPrefix(dir, ModRoot+string(filepath.Separator)) { + pkg = Target.Path + filepath.ToSlash(dir[len(ModRoot):]) + } else { + base.Errorf("vgo: package %s outside module root", pkg) + continue + } + args[i] = pkg + } + ld.importPkg(pkg, levelBuild) + } + }) + return args +} + +func Lookup(parentPath, path string) (dir, realPath string, err error) { + realPath = importmap[path] + if realPath == "" { + if search.IsStandardImportPath(path) { + dir := filepath.Join(cfg.GOROOT, "src", path) + if _, err := os.Stat(dir); err == nil { + return dir, path, nil + } + } + return "", "", fmt.Errorf("no such package in module") + } + return pkgdir[realPath], realPath, nil +} + +func iterate(doImports func(*loader)) { + var err error + mvsOp := mvs.BuildList + if isGetU { + mvsOp = mvs.UpgradeAll + } + buildList, err = mvsOp(Target, newReqs()) + if err != nil { + base.Fatalf("vgo: %v", err) + } + + var ld *loader + for { + ld = newLoader() + doImports(ld) + if len(ld.missing) == 0 { + break + } + for _, m := range ld.missing { + findMissing(m) + } + base.ExitIfErrors() + buildList, err = mvsOp(Target, newReqs()) + if err != nil { + base.Fatalf("vgo: %v", err) + } + } + base.ExitIfErrors() + + importmap = ld.importmap + pkgdir = ld.pkgdir +} + +type loader struct { + imported map[string]importLevel + importmap map[string]string + pkgdir map[string]string + missing []missing + imports []string + stack []string +} + +type missing struct { + path string + stack string +} + +func newLoader() *loader { + ld := &loader{ + imported: make(map[string]importLevel), + importmap: make(map[string]string), + pkgdir: make(map[string]string), + } + ld.imported["C"] = 100 + return ld +} + +func (ld *loader) stackText() string { + var buf bytes.Buffer + for _, p := range ld.stack[:len(ld.stack)-1] { + fmt.Fprintf(&buf, "import %q ->\n\t", p) + } + fmt.Fprintf(&buf, "import %q", ld.stack[len(ld.stack)-1]) + return buf.String() +} + +func (ld *loader) importList(pkgs []string, level importLevel) { + for _, pkg := range pkgs { + ld.importPkg(pkg, level) + } +} + +func (ld *loader) importPkg(path string, level importLevel) { + if ld.imported[path] >= level { + return + } + + ld.stack = append(ld.stack, path) + defer func() { + ld.stack = ld.stack[:len(ld.stack)-1] + }() + + // Any rewritings go here. + realPath := path + + ld.imported[path] = level + ld.importmap[path] = realPath + if realPath != path && ld.imported[realPath] >= level { + // Already handled. + return + } + + dir := ld.importDir(realPath) + if dir == "" { + return + } + + ld.pkgdir[realPath] = dir + + imports, testImports, err := imports.ScanDir(dir, imports.Tags) + if err != nil { + base.Errorf("vgo: %s [%s]: %v", ld.stackText(), dir, err) + return + } + nextLevel := level + if level == levelTest { + nextLevel = levelBuild + } + for _, pkg := range imports { + ld.importPkg(pkg, nextLevel) + } + if level >= levelTest { + for _, pkg := range testImports { + ld.importPkg(pkg, nextLevel) + } + } +} + +func (ld *loader) importDir(path string) string { + if importPathInModule(path, Target.Path) { + dir := ModRoot + if len(path) > len(Target.Path) { + dir = filepath.Join(dir, path[len(Target.Path)+1:]) + } + return dir + } + + i := strings.Index(path, "/") + if i < 0 || !strings.Contains(path[:i], ".") { + if strings.HasPrefix(path, "golang_org/") { + return filepath.Join(cfg.GOROOT, "src/vendor", path) + } + return filepath.Join(cfg.GOROOT, "src", path) + } + + var mod1 module.Version + var dir1 string + for _, mod := range buildList { + if !importPathInModule(path, mod.Path) { + continue + } + dir, err := fetch(mod) + if err != nil { + base.Errorf("vgo: %s: %v", ld.stackText(), err) + return "" + } + if len(path) > len(mod.Path) { + dir = filepath.Join(dir, path[len(mod.Path)+1:]) + } + if dir1 != "" { + base.Errorf("vgo: %s: found in both %v %v and %v %v", ld.stackText(), + mod1.Path, mod1.Version, mod.Path, mod.Version) + return "" + } + dir1 = dir + mod1 = mod + } + if dir1 != "" { + return dir1 + } + ld.missing = append(ld.missing, missing{path, ld.stackText()}) + return "" +} + +func replaced(mod module.Version) *modfile.Replace { + var found *modfile.Replace + for _, r := range modFile.Replace { + if r.Old == mod { + found = r // keep going + } + } + return found +} + +func importPathInModule(path, mpath string) bool { + return mpath == path || + len(path) > len(mpath) && path[len(mpath)] == '/' && path[:len(mpath)] == mpath +} + +var found = make(map[string]bool) + +func findMissing(m missing) { + for _, mod := range buildList { + if importPathInModule(m.path, mod.Path) { + // Leave for ordinary build to complain about the missing import. + return + } + } + fmt.Fprintf(os.Stderr, "vgo: resolving import %q\n", m.path) + repo, err := modfetch.Lookup(m.path) + if err != nil { + base.Errorf("vgo: %s: %v", m.stack, err) + return + } + root := repo.ModulePath() + fmt.Fprintf(os.Stderr, "vgo: finding %s (latest)\n", root) + if found[root] { + base.Fatalf("internal error: findmissing loop on %s", root) + } + found[root] = true + info, err := modfetch.Query(root, "latest", allowed) + if err != nil { + base.Errorf("vgo: %s: %v", m.stack, err) + return + } + fmt.Fprintf(os.Stderr, "vgo: adding %s %s\n", root, info.Version) + buildList = append(buildList, module.Version{root, info.Version}) + modFile.AddRequire(root, info.Version) +} + +type mvsReqs struct { + extra []module.Version +} + +func newReqs(extra ...module.Version) *mvsReqs { + r := &mvsReqs{ + extra: extra, + } + return r +} + +func (r *mvsReqs) Required(mod module.Version) ([]module.Version, error) { + list, err := r.required(mod) + if err != nil { + return nil, err + } + if *getU { + for i := range list { + list[i].Version = "none" + } + return list, nil + } + for i, mv := range list { + for excluded[mv] { + mv1, err := r.Next(mv) + if err != nil { + return nil, err + } + if mv1.Version == "" { + return nil, fmt.Errorf("%s(%s) depends on excluded %s(%s) with no newer version available", mod.Path, mod.Version, mv.Path, mv.Version) + } + mv = mv1 + } + list[i] = mv + } + return list, nil +} + +var vgoVersion = []byte(modconv.Prefix) + +func (r *mvsReqs) required(mod module.Version) ([]module.Version, error) { + if mod == Target { + var list []module.Version + if buildList != nil { + list = append(list, buildList[1:]...) + return list, nil + } + for _, r := range modFile.Require { + list = append(list, r.Mod) + } + list = append(list, r.extra...) + return list, nil + } + + origPath := mod.Path + if repl := replaced(mod); repl != nil { + if repl.New.Version == "" { + // TODO: need to slip the new version into the tags list etc. + dir := repl.New.Path + if !filepath.IsAbs(dir) { + dir = filepath.Join(ModRoot, dir) + } + gomod := filepath.Join(dir, "go.mod") + data, err := ioutil.ReadFile(gomod) + if err != nil { + return nil, err + } + f, err := modfile.Parse(gomod, data) + if err != nil { + return nil, err + } + var list []module.Version + for _, r := range f.Require { + list = append(list, r.Mod) + } + return list, nil + } + mod = repl.New + } + + if mod.Version == "none" { + return nil, nil + } + + if !semver.IsValid(mod.Version) { + // Disallow the broader queries supported by fetch.Lookup. + panic(fmt.Errorf("invalid semantic version %q for %s", mod.Version, mod.Path)) + return nil, fmt.Errorf("invalid semantic version %q", mod.Version) + } + + gomod := filepath.Join(srcV, "cache", mod.Path, "@v", mod.Version+".mod") + infofile := filepath.Join(srcV, "cache", mod.Path, "@v", mod.Version+".info") + var f *modfile.File + if data, err := ioutil.ReadFile(gomod); err == nil { + // If go.mod has a //vgo comment at the start, + // it was auto-converted from a legacy lock file. + // The auto-conversion details may have bugs and + // may be fixed in newer versions of vgo. + // We ignore cached go.mod files if they do not match + // our own vgoVersion. + if !bytes.HasPrefix(data, vgoVersion[:len("//vgo")]) || bytes.HasPrefix(data, vgoVersion) { + f, err := modfile.Parse(gomod, data) + if err != nil { + return nil, err + } + var list []module.Version + for _, r := range f.Require { + list = append(list, r.Mod) + } + return list, nil + } + f, err = modfile.Parse("go.mod", data) + if err != nil { + return nil, fmt.Errorf("parsing downloaded go.mod: %v", err) + } + } else { + if !quietLookup { + fmt.Fprintf(os.Stderr, "vgo: finding %s %s\n", mod.Path, mod.Version) + } + repo, err := modfetch.Lookup(mod.Path) + if err != nil { + base.Errorf("vgo: %s: %v\n", mod.Path, err) + return nil, err + } + info, err := repo.Stat(mod.Version) + if err != nil { + base.Errorf("vgo: %s %s: %v\n", mod.Path, mod.Version, err) + return nil, err + } + data, err := repo.GoMod(info.Version) + if err != nil { + base.Errorf("vgo: %s %s: %v\n", mod.Path, mod.Version, err) + return nil, err + } + + f, err = modfile.Parse("go.mod", data) + if err != nil { + return nil, fmt.Errorf("parsing downloaded go.mod: %v", err) + } + if mpath := f.Module.Mod.Path; mpath != origPath && mpath != mod.Path { + return nil, fmt.Errorf("downloaded %q and got module %q", mod.Path, mpath) + } + + dir := filepath.Dir(gomod) + if err := os.MkdirAll(dir, 0777); err != nil { + return nil, fmt.Errorf("caching go.mod: %v", err) + } + js, err := json.Marshal(info) + if err != nil { + return nil, fmt.Errorf("internal error: json failure: %v", err) + } + if err := ioutil.WriteFile(infofile, js, 0666); err != nil { + return nil, fmt.Errorf("caching info: %v", err) + } + if err := ioutil.WriteFile(gomod, data, 0666); err != nil { + return nil, fmt.Errorf("caching go.mod: %v", err) + } + } + + var list []module.Version + for _, req := range f.Require { + list = append(list, req.Mod) + } + if false { + fmt.Fprintf(os.Stderr, "REQLIST %v:\n", mod) + for _, req := range list { + fmt.Fprintf(os.Stderr, "\t%v\n", req) + } + } + return list, nil +} + +var quietLookup bool + +func (*mvsReqs) Max(v1, v2 string) string { + if semver.Compare(v1, v2) == -1 { + return v2 + } + return v1 +} + +func (*mvsReqs) Latest(path string) (module.Version, error) { + // Note that query "latest" is not the same as + // using repo.LatestAt. + // The query only falls back to untagged versions + // if nothing is tagged. The LatestAt method + // only ever returns untagged versions, + // which is not what we want. + fmt.Fprintf(os.Stderr, "vgo: finding %s latest\n", path) + info, err := modfetch.Query(path, "latest", allowed) + if err != nil { + return module.Version{}, err + } + return module.Version{Path: path, Version: info.Version}, nil +} + +var versionCache = make(map[string][]string) + +func versions(path string) ([]string, error) { + list, ok := versionCache[path] + if !ok { + var err error + repo, err := modfetch.Lookup(path) + if err != nil { + return nil, err + } + list, err = repo.Versions("") + if err != nil { + return nil, err + } + versionCache[path] = list + } + return list, nil +} + +func (*mvsReqs) Previous(m module.Version) (module.Version, error) { + list, err := versions(m.Path) + if err != nil { + return module.Version{}, err + } + i := sort.Search(len(list), func(i int) bool { return semver.Compare(list[i], m.Version) >= 0 }) + if i > 0 { + return module.Version{Path: m.Path, Version: list[i-1]}, nil + } + return module.Version{Path: m.Path, Version: "none"}, nil +} + +func (*mvsReqs) Next(m module.Version) (module.Version, error) { + list, err := versions(m.Path) + if err != nil { + return module.Version{}, err + } + i := sort.Search(len(list), func(i int) bool { return semver.Compare(list[i], m.Version) > 0 }) + if i < len(list) { + return module.Version{Path: m.Path, Version: list[i]}, nil + } + return module.Version{Path: m.Path, Version: "none"}, nil +}
diff --git a/vendor/cmd/go/internal/vgo/search.go b/vendor/cmd/go/internal/vgo/search.go new file mode 100644 index 0000000..b32dce2 --- /dev/null +++ b/vendor/cmd/go/internal/vgo/search.go
@@ -0,0 +1,174 @@ +// 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. + +package vgo + +import ( + "fmt" + "go/build" + "os" + "path/filepath" + "sort" + "strings" + + "cmd/go/internal/base" + "cmd/go/internal/cfg" + "cmd/go/internal/imports" + "cmd/go/internal/module" + "cmd/go/internal/search" +) + +func expandImportPaths(args []string) []string { + var out []string + for _, a := range args { + if search.IsMetaPackage(a) { + switch a { + default: + fmt.Fprintf(os.Stderr, "warning: %q matches no packages when using modules", a) + case "all": + out = append(out, AllPackages(a)...) + } + continue + } + if strings.Contains(a, "...") { + if build.IsLocalImport(a) { + out = append(out, search.AllPackagesInFS(a)...) + } else { + out = append(out, AllPackages(a)...) + } + continue + } + out = append(out, a) + } + return out +} + +// AllPackages returns all the packages that can be found +// under the $GOPATH directories and $GOROOT matching pattern. +// The pattern is either "all" (all packages), "std" (standard packages), +// "cmd" (standard commands), or a path including "...". +func AllPackages(pattern string) []string { + pkgs := MatchPackages(pattern) + if len(pkgs) == 0 { + fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern) + } + return pkgs +} + +// MatchPackages returns a list of package paths matching pattern +// (see go help packages for pattern syntax). +func MatchPackages(pattern string) []string { + if pattern == "std" || pattern == "cmd" { + return nil + } + if pattern == "all" { + return MatchAll() + } + + return matchPackages(pattern, buildList) +} + +func matchPackages(pattern string, buildList []module.Version) []string { + match := func(string) bool { return true } + treeCanMatch := func(string) bool { return true } + if !search.IsMetaPackage(pattern) { + match = search.MatchPattern(pattern) + treeCanMatch = search.TreeCanMatchPattern(pattern) + } + + have := map[string]bool{ + "builtin": true, // ignore pseudo-package that exists only for documentation + } + if !cfg.BuildContext.CgoEnabled { + have["runtime/cgo"] = true // ignore during walk + } + var pkgs []string + + for _, mod := range buildList { + if !treeCanMatch(mod.Path) { + continue + } + var root string + if mod.Version == "" { + root = ModRoot + } else { + var err error + root, err = fetch(mod) + if err != nil { + base.Errorf("vgo: %v", err) + continue + } + } + root = filepath.Clean(root) + + filepath.Walk(root, func(path string, fi os.FileInfo, err error) error { + if err != nil { + return nil + } + + want := true + // Avoid .foo, _foo, and testdata directory trees. + _, elem := filepath.Split(path) + if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" { + want = false + } + + name := mod.Path + filepath.ToSlash(path[len(root):]) + if !treeCanMatch(name) { + want = false + } + + if !fi.IsDir() { + if fi.Mode()&os.ModeSymlink != 0 && want { + if target, err := os.Stat(path); err == nil && target.IsDir() { + fmt.Fprintf(os.Stderr, "warning: ignoring symlink %s\n", path) + } + } + return nil + } + if !want { + return filepath.SkipDir + } + + if have[name] { + return nil + } + have[name] = true + if !match(name) { + return nil + } + if _, _, err := imports.ScanDir(path, imports.Tags); err == imports.ErrNoGo { + return nil + } + + if elem == "vendor" { + return filepath.SkipDir // ignore children + } + + pkgs = append(pkgs, name) + return nil + }) + } + return pkgs +} + +// MatchAll returns a list of the packages matching the pattern "all". +// We redefine "all" to mean start with the packages in the current module +// and then follow imports into other modules to add packages imported +// (directly or indirectly) as part of builds in this module. +// It does not include packages in other modules that are not needed +// by builds of this module. +func MatchAll() []string { + local := matchPackages("all", buildList[:1]) + ld := newLoader() + ld.importList(local, levelTestRecursive) + var all []string + for _, pkg := range ld.importmap { + if !search.IsStandardImportPath(pkg) { + all = append(all, pkg) + } + } + sort.Strings(all) + return all +}
diff --git a/vendor/cmd/go/internal/vgo/vendor.go b/vendor/cmd/go/internal/vgo/vendor.go new file mode 100644 index 0000000..e3330f6 --- /dev/null +++ b/vendor/cmd/go/internal/vgo/vendor.go
@@ -0,0 +1,124 @@ +// 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. + +package vgo + +import ( + "bytes" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "strings" + + "cmd/go/internal/base" +) + +var CmdVendor = &base.Command{ + UsageLine: "vendor", + Run: runVendor, + Short: "vendor dependencies of current module", + Long: ` +Vendor resets the module's vendor directory to include all +packages needed to builds and test all packages in the module +and their dependencies. + `, +} + +func runVendor(cmd *base.Command, args []string) { + if Init(); !Enabled() { + base.Fatalf("vgo vendor: cannot use -m outside module") + } + if len(args) != 0 { + base.Fatalf("vgo vendor: vendor takes no arguments") + } + InitMod() + // TODO(rsc): This should scan directories with more permissive build tags. + pkgs := ImportPaths([]string{"all"}) + + vdir := filepath.Join(ModRoot, "vendor") + if err := os.RemoveAll(vdir); err != nil { + base.Fatalf("vgo vendor: %v", err) + } + + for _, pkg := range pkgs { + vendorPkg(vdir, pkg) + } + + var buf bytes.Buffer + printListM(&buf) + if err := ioutil.WriteFile(filepath.Join(vdir, "vgo.list"), buf.Bytes(), 0666); err != nil { + base.Fatalf("vgo vendor: %v", err) + } +} + +func vendorPkg(vdir, pkg string) { + if hasPathPrefix(pkg, Target.Path) { + return + } + realPath := importmap[pkg] + if realPath != pkg && importmap[realPath] != "" { + fmt.Fprintf(os.Stderr, "warning: %s imported as both %s and %s; making two copies.\n", realPath, realPath, pkg) + } + + dst := filepath.Join(vdir, pkg) + src := pkgdir[realPath] + if src == "" { + fmt.Fprintf(os.Stderr, "internal error: no pkg for %s -> %s\n", pkg, realPath) + } + copyDir(dst, src, false) +} + +func copyDir(dst, src string, recursive bool) { + files, err := ioutil.ReadDir(src) + if err != nil { + base.Fatalf("vgo vendor: %v", err) + } + if err := os.MkdirAll(dst, 0777); err != nil { + base.Fatalf("vgo vendor: %v", err) + } + for _, file := range files { + if file.IsDir() { + if recursive || file.Name() == "testdata" { + copyDir(filepath.Join(dst, file.Name()), filepath.Join(src, file.Name()), true) + } + continue + } + if !file.Mode().IsRegular() { + continue + } + r, err := os.Open(filepath.Join(src, file.Name())) + if err != nil { + base.Fatalf("vgo vendor: %v", err) + } + w, err := os.Create(filepath.Join(dst, file.Name())) + if err != nil { + base.Fatalf("vgo vendor: %v", err) + } + if _, err := io.Copy(w, r); err != nil { + base.Fatalf("vgo vendor: %v", err) + } + r.Close() + if err := w.Close(); err != nil { + base.Fatalf("vgo vendor: %v", err) + } + } +} + +// hasPathPrefix reports whether the path s begins with the +// elements in prefix. +func hasPathPrefix(s, prefix string) bool { + switch { + default: + return false + case len(s) == len(prefix): + return s == prefix + case len(s) > len(prefix): + if prefix != "" && prefix[len(prefix)-1] == '/' { + return strings.HasPrefix(s, prefix) + } + return s[len(prefix)] == '/' && s[:len(prefix)] == prefix + } +}
diff --git a/vendor/cmd/go/internal/vgo/verify.go b/vendor/cmd/go/internal/vgo/verify.go new file mode 100644 index 0000000..2c07018 --- /dev/null +++ b/vendor/cmd/go/internal/vgo/verify.go
@@ -0,0 +1,101 @@ +// 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. + +package vgo + +import ( + "bytes" + "fmt" + "io/ioutil" + "os" + "path/filepath" + + "cmd/go/internal/base" + "cmd/go/internal/dirhash" + "cmd/go/internal/module" +) + +var CmdVerify = &base.Command{ + UsageLine: "verify", + Run: runVerify, + Short: "verify downloaded modules against expected hashes", + Long: ` +Verify checks that the depdendencies of the current module, +which are stored in a local downloaded source cache, +have not been modified since being downloaded. + +If all the modules are unmodified, verify prints + + all modules verified + +and exits successfully (status 0). Otherwise, verify reports +which modules have been changed and exits with a non-zero status. + `, +} + +func runVerify(cmd *base.Command, args []string) { + if Init(); !Enabled() { + base.Fatalf("vgo verify: cannot use outside module") + } + if len(args) != 0 { + // TODO: take arguments + base.Fatalf("vgo verify: verify takes no arguments") + } + InitMod() + ImportPaths([]string{"all"}) + + ok := true + for _, mod := range buildList[1:] { + ok = verifyMod(mod) && ok + } + if ok { + fmt.Printf("all modules verified\n") + } +} + +func verifyMod(mod module.Version) bool { + ok := true + zip := filepath.Join(srcV, "cache", mod.Path+"@"+mod.Version, "zip") + _, zipErr := os.Stat(zip) + dir := filepath.Join(srcV, mod.Path+"@"+mod.Version) + _, dirErr := os.Stat(dir) + data, err := ioutil.ReadFile(zip + "hash") + if err != nil { + if zipErr != nil && os.IsNotExist(zipErr) && dirErr != nil && os.IsNotExist(dirErr) { + // Nothing downloaded yet. Nothing to verify. + return true + } + base.Errorf("%s %s: missing ziphash: %v", mod.Path, mod.Version, err) + return false + } + h := string(bytes.TrimSpace(data)) + + if zipErr != nil && os.IsNotExist(zipErr) { + // ok + } else { + hZ, err := dirhash.HashZip(zip, dirhash.DefaultHash) + if err != nil { + base.Errorf("%s %s: %v", mod.Path, mod.Version, err) + return false + } else if hZ != h { + base.Errorf("%s %s: zip has been modified (%v)", mod.Path, mod.Version, zip) + ok = false + } + } + if dirErr != nil && os.IsNotExist(dirErr) { + // ok + } else { + hD, err := dirhash.HashDir(dir, mod.Path+"@"+mod.Version, dirhash.DefaultHash) + if err != nil { + + base.Errorf("%s %s: %v", mod.Path, mod.Version, err) + return false + } + if hD != h { + base.Errorf("%s %s: dir has been modified (%v)", mod.Path, mod.Version, dir) + ok = false + } + } + return ok +}