Skip to content

Commit 65c2069

Browse files
author
Bryan C. Mills
committed
cmd/go: only generate a go.mod file during 'go mod init'
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 <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Jay Conrod <[email protected]>
1 parent 4c89a10 commit 65c2069

14 files changed

+162
-80
lines changed

src/cmd/go/internal/modload/init.go

+41-36
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ func Init() {
154154
die() // Don't init a module that we're just going to ignore.
155155
}
156156
// No automatic enabling in GOPATH.
157-
if root, _ := FindModuleRoot(cwd, "", false); root != "" {
157+
if root := findModuleRoot(cwd); root != "" {
158158
cfg.GoModInGOPATH = filepath.Join(root, "go.mod")
159159
}
160160
return
@@ -164,7 +164,7 @@ func Init() {
164164
// Running 'go mod init': go.mod will be created in current directory.
165165
modRoot = cwd
166166
} else {
167-
modRoot, _ = FindModuleRoot(cwd, "", MustUseModules)
167+
modRoot = findModuleRoot(cwd)
168168
if modRoot == "" {
169169
if !MustUseModules {
170170
// GO111MODULE is 'auto' (or unset), and we can't find a module root.
@@ -302,6 +302,19 @@ func die() {
302302
if inGOPATH && !MustUseModules {
303303
base.Fatalf("go: modules disabled inside GOPATH/src by GO111MODULE=auto; see 'go help modules'")
304304
}
305+
if cwd != "" {
306+
if dir, name := findAltConfig(cwd); dir != "" {
307+
rel, err := filepath.Rel(cwd, dir)
308+
if err != nil {
309+
rel = dir
310+
}
311+
cdCmd := ""
312+
if rel != "." {
313+
cdCmd = fmt.Sprintf("cd %s && ", rel)
314+
}
315+
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)
316+
}
317+
}
305318
base.Fatalf("go: cannot find main module; see 'go help modules'")
306319
}
307320

@@ -330,12 +343,6 @@ func InitMod() {
330343
gomod := filepath.Join(modRoot, "go.mod")
331344
data, err := ioutil.ReadFile(gomod)
332345
if err != nil {
333-
if os.IsNotExist(err) {
334-
legacyModInit()
335-
modFileToBuildList()
336-
WriteGoMod()
337-
return
338-
}
339346
base.Fatalf("go: %v", err)
340347
}
341348

@@ -349,7 +356,7 @@ func InitMod() {
349356

350357
if len(f.Syntax.Stmt) == 0 || f.Module == nil {
351358
// Empty mod file. Must add module path.
352-
path, err := FindModulePath(modRoot)
359+
path, err := findModulePath(modRoot)
353360
if err != nil {
354361
base.Fatalf("go: %v", err)
355362
}
@@ -387,7 +394,7 @@ func Allowed(m module.Version) bool {
387394

388395
func legacyModInit() {
389396
if modFile == nil {
390-
path, err := FindModulePath(modRoot)
397+
path, err := findModulePath(modRoot)
391398
if err != nil {
392399
base.Fatalf("go: %v", err)
393400
}
@@ -454,57 +461,55 @@ var altConfigs = []string{
454461
".git/config",
455462
}
456463

457-
// Exported only for testing.
458-
func FindModuleRoot(dir, limit string, legacyConfigOK bool) (root, file string) {
464+
func findModuleRoot(dir string) (root string) {
459465
dir = filepath.Clean(dir)
460-
dir1 := dir
461-
limit = filepath.Clean(limit)
462466

463467
// Look for enclosing go.mod.
464468
for {
465469
if fi, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil && !fi.IsDir() {
466-
return dir, "go.mod"
467-
}
468-
if dir == limit {
469-
break
470+
return dir
470471
}
471472
d := filepath.Dir(dir)
472473
if d == dir {
473474
break
474475
}
475476
dir = d
476477
}
478+
return ""
479+
}
477480

478-
// Failing that, look for enclosing alternate version config.
479-
if legacyConfigOK {
480-
dir = dir1
481-
for {
482-
for _, name := range altConfigs {
483-
if fi, err := os.Stat(filepath.Join(dir, name)); err == nil && !fi.IsDir() {
484-
return dir, name
481+
func findAltConfig(dir string) (root, name string) {
482+
dir = filepath.Clean(dir)
483+
for {
484+
for _, name := range altConfigs {
485+
if fi, err := os.Stat(filepath.Join(dir, name)); err == nil && !fi.IsDir() {
486+
if rel := search.InDir(dir, cfg.BuildContext.GOROOT); rel == "." {
487+
// Don't suggest creating a module from $GOROOT/.git/config.
488+
return "", ""
485489
}
490+
return dir, name
486491
}
487-
if dir == limit {
488-
break
489-
}
490-
d := filepath.Dir(dir)
491-
if d == dir {
492-
break
493-
}
494-
dir = d
495492
}
493+
d := filepath.Dir(dir)
494+
if d == dir {
495+
break
496+
}
497+
dir = d
496498
}
497-
498499
return "", ""
499500
}
500501

501-
// Exported only for testing.
502-
func FindModulePath(dir string) (string, error) {
502+
func findModulePath(dir string) (string, error) {
503503
if CmdModModule != "" {
504504
// Running go mod init x/y/z; return x/y/z.
505505
return CmdModModule, nil
506506
}
507507

508+
// TODO(bcmills): once we have located a plausible module path, we should
509+
// query version control (if available) to verify that it matches the major
510+
// version of the most recent tag.
511+
// See https://golang.org/issue/29433 and https://golang.org/issue/27009.
512+
508513
// Cast about for import comments,
509514
// first in top-level directory, then in subdirectories.
510515
list, _ := ioutil.ReadDir(dir)

src/cmd/go/internal/modload/init_test.go

-42
This file was deleted.

src/cmd/go/internal/modload/load.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ func ImportPaths(patterns []string) []*search.Match {
111111
} else {
112112
pkg = Target.Path + suffix
113113
}
114-
} else if sub := search.InDir(dir, cfg.GOROOTsrc); sub != "" && !strings.Contains(sub, "@") {
114+
} else if sub := search.InDir(dir, cfg.GOROOTsrc); sub != "" && sub != "." && !strings.Contains(sub, "@") {
115115
pkg = filepath.ToSlash(sub)
116116
} else if path := pathInModuleCache(dir); path != "" {
117117
pkg = path
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,31 @@
11
env GO111MODULE=on
22

3+
# We should not create a go.mod file unless the user ran 'go mod init' explicitly.
4+
# However, we should suggest 'go mod init' if we can find an alternate config file.
35
cd $WORK/test/x
6+
! go list .
7+
stderr 'found Gopkg.lock in .*[/\\]test'
8+
stderr '\s*cd \.\. && go mod init'
9+
10+
# The command we suggested should succeed.
11+
cd ..
12+
go mod init
413
go list -m all
514
stdout '^m$'
615

16+
# In Plan 9, directories are automatically created in /n.
17+
# For example, /n/Gopkg.lock always exists, but it's a directory.
18+
# Test that we ignore directories when trying to find alternate config files.
19+
cd $WORK/gopkgdir/x
20+
! go list .
21+
stderr 'cannot find main module'
22+
! stderr 'Gopkg.lock'
23+
! stderr 'go mod init'
24+
725
-- $WORK/test/Gopkg.lock --
826
-- $WORK/test/x/x.go --
927
package x // import "m/x"
28+
-- $WORK/gopkgdir/Gopkg.lock/README.txt --
29+
../Gopkg.lock is a directory, not a file.
30+
-- $WORK/gopkgdir/x/x.go --
31+
package x // import "m/x"
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,23 @@
11
env GO111MODULE=on
22

3-
# detect root of module tree as root of enclosing git repo
3+
# We should not create a go.mod file unless the user ran 'go mod init' explicitly.
4+
# However, we should suggest 'go mod init' if we can find an alternate config file.
45
cd $WORK/test/x
6+
! go list .
7+
stderr 'found .git/config in .*[/\\]test'
8+
stderr '\s*cd \.\. && go mod init'
9+
10+
# The command we suggested should succeed.
11+
cd ..
12+
go mod init
513
go list -m all
614
stdout '^m$'
715

16+
# We should not suggest creating a go.mod file in $GOROOT, even though there may be a .git/config there.
17+
cd $GOROOT
18+
! go list .
19+
! stderr 'go mod init'
20+
821
-- $WORK/test/.git/config --
922
-- $WORK/test/x/x.go --
1023
package x // import "m/x"

src/cmd/go/testdata/script/mod_convert_glide.txt

+9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
env GO111MODULE=on
22

3+
# We should not create a go.mod file unless the user ran 'go mod init' explicitly.
4+
# However, we should suggest 'go mod init' if we can find an alternate config file.
35
cd $WORK/test/x
6+
! go list .
7+
stderr 'found glide.lock in .*[/\\]test'
8+
stderr '\s*cd \.\. && go mod init'
9+
10+
# The command we suggested should succeed.
11+
cd ..
12+
go mod init
413
go list -m all
514
stdout '^m$'
615

src/cmd/go/testdata/script/mod_convert_glockfile.txt

+9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
env GO111MODULE=on
22

3+
# We should not create a go.mod file unless the user ran 'go mod init' explicitly.
4+
# However, we should suggest 'go mod init' if we can find an alternate config file.
35
cd $WORK/test/x
6+
! go list .
7+
stderr 'found GLOCKFILE in .*[/\\]test'
8+
stderr '\s*cd \.\. && go mod init'
9+
10+
# The command we suggested should succeed.
11+
cd ..
12+
go mod init
413
go list -m all
514
stdout '^m$'
615

src/cmd/go/testdata/script/mod_convert_godeps.txt

+9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
env GO111MODULE=on
22

3+
# We should not create a go.mod file unless the user ran 'go mod init' explicitly.
4+
# However, we should suggest 'go mod init' if we can find an alternate config file.
35
cd $WORK/test/x
6+
! go list .
7+
stderr 'found Godeps/Godeps.json in .*[/\\]test'
8+
stderr '\s*cd \.\. && go mod init'
9+
10+
# The command we suggested should succeed.
11+
cd ..
12+
go mod init
413
go list -m all
514
stdout '^m$'
615

src/cmd/go/testdata/script/mod_convert_tsv.txt

+9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
env GO111MODULE=on
22

3+
# We should not create a go.mod file unless the user ran 'go mod init' explicitly.
4+
# However, we should suggest 'go mod init' if we can find an alternate config file.
35
cd $WORK/test/x
6+
! go list .
7+
stderr 'found dependencies.tsv in .*[/\\]test'
8+
stderr '\s*cd \.\. && go mod init'
9+
10+
# The command we suggested should succeed.
11+
cd ..
12+
go mod init
413
go list -m all
514
stdout '^m$'
615

src/cmd/go/testdata/script/mod_convert_vendor_conf.txt

+9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
env GO111MODULE=on
22

3+
# We should not create a go.mod file unless the user ran 'go mod init' explicitly.
4+
# However, we should suggest 'go mod init' if we can find an alternate config file.
35
cd $WORK/test/x
6+
! go list .
7+
stderr 'found vendor.conf in .*[/\\]test'
8+
stderr '\s*cd \.\. && go mod init'
9+
10+
# The command we suggested should succeed.
11+
cd ..
12+
go mod init
413
go list -m all
514
stdout '^m$'
615

src/cmd/go/testdata/script/mod_convert_vendor_json.txt

+9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
env GO111MODULE=on
22

3+
# We should not create a go.mod file unless the user ran 'go mod init' explicitly.
4+
# However, we should suggest 'go mod init' if we can find an alternate config file.
35
cd $WORK/test/x
6+
! go list .
7+
stderr 'found vendor/vendor.json in .*[/\\]test'
8+
stderr '\s*cd \.\. && go mod init'
9+
10+
# The command we suggested should succeed.
11+
cd ..
12+
go mod init
413
go list -m all
514
stdout '^m$'
615

src/cmd/go/testdata/script/mod_convert_vendor_manifest.txt

+9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
env GO111MODULE=on
22

3+
# We should not create a go.mod file unless the user ran 'go mod init' explicitly.
4+
# However, we should suggest 'go mod init' if we can find an alternate config file.
35
cd $WORK/test/x
6+
! go list .
7+
stderr 'found vendor/manifest in .*[/\\]test'
8+
stderr '\s*cd \.\. && go mod init'
9+
10+
# The command we suggested should succeed.
11+
cd ..
12+
go mod init
413
go list -m all
514
stdout '^m$'
615

src/cmd/go/testdata/script/mod_convert_vendor_yml.txt

+9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
env GO111MODULE=on
22

3+
# We should not create a go.mod file unless the user ran 'go mod init' explicitly.
4+
# However, we should suggest 'go mod init' if we can find an alternate config file.
35
cd $WORK/test/x
6+
! go list .
7+
stderr 'found vendor.yml in .*[/\\]test'
8+
stderr '\s*cd \.\. && go mod init'
9+
10+
# The command we suggested should succeed.
11+
cd ..
12+
go mod init
413
go list -m all
514
stdout '^m$'
615

0 commit comments

Comments
 (0)