Commit 57d06fff authored by Russ Cox's avatar Russ Cox

cmd/go, go/build: better defenses against GOPATH=GOROOT

Fixes #18863.

Change-Id: I0723563cd23728b0d43ebcc25979bf8d21e2a72c
Reviewed-on: https://go-review.googlesource.com/36427
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: 's avatarIan Lance Taylor <iant@golang.org>
parent 4af6b81d
...@@ -1683,173 +1683,111 @@ func homeEnvName() string { ...@@ -1683,173 +1683,111 @@ func homeEnvName() string {
} }
} }
// Test go env missing GOPATH shows default. func TestDefaultGOPATH(t *testing.T) {
func TestMissingGOPATHEnvShowsDefault(t *testing.T) {
tg := testgo(t) tg := testgo(t)
defer tg.cleanup() defer tg.cleanup()
tg.parallel() tg.parallel()
tg.setenv("GOPATH", "") tg.tempDir("home/go")
tg.run("env", "GOPATH") tg.setenv(homeEnvName(), tg.path("home"))
want := filepath.Join(os.Getenv(homeEnvName()), "go")
got := strings.TrimSpace(tg.getStdout())
if got != want {
t.Errorf("got %q; want %q", got, want)
}
}
// Test go get missing GOPATH causes go get to warn if directory doesn't exist.
func TestMissingGOPATHGetWarnsIfNotExists(t *testing.T) {
testenv.MustHaveExternalNetwork(t)
if _, err := exec.LookPath("git"); err != nil { tg.run("env", "GOPATH")
t.Skip("skipping because git binary not found") tg.grepStdout(regexp.QuoteMeta(tg.path("home/go")), "want GOPATH=$HOME/go")
}
tg := testgo(t)
defer tg.cleanup()
// setenv variables for test and defer deleting temporary home directory.
tg.setenv("GOPATH", "")
tmp, err := ioutil.TempDir("", "")
if err != nil {
t.Fatalf("could not create tmp home: %v", err)
}
defer os.RemoveAll(tmp)
tg.setenv(homeEnvName(), tmp)
tg.run("get", "-v", "github.com/golang/example/hello") tg.setenv("GOROOT", tg.path("home/go"))
tg.run("env", "GOPATH")
tg.grepStdoutNot(".", "want unset GOPATH because GOROOT=$HOME/go")
want := fmt.Sprintf("created GOPATH=%s; see 'go help gopath'", filepath.Join(tmp, "go")) tg.setenv("GOROOT", tg.path("home/go")+"/")
got := strings.TrimSpace(tg.getStderr()) tg.run("env", "GOPATH")
if !strings.Contains(got, want) { tg.grepStdoutNot(".", "want unset GOPATH because GOROOT=$HOME/go/")
t.Errorf("got %q; want %q", got, want)
}
} }
// Test go get missing GOPATH causes no warning if directory exists. func TestDefaultGOPATHGet(t *testing.T) {
func TestMissingGOPATHGetDoesntWarnIfExists(t *testing.T) {
testenv.MustHaveExternalNetwork(t) testenv.MustHaveExternalNetwork(t)
if _, err := exec.LookPath("git"); err != nil {
t.Skip("skipping because git binary not found")
}
tg := testgo(t) tg := testgo(t)
defer tg.cleanup() defer tg.cleanup()
// setenv variables for test and defer resetting them.
tg.setenv("GOPATH", "") tg.setenv("GOPATH", "")
tmp, err := ioutil.TempDir("", "") tg.tempDir("home")
if err != nil { tg.setenv(homeEnvName(), tg.path("home"))
t.Fatalf("could not create tmp home: %v", err)
}
defer os.RemoveAll(tmp)
if err := os.Mkdir(filepath.Join(tmp, "go"), 0777); err != nil {
t.Fatalf("could not create $HOME/go: %v", err)
}
tg.setenv(homeEnvName(), tmp) // warn for creating directory
tg.run("get", "-v", "github.com/golang/example/hello")
tg.grepStderr("created GOPATH="+regexp.QuoteMeta(tg.path("home/go"))+"; see 'go help gopath'", "did not create GOPATH")
// no warning if directory already exists
tg.must(os.RemoveAll(tg.path("home/go")))
tg.tempDir("home/go")
tg.run("get", "github.com/golang/example/hello") tg.run("get", "github.com/golang/example/hello")
tg.grepStderrNot(".", "expected no output on standard error")
got := strings.TrimSpace(tg.getStderr()) // error if $HOME/go is a file
if got != "" { tg.must(os.RemoveAll(tg.path("home/go")))
t.Errorf("got %q; wants empty", got) tg.tempFile("home/go", "")
} tg.runFail("get", "github.com/golang/example/hello")
} tg.grepStderr(`mkdir .*[/\\]go: .*(not a directory|cannot find the path)`, "expected error because $HOME/go is a file")
// Test go get missing GOPATH fails if pointed file is not a directory.
func TestMissingGOPATHGetFailsIfItsNotDirectory(t *testing.T) {
testenv.MustHaveExternalNetwork(t)
tg := testgo(t)
defer tg.cleanup()
// setenv variables for test and defer resetting them.
tg.setenv("GOPATH", "")
tmp, err := ioutil.TempDir("", "")
if err != nil {
t.Fatalf("could not create tmp home: %v", err)
}
defer os.RemoveAll(tmp)
path := filepath.Join(tmp, "go")
if err := ioutil.WriteFile(path, nil, 0777); err != nil {
t.Fatalf("could not create GOPATH at %s: %v", path, err)
}
tg.setenv(homeEnvName(), tmp)
const pkg = "github.com/golang/example/hello"
tg.runFail("get", pkg)
msg := "not a directory"
if runtime.GOOS == "windows" {
msg = "The system cannot find the path specified."
}
want := fmt.Sprintf("package %s: mkdir %s: %s", pkg, filepath.Join(tmp, "go"), msg)
got := strings.TrimSpace(tg.getStderr())
if got != want {
t.Errorf("got %q; wants %q", got, want)
}
} }
// Test go install of missing package when missing GOPATH fails and shows default GOPATH. func TestDefaultGOPATHPrintedSearchList(t *testing.T) {
func TestMissingGOPATHInstallMissingPackageFailsAndShowsDefault(t *testing.T) {
tg := testgo(t) tg := testgo(t)
defer tg.cleanup() defer tg.cleanup()
// setenv variables for test and defer resetting them.
tg.setenv("GOPATH", "") tg.setenv("GOPATH", "")
tmp, err := ioutil.TempDir("", "") tg.tempDir("home")
if err != nil { tg.setenv(homeEnvName(), tg.path("home"))
t.Fatalf("could not create tmp home: %v", err)
}
defer os.RemoveAll(tmp)
if err := os.Mkdir(filepath.Join(tmp, "go"), 0777); err != nil {
t.Fatalf("could not create $HOME/go: %v", err)
}
tg.setenv(homeEnvName(), tmp)
const pkg = "github.com/golang/example/hello"
tg.runFail("install", pkg)
pkgPath := filepath.Join(strings.Split(pkg, "/")...) tg.runFail("install", "github.com/golang/example/hello")
want := fmt.Sprintf("can't load package: package %s: cannot find package \"%s\" in any of:", pkg, pkg) + tg.grepStderr(regexp.QuoteMeta(tg.path("home/go/src/github.com/golang/example/hello"))+`.*from \$GOPATH`, "expected default GOPATH")
fmt.Sprintf("\n\t%s (from $GOROOT)", filepath.Join(runtime.GOROOT(), "src", pkgPath)) +
fmt.Sprintf("\n\t%s (from $GOPATH)", filepath.Join(tmp, "go", "src", pkgPath))
got := strings.TrimSpace(tg.getStderr())
if got != want {
t.Errorf("got %q; wants %q", got, want)
}
} }
// Issue 4186. go get cannot be used to download packages to $GOROOT. // Issue 4186. go get cannot be used to download packages to $GOROOT.
// Test that without GOPATH set, go get should fail. // Test that without GOPATH set, go get should fail.
func TestWithoutGOPATHGoGetFails(t *testing.T) { func TestGoGetIntoGOROOT(t *testing.T) {
testenv.MustHaveExternalNetwork(t) testenv.MustHaveExternalNetwork(t)
tg := testgo(t) tg := testgo(t)
defer tg.cleanup() defer tg.cleanup()
tg.parallel() tg.parallel()
tg.tempDir("src") tg.tempDir("src")
tg.setenv("GOPATH", "")
// Fails because GOROOT=GOPATH
tg.setenv("GOPATH", tg.path("."))
tg.setenv("GOROOT", tg.path(".")) tg.setenv("GOROOT", tg.path("."))
tg.runFail("get", "-d", "golang.org/x/codereview/cmd/hgpatch") tg.runFail("get", "-d", "github.com/golang/example/hello")
} tg.grepStderr("warning: GOPATH set to GOROOT", "go should detect GOPATH=GOROOT")
tg.grepStderr(`\$GOPATH must not be set to \$GOROOT`, "go should detect GOPATH=GOROOT")
// Test that with GOPATH=$GOROOT, go get should fail. // Fails because GOROOT=GOPATH after cleaning.
func TestWithGOPATHEqualsGOROOTGoGetFails(t *testing.T) { tg.setenv("GOPATH", tg.path(".")+"/")
testenv.MustHaveExternalNetwork(t) tg.setenv("GOROOT", tg.path("."))
tg.runFail("get", "-d", "github.com/golang/example/hello")
tg.grepStderr("warning: GOPATH set to GOROOT", "go should detect GOPATH=GOROOT")
tg.grepStderr(`\$GOPATH must not be set to \$GOROOT`, "go should detect GOPATH=GOROOT")
tg := testgo(t)
defer tg.cleanup()
tg.parallel()
tg.tempDir("src")
tg.setenv("GOPATH", tg.path(".")) tg.setenv("GOPATH", tg.path("."))
tg.setenv("GOROOT", tg.path(".")) tg.setenv("GOROOT", tg.path(".")+"/")
tg.runFail("get", "-d", "golang.org/x/codereview/cmd/hgpatch") tg.runFail("get", "-d", "github.com/golang/example/hello")
tg.grepStderr("warning: GOPATH set to GOROOT", "go should detect GOPATH=GOROOT")
tg.grepStderr(`\$GOPATH must not be set to \$GOROOT`, "go should detect GOPATH=GOROOT")
// Fails because GOROOT=$HOME/go so default GOPATH unset.
tg.tempDir("home/go")
tg.setenv(homeEnvName(), tg.path("home"))
tg.setenv("GOPATH", "")
tg.setenv("GOROOT", tg.path("home/go"))
tg.runFail("get", "-d", "github.com/golang/example/hello")
tg.grepStderr(`\$GOPATH not set`, "expected GOPATH not set")
tg.setenv(homeEnvName(), tg.path("home")+"/")
tg.setenv("GOPATH", "")
tg.setenv("GOROOT", tg.path("home/go"))
tg.runFail("get", "-d", "github.com/golang/example/hello")
tg.grepStderr(`\$GOPATH not set`, "expected GOPATH not set")
tg.setenv(homeEnvName(), tg.path("home"))
tg.setenv("GOPATH", "")
tg.setenv("GOROOT", tg.path("home/go")+"/")
tg.runFail("get", "-d", "github.com/golang/example/hello")
tg.grepStderr(`\$GOPATH not set`, "expected GOPATH not set")
} }
func TestLdflagsArgumentsWithSpacesIssue3941(t *testing.T) { func TestLdflagsArgumentsWithSpacesIssue3941(t *testing.T) {
......
...@@ -426,7 +426,7 @@ func downloadPackage(p *load.Package) error { ...@@ -426,7 +426,7 @@ func downloadPackage(p *load.Package) error {
return fmt.Errorf("cannot download, $GOPATH not set. For more details see: 'go help gopath'") return fmt.Errorf("cannot download, $GOPATH not set. For more details see: 'go help gopath'")
} }
// Guard against people setting GOPATH=$GOROOT. // Guard against people setting GOPATH=$GOROOT.
if list[0] == cfg.GOROOT { if filepath.Clean(list[0]) == filepath.Clean(cfg.GOROOT) {
return fmt.Errorf("cannot download, $GOPATH must not be set to $GOROOT. For more details see: 'go help gopath'") return fmt.Errorf("cannot download, $GOPATH must not be set to $GOROOT. For more details see: 'go help gopath'")
} }
if _, err := os.Stat(filepath.Join(list[0], "src/cmd/go/alldocs.go")); err == nil { if _, err := os.Stat(filepath.Join(list[0], "src/cmd/go/alldocs.go")); err == nil {
......
...@@ -85,7 +85,7 @@ func main() { ...@@ -85,7 +85,7 @@ func main() {
// Diagnose common mistake: GOPATH==GOROOT. // Diagnose common mistake: GOPATH==GOROOT.
// This setting is equivalent to not setting GOPATH at all, // This setting is equivalent to not setting GOPATH at all,
// which is not what most people want when they do it. // which is not what most people want when they do it.
if gopath := cfg.BuildContext.GOPATH; gopath == runtime.GOROOT() { if gopath := cfg.BuildContext.GOPATH; filepath.Clean(gopath) == filepath.Clean(runtime.GOROOT()) {
fmt.Fprintf(os.Stderr, "warning: GOPATH set to GOROOT (%s) has no effect\n", gopath) fmt.Fprintf(os.Stderr, "warning: GOPATH set to GOROOT (%s) has no effect\n", gopath)
} else { } else {
for _, p := range filepath.SplitList(gopath) { for _, p := range filepath.SplitList(gopath) {
......
...@@ -266,7 +266,7 @@ func defaultGOPATH() string { ...@@ -266,7 +266,7 @@ func defaultGOPATH() string {
} }
if home := os.Getenv(env); home != "" { if home := os.Getenv(env); home != "" {
def := filepath.Join(home, "go") def := filepath.Join(home, "go")
if def == runtime.GOROOT() { if filepath.Clean(def) == filepath.Clean(runtime.GOROOT()) {
// Don't set the default GOPATH to GOROOT, // Don't set the default GOPATH to GOROOT,
// as that will trigger warnings from the go tool. // as that will trigger warnings from the go tool.
return "" return ""
......
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