Skip to content

Commit 764d0bb

Browse files
committed
cmd/go: diagnose 'go mod' in GOPATH/src better
People are (understandably) confused by creating go.mod files in GOPATH/src and then having the go command not use modules in those directories. We can't change that behavior (or we'll break non-module users of GOPATH) but we can force 'go mod' (including 'go mod -init') to fail loudly in that case. If this is not enough, the next step would be to print a warning every time the go command is run in a GOPATH/src directory with a go.mod but module mode hasn't triggered. But that will annoy all the non-module users. Hopefully anyone confused will eventually run a 'go mod' command of some kind, which will fail loudly. Fixes #26365. Change-Id: I8c5fe987fbc3f8d2eceb1138e6862a391ade150c Reviewed-on: https://go-review.googlesource.com/124708 Reviewed-by: Bryan C. Mills <[email protected]>
1 parent 50df4b3 commit 764d0bb

File tree

4 files changed

+102
-36
lines changed

4 files changed

+102
-36
lines changed

src/cmd/go/internal/modcmd/mod.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ func init() {
207207

208208
func runMod(cmd *base.Command, args []string) {
209209
if modload.Init(); !modload.Enabled() {
210-
base.Fatalf("go mod: cannot use outside module")
210+
base.Fatalf("go mod: cannot use outside module; see 'go help modules'")
211211
}
212212
if len(args) != 0 {
213213
base.Fatalf("go mod: mod takes no arguments")

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

Lines changed: 23 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import (
1717
"cmd/go/internal/module"
1818
"cmd/go/internal/mvs"
1919
"cmd/go/internal/search"
20-
"cmd/go/internal/str"
2120
"encoding/json"
2221
"fmt"
2322
"io/ioutil"
@@ -96,8 +95,8 @@ func Init() {
9695
}
9796

9897
// If this is testgo - the test binary during cmd/go tests -
99-
// then do not let it look for a go.mod unless GO111MODULE has an explicit setting.
100-
if base := filepath.Base(os.Args[0]); (base == "testgo" || base == "testgo.exe") && env == "" {
98+
// then do not let it look for a go.mod unless GO111MODULE has an explicit setting or this is 'go mod -init'.
99+
if base := filepath.Base(os.Args[0]); (base == "testgo" || base == "testgo.exe") && env == "" && !CmdModInit {
101100
return
102101
}
103102

@@ -134,25 +133,27 @@ func Init() {
134133
base.Fatalf("go: %v", err)
135134
}
136135

136+
inGOPATH := false
137+
for _, gopath := range filepath.SplitList(cfg.BuildContext.GOPATH) {
138+
if gopath == "" {
139+
continue
140+
}
141+
if search.InDir(cwd, filepath.Join(gopath, "src")) != "" {
142+
inGOPATH = true
143+
break
144+
}
145+
}
146+
if inGOPATH && !MustUseModules && cfg.CmdName == "mod" {
147+
base.Fatalf("go: modules disabled inside GOPATH/src by GO111MODULE=auto; see 'go help modules'")
148+
}
149+
137150
if CmdModInit {
138151
// Running 'go mod -init': go.mod will be created in current directory.
139152
ModRoot = cwd
140153
} else {
141-
inGOPATH := false
142-
for _, gopath := range filepath.SplitList(cfg.BuildContext.GOPATH) {
143-
if gopath == "" {
144-
continue
145-
}
146-
if str.HasFilePathPrefix(cwd, filepath.Join(gopath, "src")) {
147-
inGOPATH = true
148-
break
149-
}
150-
}
151-
if inGOPATH {
152-
if !MustUseModules {
153-
// No automatic enabling in GOPATH.
154-
return
155-
}
154+
if inGOPATH && !MustUseModules {
155+
// No automatic enabling in GOPATH.
156+
return
156157
}
157158
root, _ := FindModuleRoot(cwd, "", MustUseModules)
158159
if root == "" {
@@ -422,22 +423,12 @@ func FindModulePath(dir string) (string, error) {
422423
}
423424

424425
// Look for path in GOPATH.
425-
xdir, errdir := filepath.EvalSymlinks(dir)
426426
for _, gpdir := range filepath.SplitList(cfg.BuildContext.GOPATH) {
427-
xgpdir, errgpdir := filepath.EvalSymlinks(gpdir)
428-
src := filepath.Join(gpdir, "src") + string(filepath.Separator)
429-
xsrc := filepath.Join(xgpdir, "src") + string(filepath.Separator)
430-
if strings.HasPrefix(dir, src) {
431-
return filepath.ToSlash(dir[len(src):]), nil
432-
}
433-
if errdir == nil && strings.HasPrefix(xdir, src) {
434-
return filepath.ToSlash(xdir[len(src):]), nil
435-
}
436-
if errgpdir == nil && strings.HasPrefix(dir, xsrc) {
437-
return filepath.ToSlash(dir[len(xsrc):]), nil
427+
if gpdir == "" {
428+
continue
438429
}
439-
if errdir == nil && errgpdir == nil && strings.HasPrefix(xdir, xsrc) {
440-
return filepath.ToSlash(xdir[len(xsrc):]), nil
430+
if rel := search.InDir(dir, filepath.Join(gpdir, "src")); rel != "" && rel != "." {
431+
return filepath.ToSlash(rel), nil
441432
}
442433
}
443434

src/cmd/go/internal/search/search.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,3 +437,74 @@ func IsStandardImportPath(path string) bool {
437437
func IsRelativePath(pattern string) bool {
438438
return strings.HasPrefix(pattern, "./") || strings.HasPrefix(pattern, "../") || pattern == "." || pattern == ".."
439439
}
440+
441+
// InDir checks whether path is in the file tree rooted at dir.
442+
// If so, InDir returns an equivalent path relative to dir.
443+
// If not, InDir returns an empty string.
444+
// InDir makes some effort to succeed even in the presence of symbolic links.
445+
// TODO(rsc): Replace internal/test.inDir with a call to this function for Go 1.12.
446+
func InDir(path, dir string) string {
447+
if rel := inDirLex(path, dir); rel != "" {
448+
return rel
449+
}
450+
xpath, err := filepath.EvalSymlinks(path)
451+
if err != nil || xpath == path {
452+
xpath = ""
453+
} else {
454+
if rel := inDirLex(xpath, dir); rel != "" {
455+
return rel
456+
}
457+
}
458+
459+
xdir, err := filepath.EvalSymlinks(dir)
460+
if err == nil && xdir != dir {
461+
if rel := inDirLex(path, xdir); rel != "" {
462+
return rel
463+
}
464+
if xpath != "" {
465+
if rel := inDirLex(xpath, xdir); rel != "" {
466+
return rel
467+
}
468+
}
469+
}
470+
return ""
471+
}
472+
473+
// inDirLex is like inDir but only checks the lexical form of the file names.
474+
// It does not consider symbolic links.
475+
// TODO(rsc): This is a copy of str.HasFilePathPrefix, modified to
476+
// return the suffix. Most uses of str.HasFilePathPrefix should probably
477+
// be calling InDir instead.
478+
func inDirLex(path, dir string) string {
479+
pv := strings.ToUpper(filepath.VolumeName(path))
480+
dv := strings.ToUpper(filepath.VolumeName(dir))
481+
path = path[len(pv):]
482+
dir = dir[len(dv):]
483+
switch {
484+
default:
485+
return ""
486+
case pv != dv:
487+
return ""
488+
case len(path) == len(dir):
489+
if path == dir {
490+
return "."
491+
}
492+
return ""
493+
case dir == "":
494+
return path
495+
case len(path) > len(dir):
496+
if dir[len(dir)-1] == filepath.Separator {
497+
if path[:len(dir)] == dir {
498+
return path[len(dir):]
499+
}
500+
return ""
501+
}
502+
if path[len(dir)] == filepath.Separator && path[:len(dir)] == dir {
503+
if len(path) == len(dir)+1 {
504+
return "."
505+
}
506+
return path[len(dir)+1:]
507+
}
508+
return ""
509+
}
510+
}

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
env GO111MODULE=on
2-
31
# Derive module path from import comment.
4-
# TODO SHOULD NOT NEED ENV VAR YET
52
cd $WORK/x
63
exists x.go
74
go mod -init
@@ -13,6 +10,13 @@ addcrlf x.go
1310
go mod -init
1411
stderr 'module x'
1512

13+
# go mod should die in GOPATH if modules are not enabled for GOPATH
14+
cd $GOPATH/src/example.com/x/y
15+
! go mod -init
16+
stderr 'go: modules disabled inside GOPATH/src by GO111MODULE=auto; see ''go help modules'''
17+
18+
env GO111MODULE=on
19+
1620
# Derive module path from location inside GOPATH.
1721
cd $GOPATH/src/example.com/x/y
1822
go mod -init

0 commit comments

Comments
 (0)