Commit 764d0bb0 authored by Russ Cox's avatar Russ Cox

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/124708Reviewed-by: default avatarBryan C. Mills <bcmills@google.com>
parent 50df4b30
......@@ -207,7 +207,7 @@ func init() {
func runMod(cmd *base.Command, args []string) {
if modload.Init(); !modload.Enabled() {
base.Fatalf("go mod: cannot use outside module")
base.Fatalf("go mod: cannot use outside module; see 'go help modules'")
}
if len(args) != 0 {
base.Fatalf("go mod: mod takes no arguments")
......
......@@ -17,7 +17,6 @@ import (
"cmd/go/internal/module"
"cmd/go/internal/mvs"
"cmd/go/internal/search"
"cmd/go/internal/str"
"encoding/json"
"fmt"
"io/ioutil"
......@@ -96,8 +95,8 @@ func Init() {
}
// If this is testgo - the test binary during cmd/go tests -
// then do not let it look for a go.mod unless GO111MODULE has an explicit setting.
if base := filepath.Base(os.Args[0]); (base == "testgo" || base == "testgo.exe") && env == "" {
// then do not let it look for a go.mod unless GO111MODULE has an explicit setting or this is 'go mod -init'.
if base := filepath.Base(os.Args[0]); (base == "testgo" || base == "testgo.exe") && env == "" && !CmdModInit {
return
}
......@@ -134,26 +133,28 @@ func Init() {
base.Fatalf("go: %v", err)
}
if CmdModInit {
// Running 'go mod -init': go.mod will be created in current directory.
ModRoot = cwd
} else {
inGOPATH := false
for _, gopath := range filepath.SplitList(cfg.BuildContext.GOPATH) {
if gopath == "" {
continue
}
if str.HasFilePathPrefix(cwd, filepath.Join(gopath, "src")) {
if search.InDir(cwd, filepath.Join(gopath, "src")) != "" {
inGOPATH = true
break
}
}
if inGOPATH {
if !MustUseModules {
if inGOPATH && !MustUseModules && cfg.CmdName == "mod" {
base.Fatalf("go: modules disabled inside GOPATH/src by GO111MODULE=auto; see 'go help modules'")
}
if CmdModInit {
// Running 'go mod -init': go.mod will be created in current directory.
ModRoot = cwd
} else {
if inGOPATH && !MustUseModules {
// No automatic enabling in GOPATH.
return
}
}
root, _ := FindModuleRoot(cwd, "", MustUseModules)
if root == "" {
// If invoked as vgo, insist on a mod file.
......@@ -422,22 +423,12 @@ func FindModulePath(dir string) (string, error) {
}
// Look for path in GOPATH.
xdir, errdir := filepath.EvalSymlinks(dir)
for _, gpdir := range filepath.SplitList(cfg.BuildContext.GOPATH) {
xgpdir, errgpdir := filepath.EvalSymlinks(gpdir)
src := filepath.Join(gpdir, "src") + string(filepath.Separator)
xsrc := filepath.Join(xgpdir, "src") + string(filepath.Separator)
if strings.HasPrefix(dir, src) {
return filepath.ToSlash(dir[len(src):]), nil
}
if errdir == nil && strings.HasPrefix(xdir, src) {
return filepath.ToSlash(xdir[len(src):]), nil
}
if errgpdir == nil && strings.HasPrefix(dir, xsrc) {
return filepath.ToSlash(dir[len(xsrc):]), nil
if gpdir == "" {
continue
}
if errdir == nil && errgpdir == nil && strings.HasPrefix(xdir, xsrc) {
return filepath.ToSlash(xdir[len(xsrc):]), nil
if rel := search.InDir(dir, filepath.Join(gpdir, "src")); rel != "" && rel != "." {
return filepath.ToSlash(rel), nil
}
}
......
......@@ -437,3 +437,74 @@ func IsStandardImportPath(path string) bool {
func IsRelativePath(pattern string) bool {
return strings.HasPrefix(pattern, "./") || strings.HasPrefix(pattern, "../") || pattern == "." || pattern == ".."
}
// InDir checks whether path is in the file tree rooted at dir.
// If so, InDir returns an equivalent path relative to dir.
// If not, InDir returns an empty string.
// InDir makes some effort to succeed even in the presence of symbolic links.
// TODO(rsc): Replace internal/test.inDir with a call to this function for Go 1.12.
func InDir(path, dir string) string {
if rel := inDirLex(path, dir); rel != "" {
return rel
}
xpath, err := filepath.EvalSymlinks(path)
if err != nil || xpath == path {
xpath = ""
} else {
if rel := inDirLex(xpath, dir); rel != "" {
return rel
}
}
xdir, err := filepath.EvalSymlinks(dir)
if err == nil && xdir != dir {
if rel := inDirLex(path, xdir); rel != "" {
return rel
}
if xpath != "" {
if rel := inDirLex(xpath, xdir); rel != "" {
return rel
}
}
}
return ""
}
// inDirLex is like inDir but only checks the lexical form of the file names.
// It does not consider symbolic links.
// TODO(rsc): This is a copy of str.HasFilePathPrefix, modified to
// return the suffix. Most uses of str.HasFilePathPrefix should probably
// be calling InDir instead.
func inDirLex(path, dir string) string {
pv := strings.ToUpper(filepath.VolumeName(path))
dv := strings.ToUpper(filepath.VolumeName(dir))
path = path[len(pv):]
dir = dir[len(dv):]
switch {
default:
return ""
case pv != dv:
return ""
case len(path) == len(dir):
if path == dir {
return "."
}
return ""
case dir == "":
return path
case len(path) > len(dir):
if dir[len(dir)-1] == filepath.Separator {
if path[:len(dir)] == dir {
return path[len(dir):]
}
return ""
}
if path[len(dir)] == filepath.Separator && path[:len(dir)] == dir {
if len(path) == len(dir)+1 {
return "."
}
return path[len(dir)+1:]
}
return ""
}
}
env GO111MODULE=on
# Derive module path from import comment.
# TODO SHOULD NOT NEED ENV VAR YET
cd $WORK/x
exists x.go
go mod -init
......@@ -13,6 +10,13 @@ addcrlf x.go
go mod -init
stderr 'module x'
# go mod should die in GOPATH if modules are not enabled for GOPATH
cd $GOPATH/src/example.com/x/y
! go mod -init
stderr 'go: modules disabled inside GOPATH/src by GO111MODULE=auto; see ''go help modules'''
env GO111MODULE=on
# Derive module path from location inside GOPATH.
cd $GOPATH/src/example.com/x/y
go mod -init
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment