cmd/go/internal/modcmd: remove unused modules during -sync Fixes golang/go#24101. Change-Id: I3900502372281515b49e98ed1465c2aa8d9a016b Reviewed-on: https://go-review.googlesource.com/118877 Reviewed-by: Bryan C. Mills <bcmills@google.com>
diff --git a/vendor/cmd/go/internal/modcmd/mod.go b/vendor/cmd/go/internal/modcmd/mod.go index 55c7a61..b97cd8c 100644 --- a/vendor/cmd/go/internal/modcmd/mod.go +++ b/vendor/cmd/go/internal/modcmd/mod.go
@@ -236,15 +236,34 @@ needBuildList := *modFix if *modSync || *modVendor || needBuildList { + var pkgs []string if *modSync || *modVendor { - fmt.Println(vgo.ImportPaths([]string{"ALL"})) + pkgs = vgo.ImportPaths([]string{"ALL"}) } else { vgo.LoadBuildList() } if *modSync { // ImportPaths(ALL) already added missing modules. // Remove unused modules. - panic("TODO sync unused") + used := map[module.Version]bool{vgo.Target: true} + for _, pkg := range pkgs { + used[vgo.PackageModule(pkg)] = true + } + + inGoMod := make(map[string]bool) + for _, r := range vgo.ModFile().Require { + inGoMod[r.Mod.Path] = true + } + + var keep []module.Version + for _, m := range vgo.BuildList() { + if used[m] { + keep = append(keep, m) + } else if *modV && inGoMod[m.Path] { + fmt.Fprintf(os.Stderr, "unused %s\n", m.Path) + } + } + vgo.SetBuildList(keep) } vgo.WriteGoMod() if *modVendor {
diff --git a/vendor/cmd/go/internal/vgo/load.go b/vendor/cmd/go/internal/vgo/load.go index 30349cc..25cb0dd 100644 --- a/vendor/cmd/go/internal/vgo/load.go +++ b/vendor/cmd/go/internal/vgo/load.go
@@ -85,6 +85,12 @@ return buildList } +// SetBuildList sets the module build list. +// The caller is responsible for ensuring that the list is valid. +func SetBuildList(list []module.Version) { + buildList = list +} + // ImportMap returns the actual package import path // for an import path found in source code. // If the given import path does not appear in the source code
diff --git a/vendor/cmd/go/vgo_test.go b/vendor/cmd/go/vgo_test.go index b659c28..f140dc8 100644 --- a/vendor/cmd/go/vgo_test.go +++ b/vendor/cmd/go/vgo_test.go
@@ -363,6 +363,82 @@ tg.grepStderr("tcp.*nonexistent.rsc.io", "expected error for nonexistent.rsc.io") } +func TestVgoSync(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.makeTempdir() + + write := func(name, text string) { + name = tg.path(name) + dir := filepath.Dir(name) + tg.must(os.MkdirAll(dir, 0777)) + tg.must(ioutil.WriteFile(name, []byte(text), 0666)) + } + + write("m/go.mod", ` +module m + +require ( + x.1 v1.0.0 + y.1 v1.0.0 + w.1 v1.2.0 +) + +replace x.1 v1.0.0 => ../x +replace y.1 v1.0.0 => ../y +replace z.1 v1.1.0 => ../z +replace z.1 v1.2.0 => ../z +replace w.1 v1.1.0 => ../w +replace w.1 v1.2.0 => ../w +`) + write("m/m.go", ` +package m + +import _ "x.1" +import _ "z.1/sub" +`) + + write("w/go.mod", ` +module w +`) + write("w/w.go", ` +package w +`) + + write("x/go.mod", ` +module x +require w.1 v1.1.0 +require z.1 v1.1.0 +`) + write("x/x.go", ` +package x + +import _ "w.1" +`) + + write("y/go.mod", ` +module y +require z.1 v1.2.0 +`) + + write("z/go.mod", ` +module z +`) + write("z/sub/sub.go", ` +package sub +`) + + tg.cd(tg.path("m")) + tg.run("-vgo", "mod", "-sync", "-v") + tg.grepStderr(`^unused y.1`, "need y.1 unused") + tg.grepStderrNot(`^unused [^y]`, "only y.1 should be unused") + + tg.run("-vgo", "list", "-m") + tg.grepStdoutNot(`^y.1`, "y should be gone") + tg.grepStdout(`^w.1\s+v1.2.0`, "need w.1 to stay at v1.2.0") + tg.grepStdout(`^z.1\s+v1.2.0`, "need z.1 to stay at v1.2.0 even though y is gone") +} + func TestVgoVendor(t *testing.T) { tg := testgo(t) defer tg.cleanup()