cmd/go/internal/modload: work better with -getmode=vendor
If we have -getmode=vendor, we can still make a list
of all the modules using the vendor/modules.txt file.
And we can make sure that builds work.
Fixes golang/go#26126.
Change-Id: I1a68cbfed71227b8dad2803fcdf8291129681b72
Reviewed-on: https://go-review.googlesource.com/122479
Reviewed-by: Bryan C. Mills <bcmills@google.com>
diff --git a/vendor/cmd/go/internal/modload/build.go b/vendor/cmd/go/internal/modload/build.go
index b6b3280..d139996 100644
--- a/vendor/cmd/go/internal/modload/build.go
+++ b/vendor/cmd/go/internal/modload/build.go
@@ -101,6 +101,11 @@
Indirect: fromBuildList && loaded != nil && !loaded.direct[m.Path],
}
+ if cfg.BuildGetmode == "vendor" {
+ info.Dir = filepath.Join(ModRoot, "vendor", m.Path)
+ return info
+ }
+
// complete fills in the extra fields in m.
complete := func(m *modinfo.ModulePublic) {
if m.Version != "" {
diff --git a/vendor/cmd/go/internal/modload/load.go b/vendor/cmd/go/internal/modload/load.go
index 703d576..6d9f83e 100644
--- a/vendor/cmd/go/internal/modload/load.go
+++ b/vendor/cmd/go/internal/modload/load.go
@@ -819,6 +819,24 @@
return c.list, c.err
}
+var vendorOnce sync.Once
+var vendorList []module.Version
+
+// readVendorList reads the list of vendored modules from vendor/modules.txt.
+func readVendorList() {
+ var list []module.Version
+ data, _ := ioutil.ReadFile(filepath.Join(ModRoot, "vendor/modules.txt"))
+ for _, line := range strings.Split(string(data), "\n") {
+ if strings.HasPrefix(line, "# ") {
+ f := strings.Fields(line)
+ if len(f) == 3 && semver.IsValid(f[2]) {
+ list = append(list, module.Version{Path: f[1], Version: f[2]})
+ }
+ }
+ }
+ vendorList = list
+}
+
func (r *mvsReqs) required(mod module.Version) ([]module.Version, error) {
if mod == Target {
var list []module.Version
@@ -826,6 +844,14 @@
return list, nil
}
+ if cfg.BuildGetmode == "vendor" {
+ // For every module other than the target,
+ // return the full list of modules from modules.txt.
+ // But only read vendor/modules.txt once.
+ vendorOnce.Do(readVendorList)
+ return vendorList, nil
+ }
+
origPath := mod.Path
if repl := Replacement(mod); repl.Path != "" {
if repl.Version == "" {
diff --git a/vendor/cmd/go/mod_test.go b/vendor/cmd/go/mod_test.go
index 01e07d0..d94556b 100644
--- a/vendor/cmd/go/mod_test.go
+++ b/vendor/cmd/go/mod_test.go
@@ -634,6 +634,21 @@
tg.grepStdout(`rsc.io/quote v1.2.1`, "expected quote v1.2.1") // -u=patch with no args applies to deps of main module
tg.grepStdout(`rsc.io/sampler v1.3.1`, "expected sampler line to stay")
tg.grepStdout(`golang.org/x/text v0.0.0-`, "expected x/text pseudo-version") // even though x/text v0.3.0 is tagged
+
+ tg.run("get", "-m", "rsc.io/quote@v1.5.1")
+ tg.run("mod", "-vendor")
+ tg.setenv("GOPATH", tg.path("empty"))
+ tg.setenv("GOPROXY", "file:///nonexist")
+
+ tg.run("list", "-getmode=vendor", "all")
+ tg.run("list", "-getmode=vendor", "-m", "-f={{.Path}} {{.Version}} {{.Dir}}", "all")
+ tg.grepStdout(`rsc.io/quote v1.5.1 .*vendor[\\/]rsc.io[\\/]quote`, "expected vendored rsc.io/quote")
+ tg.grepStdout(`golang.org/x/text v0.0.0.* .*vendor[\\/]golang.org[\\/]x[\\/]text`, "expected vendored golang.org/x/text")
+
+ tg.runFail("list", "-getmode=vendor", "-m", "rsc.io/quote@latest")
+ tg.grepStderr(`module lookup disabled by -getmode=vendor`, "expected disabled")
+ tg.runFail("get", "-getmode=vendor", "-u")
+ tg.grepStderr(`go get: disabled by -getmode=vendor`, "expected disabled")
}
func TestModBadDomain(t *testing.T) {
@@ -855,10 +870,12 @@
tg.must(os.MkdirAll(tg.path("x"), 0777))
tg.must(ioutil.WriteFile(tg.path("x/x.go"), []byte(`
package x
+ import _ "rsc.io/quote"
`), 0666))
tg.must(ioutil.WriteFile(tg.path("x/go.mod"), []byte(`
module x
- require rsc.io/quote v1.2.0
+ require rsc.io/quote v1.5.1
+ replace rsc.io/sampler v1.3.0 => rsc.io/sampler v1.3.1
`), 0666))
tg.cd(tg.path("x"))
@@ -871,7 +888,7 @@
tg.grepStdoutNot(`quote@`, "should not have local copy of code")
tg.run("list", "-f={{.Dir}}", "rsc.io/quote") // downloads code to load package
- tg.grepStdout(`mod[\\/]rsc.io[\\/]quote@v1.2.0`, "expected cached copy of code")
+ tg.grepStdout(`mod[\\/]rsc.io[\\/]quote@v1.5.1`, "expected cached copy of code")
dir := strings.TrimSpace(tg.getStdout())
info, err := os.Stat(dir)
if err != nil {
@@ -881,8 +898,9 @@
t.Fatalf("%s should be unwritable", dir)
}
- tg.run("list", "-m", "-f={{.Dir}}", "rsc.io/quote") // now module list should find it too
- tg.grepStdout(`mod[\\/]rsc.io[\\/]quote@v1.2.0`, "expected cached copy of code")
+ tg.run("list", "-m", "-f={{.Path}} {{.Version}} {{.Dir}}{{with .Replace}} => {{.Version}} {{.Dir}}{{end}}", "all")
+ tg.grepStdout(`mod[\\/]rsc.io[\\/]quote@v1.5.1`, "expected cached copy of code")
+ tg.grepStdout(`v1.3.0 .*mod[\\/]rsc.io[\\/]sampler@v1.3.1 => v1.3.1 .*@v1.3.1`, "expected v1.3.1 replacement")
// check that list std works; also check that rsc.io/quote/buggy is a listable package
tg.run("list", "std", "rsc.io/quote/buggy")