Commit dc4a815d authored by Francesc Campoy's avatar Francesc Campoy Committed by Francesc Campoy Flores

go/build: implement default GOPATH

Whenever GOPATH is not defined in the environment, use $HOME/go
as its default value. For Windows systems use %USERPROFILE%/go
and $home/go for plan9.

The choice of these environment variables is based on what Docker
currently does. The os/user package is not used to avoid having
a cgo dependency.

Updates #17262. Documentation changes forthcoming.

Change-Id: I6368fbfbc5afda99d6e64c35c1980076fcf45344
Reviewed-on: https://go-review.googlesource.com/32019
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarRuss Cox <rsc@golang.org>
parent ebc0b625
...@@ -40,7 +40,7 @@ func mkEnv() []envVar { ...@@ -40,7 +40,7 @@ func mkEnv() []envVar {
{"GOHOSTARCH", runtime.GOARCH}, {"GOHOSTARCH", runtime.GOARCH},
{"GOHOSTOS", runtime.GOOS}, {"GOHOSTOS", runtime.GOOS},
{"GOOS", goos}, {"GOOS", goos},
{"GOPATH", os.Getenv("GOPATH")}, {"GOPATH", buildContext.GOPATH},
{"GORACE", os.Getenv("GORACE")}, {"GORACE", os.Getenv("GORACE")},
{"GOROOT", goroot}, {"GOROOT", goroot},
{"GOTOOLDIR", toolDir}, {"GOTOOLDIR", toolDir},
......
...@@ -417,6 +417,10 @@ func downloadPackage(p *Package) error { ...@@ -417,6 +417,10 @@ func downloadPackage(p *Package) error {
if list[0] == goroot { if list[0] == 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 {
return fmt.Errorf("cannot download, %s is a GOROOT, not a GOPATH. For more details see: 'go help gopath'", list[0])
}
p.build.Root = list[0]
p.build.SrcRoot = filepath.Join(list[0], "src") p.build.SrcRoot = filepath.Join(list[0], "src")
p.build.PkgRoot = filepath.Join(list[0], "pkg") p.build.PkgRoot = filepath.Join(list[0], "pkg")
} }
...@@ -445,11 +449,19 @@ func downloadPackage(p *Package) error { ...@@ -445,11 +449,19 @@ func downloadPackage(p *Package) error {
if _, err := os.Stat(root); err == nil { if _, err := os.Stat(root); err == nil {
return fmt.Errorf("%s exists but %s does not - stale checkout?", root, meta) return fmt.Errorf("%s exists but %s does not - stale checkout?", root, meta)
} }
_, err := os.Stat(p.build.Root)
gopathExisted := err == nil
// Some version control tools require the parent of the target to exist. // Some version control tools require the parent of the target to exist.
parent, _ := filepath.Split(root) parent, _ := filepath.Split(root)
if err = os.MkdirAll(parent, 0777); err != nil { if err = os.MkdirAll(parent, 0777); err != nil {
return err return err
} }
if buildV && !gopathExisted && p.build.Root == buildContext.GOPATH {
fmt.Fprintf(os.Stderr, "created GOPATH=%s; see 'go help gopath'\n", p.build.Root)
}
if err = vcs.create(root, repo); err != nil { if err = vcs.create(root, repo); err != nil {
return err return err
} }
......
...@@ -1664,15 +1664,150 @@ func TestMentionGOPATHNotOnSecondEntry(t *testing.T) { ...@@ -1664,15 +1664,150 @@ func TestMentionGOPATHNotOnSecondEntry(t *testing.T) {
} }
} }
// Test missing GOPATH is reported. func homeEnvName() string {
func TestMissingGOPATHIsReported(t *testing.T) { switch runtime.GOOS {
case "windows":
return "USERPROFILE"
case "plan9":
return "home"
default:
return "HOME"
}
}
// Test go env missing GOPATH shows default.
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.setenv("GOPATH", "")
tg.runFail("install", "foo/quxx") tg.run("env", "GOPATH")
if tg.grepCountBoth(`\(\$GOPATH not set\. For more details see: 'go help gopath'\)$`) != 1 {
t.Error(`go install foo/quxx expected error: ($GOPATH not set. For more details see: 'go help gopath')`) 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) {
if _, err := exec.LookPath("git"); err != nil {
t.Skip("skipping because git binary not found")
}
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")
want := fmt.Sprintf("created GOPATH=%s; see 'go help gopath'", filepath.Join(tmp, "go"))
got := strings.TrimSpace(tg.getStderr())
if !strings.Contains(got, want) {
t.Errorf("got %q; want %q", got, want)
}
}
// Test go get missing GOPATH causes no warning if directory exists.
func TestMissingGOPATHGetDoesntWarnIfExists(t *testing.T) {
if _, err := exec.LookPath("git"); err != nil {
t.Skip("skipping because git binary not found")
}
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)
if err := os.Mkdir(filepath.Join(tmp, "go"), 0777); err != nil {
t.Fatalf("could not create $HOME/go: %v", err)
}
tg.setenv(homeEnvName(), tmp)
tg.run("get", "github.com/golang/example/hello")
got := strings.TrimSpace(tg.getStderr())
if got != "" {
t.Errorf("got %q; wants empty", got)
}
}
// Test go get missing GOPATH fails if pointed file is not a directory.
func TestMissingGOPATHGetFailsIfItsNotDirectory(t *testing.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 TestMissingGOPATHInstallMissingPackageFailsAndShowsDefault(t *testing.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)
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, "/")...)
want := fmt.Sprintf("can't load package: package %s: cannot find package \"%s\" in any of:", pkg, pkg) +
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)
} }
} }
......
...@@ -135,7 +135,7 @@ func main() { ...@@ -135,7 +135,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 := os.Getenv("GOPATH"); gopath == runtime.GOROOT() { if gopath := buildContext.GOPATH; gopath == 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) {
......
...@@ -256,13 +256,26 @@ func (ctxt *Context) SrcDirs() []string { ...@@ -256,13 +256,26 @@ func (ctxt *Context) SrcDirs() []string {
// if set, or else the compiled code's GOARCH, GOOS, and GOROOT. // if set, or else the compiled code's GOARCH, GOOS, and GOROOT.
var Default Context = defaultContext() var Default Context = defaultContext()
func defaultGOPATH() string {
env := "HOME"
if runtime.GOOS == "windows" {
env = "USERPROFILE"
} else if runtime.GOOS == "plan9" {
env = "home"
}
if home := os.Getenv(env); home != "" {
return filepath.Join(home, "go")
}
return ""
}
func defaultContext() Context { func defaultContext() Context {
var c Context var c Context
c.GOARCH = envOr("GOARCH", runtime.GOARCH) c.GOARCH = envOr("GOARCH", runtime.GOARCH)
c.GOOS = envOr("GOOS", runtime.GOOS) c.GOOS = envOr("GOOS", runtime.GOOS)
c.GOROOT = pathpkg.Clean(runtime.GOROOT()) c.GOROOT = pathpkg.Clean(runtime.GOROOT())
c.GOPATH = envOr("GOPATH", "") c.GOPATH = envOr("GOPATH", defaultGOPATH())
c.Compiler = runtime.Compiler c.Compiler = runtime.Compiler
// Each major Go release in the Go 1.x series should add a tag here. // Each major Go release in the Go 1.x series should add a tag here.
......
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