]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/go: only generate a go.mod file during 'go mod init'
authorBryan C. Mills <bcmills@google.com>
Wed, 13 Feb 2019 22:37:50 +0000 (17:37 -0500)
committerBryan C. Mills <bcmills@google.com>
Fri, 15 Feb 2019 17:32:07 +0000 (17:32 +0000)
In the general case, we do not know the correct module path for a new
module unless we have checked its VCS tags for a major version. If we
do not know the correct path, then we should not synthesize a go.mod
file automatically from it.

On the other hand, we don't want to run VCS commands in the working
directory without an explicit request by the user to do so: 'go mod
init' can reasonably invoke a VCS command, but 'go build' should not.

Therefore, we should only create a go.mod file during 'go mod init'.

This change removes the previous behavior of synthesizing a file
automatically, and instead suggests a command that the user can opt to
run explicitly.

Updates #29433
Updates #27009
Updates #30228

Change-Id: I8c4554969db17156e97428df220b129a4d361040
Reviewed-on: https://go-review.googlesource.com/c/162699
Run-TryBot: Bryan C. Mills <bcmills@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Jay Conrod <jayconrod@google.com>
14 files changed:
src/cmd/go/internal/modload/init.go
src/cmd/go/internal/modload/init_test.go [deleted file]
src/cmd/go/internal/modload/load.go
src/cmd/go/testdata/script/mod_convert_dep.txt
src/cmd/go/testdata/script/mod_convert_git.txt
src/cmd/go/testdata/script/mod_convert_glide.txt
src/cmd/go/testdata/script/mod_convert_glockfile.txt
src/cmd/go/testdata/script/mod_convert_godeps.txt
src/cmd/go/testdata/script/mod_convert_tsv.txt
src/cmd/go/testdata/script/mod_convert_vendor_conf.txt
src/cmd/go/testdata/script/mod_convert_vendor_json.txt
src/cmd/go/testdata/script/mod_convert_vendor_manifest.txt
src/cmd/go/testdata/script/mod_convert_vendor_yml.txt
src/cmd/go/testdata/script/mod_find.txt

index 22d14ccce78b241d780ee526754bd3ca8510144a..a0514d425e2bca07c0c28006feaf89a4754500f3 100644 (file)
@@ -154,7 +154,7 @@ func Init() {
                        die() // Don't init a module that we're just going to ignore.
                }
                // No automatic enabling in GOPATH.
-               if root, _ := FindModuleRoot(cwd, "", false); root != "" {
+               if root := findModuleRoot(cwd); root != "" {
                        cfg.GoModInGOPATH = filepath.Join(root, "go.mod")
                }
                return
@@ -164,7 +164,7 @@ func Init() {
                // Running 'go mod init': go.mod will be created in current directory.
                modRoot = cwd
        } else {
-               modRoot, _ = FindModuleRoot(cwd, "", MustUseModules)
+               modRoot = findModuleRoot(cwd)
                if modRoot == "" {
                        if !MustUseModules {
                                // GO111MODULE is 'auto' (or unset), and we can't find a module root.
@@ -302,6 +302,19 @@ func die() {
        if inGOPATH && !MustUseModules {
                base.Fatalf("go: modules disabled inside GOPATH/src by GO111MODULE=auto; see 'go help modules'")
        }
+       if cwd != "" {
+               if dir, name := findAltConfig(cwd); dir != "" {
+                       rel, err := filepath.Rel(cwd, dir)
+                       if err != nil {
+                               rel = dir
+                       }
+                       cdCmd := ""
+                       if rel != "." {
+                               cdCmd = fmt.Sprintf("cd %s && ", rel)
+                       }
+                       base.Fatalf("go: cannot find main module, but found %s in %s\n\tto create a module there, run:\n\t%sgo mod init", name, dir, cdCmd)
+               }
+       }
        base.Fatalf("go: cannot find main module; see 'go help modules'")
 }
 
@@ -330,12 +343,6 @@ func InitMod() {
        gomod := filepath.Join(modRoot, "go.mod")
        data, err := ioutil.ReadFile(gomod)
        if err != nil {
-               if os.IsNotExist(err) {
-                       legacyModInit()
-                       modFileToBuildList()
-                       WriteGoMod()
-                       return
-               }
                base.Fatalf("go: %v", err)
        }
 
@@ -349,7 +356,7 @@ func InitMod() {
 
        if len(f.Syntax.Stmt) == 0 || f.Module == nil {
                // Empty mod file. Must add module path.
-               path, err := FindModulePath(modRoot)
+               path, err := findModulePath(modRoot)
                if err != nil {
                        base.Fatalf("go: %v", err)
                }
@@ -387,7 +394,7 @@ func Allowed(m module.Version) bool {
 
 func legacyModInit() {
        if modFile == nil {
-               path, err := FindModulePath(modRoot)
+               path, err := findModulePath(modRoot)
                if err != nil {
                        base.Fatalf("go: %v", err)
                }
@@ -454,19 +461,13 @@ var altConfigs = []string{
        ".git/config",
 }
 
-// Exported only for testing.
-func FindModuleRoot(dir, limit string, legacyConfigOK bool) (root, file string) {
+func findModuleRoot(dir string) (root string) {
        dir = filepath.Clean(dir)
-       dir1 := dir
-       limit = filepath.Clean(limit)
 
        // Look for enclosing go.mod.
        for {
                if fi, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil && !fi.IsDir() {
-                       return dir, "go.mod"
-               }
-               if dir == limit {
-                       break
+                       return dir
                }
                d := filepath.Dir(dir)
                if d == dir {
@@ -474,37 +475,41 @@ func FindModuleRoot(dir, limit string, legacyConfigOK bool) (root, file string)
                }
                dir = d
        }
+       return ""
+}
 
-       // Failing that, look for enclosing alternate version config.
-       if legacyConfigOK {
-               dir = dir1
-               for {
-                       for _, name := range altConfigs {
-                               if fi, err := os.Stat(filepath.Join(dir, name)); err == nil && !fi.IsDir() {
-                                       return dir, name
+func findAltConfig(dir string) (root, name string) {
+       dir = filepath.Clean(dir)
+       for {
+               for _, name := range altConfigs {
+                       if fi, err := os.Stat(filepath.Join(dir, name)); err == nil && !fi.IsDir() {
+                               if rel := search.InDir(dir, cfg.BuildContext.GOROOT); rel == "." {
+                                       // Don't suggest creating a module from $GOROOT/.git/config.
+                                       return "", ""
                                }
+                               return dir, name
                        }
-                       if dir == limit {
-                               break
-                       }
-                       d := filepath.Dir(dir)
-                       if d == dir {
-                               break
-                       }
-                       dir = d
                }
+               d := filepath.Dir(dir)
+               if d == dir {
+                       break
+               }
+               dir = d
        }
-
        return "", ""
 }
 
-// Exported only for testing.
-func FindModulePath(dir string) (string, error) {
+func findModulePath(dir string) (string, error) {
        if CmdModModule != "" {
                // Running go mod init x/y/z; return x/y/z.
                return CmdModModule, nil
        }
 
+       // TODO(bcmills): once we have located a plausible module path, we should
+       // query version control (if available) to verify that it matches the major
+       // version of the most recent tag.
+       // See https://golang.org/issue/29433 and https://golang.org/issue/27009.
+
        // Cast about for import comments,
        // first in top-level directory, then in subdirectories.
        list, _ := ioutil.ReadDir(dir)
diff --git a/src/cmd/go/internal/modload/init_test.go b/src/cmd/go/internal/modload/init_test.go
deleted file mode 100644 (file)
index 2df9d8a..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-// 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 modload
-
-import (
-       "io/ioutil"
-       "os"
-       "path/filepath"
-       "testing"
-)
-
-func TestFindModuleRootIgnoreDir(t *testing.T) {
-       // In Plan 9, directories are automatically created in /n.
-       // For example, /n/go.mod always exist, but it's a directory.
-       // Test that we ignore directories when trying to find go.mod and other config files.
-
-       dir, err := ioutil.TempDir("", "gotest")
-       if err != nil {
-               t.Fatalf("failed to create temporary directory: %v", err)
-       }
-       defer os.RemoveAll(dir)
-       if err := os.Mkdir(filepath.Join(dir, "go.mod"), os.ModeDir|0755); err != nil {
-               t.Fatalf("Mkdir failed: %v", err)
-       }
-       for _, name := range altConfigs {
-               if err := os.MkdirAll(filepath.Join(dir, name), os.ModeDir|0755); err != nil {
-                       t.Fatalf("MkdirAll failed: %v", err)
-               }
-       }
-       p := filepath.Join(dir, "example")
-       if err := os.Mkdir(p, os.ModeDir|0755); err != nil {
-               t.Fatalf("Mkdir failed: %v", err)
-       }
-       if root, _ := FindModuleRoot(p, "", false); root != "" {
-               t.Errorf("FindModuleRoot(%q, \"\", false): %q, want empty string", p, root)
-       }
-       if root, _ := FindModuleRoot(p, "", true); root != "" {
-               t.Errorf("FindModuleRoot(%q, \"\", true): %q, want empty string", p, root)
-       }
-}
index 5bb943dd6decb88f5f24c6c8d59c3614055c9a25..6d6c037af21b7bd4c909f994c8604ee9184d6ce2 100644 (file)
@@ -111,7 +111,7 @@ func ImportPaths(patterns []string) []*search.Match {
                                                } else {
                                                        pkg = Target.Path + suffix
                                                }
-                                       } else if sub := search.InDir(dir, cfg.GOROOTsrc); sub != "" && !strings.Contains(sub, "@") {
+                                       } else if sub := search.InDir(dir, cfg.GOROOTsrc); sub != "" && sub != "." && !strings.Contains(sub, "@") {
                                                pkg = filepath.ToSlash(sub)
                                        } else if path := pathInModuleCache(dir); path != "" {
                                                pkg = path
index cc1083bcba2b3706d5a537195f70f70a8a94276a..267c90eb3ce07daad555058fca144dddf6e46726 100644 (file)
@@ -1,9 +1,31 @@
 env GO111MODULE=on
 
+# We should not create a go.mod file unless the user ran 'go mod init' explicitly.
+# However, we should suggest 'go mod init' if we can find an alternate config file.
 cd $WORK/test/x
+! go list .
+stderr 'found Gopkg.lock in .*[/\\]test'
+stderr '\s*cd \.\. && go mod init'
+
+# The command we suggested should succeed.
+cd ..
+go mod init
 go list -m all
 stdout '^m$'
 
+# In Plan 9, directories are automatically created in /n.
+# For example, /n/Gopkg.lock always exists, but it's a directory.
+# Test that we ignore directories when trying to find alternate config files.
+cd $WORK/gopkgdir/x
+! go list .
+stderr 'cannot find main module'
+! stderr 'Gopkg.lock'
+! stderr 'go mod init'
+
 -- $WORK/test/Gopkg.lock --
 -- $WORK/test/x/x.go --
 package x // import "m/x"
+-- $WORK/gopkgdir/Gopkg.lock/README.txt --
+../Gopkg.lock is a directory, not a file.
+-- $WORK/gopkgdir/x/x.go --
+package x // import "m/x"
index 5ef534a8f886c7ae2a59b34766b264aebeeff413..ece505a7ba2c1c946428b7d7c9094ce39a181d51 100644 (file)
@@ -1,10 +1,23 @@
 env GO111MODULE=on
 
-# detect root of module tree as root of enclosing git repo
+# We should not create a go.mod file unless the user ran 'go mod init' explicitly.
+# However, we should suggest 'go mod init' if we can find an alternate config file.
 cd $WORK/test/x
+! go list .
+stderr 'found .git/config in .*[/\\]test'
+stderr '\s*cd \.\. && go mod init'
+
+# The command we suggested should succeed.
+cd ..
+go mod init
 go list -m all
 stdout '^m$'
 
+# We should not suggest creating a go.mod file in $GOROOT, even though there may be a .git/config there.
+cd $GOROOT
+! go list .
+! stderr 'go mod init'
+
 -- $WORK/test/.git/config --
 -- $WORK/test/x/x.go --
 package x // import "m/x"
index 50460bbf365ab306a5d6e5a3f31baf6fcb39eaa4..9f1fff51bf44643db8320aa90cde261a0318ccb6 100644 (file)
@@ -1,6 +1,15 @@
 env GO111MODULE=on
 
+# We should not create a go.mod file unless the user ran 'go mod init' explicitly.
+# However, we should suggest 'go mod init' if we can find an alternate config file.
 cd $WORK/test/x
+! go list .
+stderr 'found glide.lock in .*[/\\]test'
+stderr '\s*cd \.\. && go mod init'
+
+# The command we suggested should succeed.
+cd ..
+go mod init
 go list -m all
 stdout '^m$'
 
index 4d9aaffab50c73c45eb1edd4d669a1c16b0d366e..6aa07948883caa446d982ab217a3c802f1b364a1 100644 (file)
@@ -1,6 +1,15 @@
 env GO111MODULE=on
 
+# We should not create a go.mod file unless the user ran 'go mod init' explicitly.
+# However, we should suggest 'go mod init' if we can find an alternate config file.
 cd $WORK/test/x
+! go list .
+stderr 'found GLOCKFILE in .*[/\\]test'
+stderr '\s*cd \.\. && go mod init'
+
+# The command we suggested should succeed.
+cd ..
+go mod init
 go list -m all
 stdout '^m$'
 
index 61fbab112409b20de0ab191468057d3b87e5749d..da7b6c1059473735437a55726a7f114e2eaa490a 100644 (file)
@@ -1,6 +1,15 @@
 env GO111MODULE=on
 
+# We should not create a go.mod file unless the user ran 'go mod init' explicitly.
+# However, we should suggest 'go mod init' if we can find an alternate config file.
 cd $WORK/test/x
+! go list .
+stderr 'found Godeps/Godeps.json in .*[/\\]test'
+stderr '\s*cd \.\. && go mod init'
+
+# The command we suggested should succeed.
+cd ..
+go mod init
 go list -m all
 stdout '^m$'
 
index 5b82d85d653c7f9dfb5558cbd53f76e8833b16cd..6015ac8754338c2bc61838825efb3046556c5160 100644 (file)
@@ -1,6 +1,15 @@
 env GO111MODULE=on
 
+# We should not create a go.mod file unless the user ran 'go mod init' explicitly.
+# However, we should suggest 'go mod init' if we can find an alternate config file.
 cd $WORK/test/x
+! go list .
+stderr 'found dependencies.tsv in .*[/\\]test'
+stderr '\s*cd \.\. && go mod init'
+
+# The command we suggested should succeed.
+cd ..
+go mod init
 go list -m all
 stdout '^m$'
 
index b45d3b69fe2243de77cbb67dea484e142571bc41..57ec4191a451e00ff6ddea2124490f31996efc7e 100644 (file)
@@ -1,6 +1,15 @@
 env GO111MODULE=on
 
+# We should not create a go.mod file unless the user ran 'go mod init' explicitly.
+# However, we should suggest 'go mod init' if we can find an alternate config file.
 cd $WORK/test/x
+! go list .
+stderr 'found vendor.conf in .*[/\\]test'
+stderr '\s*cd \.\. && go mod init'
+
+# The command we suggested should succeed.
+cd ..
+go mod init
 go list -m all
 stdout '^m$'
 
index cb6e5fee15fed8f60cb9331db1d29cc5a9484ddd..47d111d4c13b32965eb993d92d43805160aa59ea 100644 (file)
@@ -1,6 +1,15 @@
 env GO111MODULE=on
 
+# We should not create a go.mod file unless the user ran 'go mod init' explicitly.
+# However, we should suggest 'go mod init' if we can find an alternate config file.
 cd $WORK/test/x
+! go list .
+stderr 'found vendor/vendor.json in .*[/\\]test'
+stderr '\s*cd \.\. && go mod init'
+
+# The command we suggested should succeed.
+cd ..
+go mod init
 go list -m all
 stdout '^m$'
 
index bcf185136ba8dc723ffe82ecb273f8ca2ed65935..68edb9dc292e8a953e00cae65a05e6ee92939205 100644 (file)
@@ -1,6 +1,15 @@
 env GO111MODULE=on
 
+# We should not create a go.mod file unless the user ran 'go mod init' explicitly.
+# However, we should suggest 'go mod init' if we can find an alternate config file.
 cd $WORK/test/x
+! go list .
+stderr 'found vendor/manifest in .*[/\\]test'
+stderr '\s*cd \.\. && go mod init'
+
+# The command we suggested should succeed.
+cd ..
+go mod init
 go list -m all
 stdout '^m$'
 
index 0cd245bace1f729997f992a7971b5b9d5ffdc812..4ed140a25a2fa4b7ffab170489728d3a888bb2c8 100644 (file)
@@ -1,6 +1,15 @@
 env GO111MODULE=on
 
+# We should not create a go.mod file unless the user ran 'go mod init' explicitly.
+# However, we should suggest 'go mod init' if we can find an alternate config file.
 cd $WORK/test/x
+! go list .
+stderr 'found vendor.yml in .*[/\\]test'
+stderr '\s*cd \.\. && go mod init'
+
+# The command we suggested should succeed.
+cd ..
+go mod init
 go list -m all
 stdout '^m$'
 
index f4ac8d01f563bbfce37856212274a68d96f866d0..eb7f974b3b7bbcca6baef53e8ed3bd80cbccf617 100644 (file)
@@ -43,6 +43,13 @@ go mod init
 stderr 'empty'
 rm go.mod
 
+# In Plan 9, directories are automatically created in /n.
+# For example, /n/go.mod always exist, but it's a directory.
+# Test that we ignore directories when trying to find go.mod.
+cd $WORK/gomoddir
+! go list .
+stderr 'cannot find main module'
+
 [!symlink] stop
 
 # gplink1/src/empty where gopathlink -> GOPATH
@@ -89,3 +96,8 @@ package y
 package z
 -- $GOPATH/src/example.com/x/y/z/Godeps/Godeps.json --
 {"ImportPath": "unexpected.com/z"}
+
+-- $WORK/gomoddir/go.mod/README.txt --
+../go.mod is a directory, not a file.
+-- $WORK/gomoddir/p.go --
+package p