diff --git a/src/cmd/go/internal/cfg/cfg.go b/src/cmd/go/internal/cfg/cfg.go index a4edd854f1d35a..78cbbb949e7c27 100644 --- a/src/cmd/go/internal/cfg/cfg.go +++ b/src/cmd/go/internal/cfg/cfg.go @@ -460,7 +460,7 @@ var ( GOROOTpkg string GOROOTsrc string - GOBIN = Getenv("GOBIN") + GOBIN, GOBINChanged = EnvOrAndChanged("GOBIN", "") GOCACHEPROG, GOCACHEPROGChanged = EnvOrAndChanged("GOCACHEPROG", "") GOMODCACHE, GOMODCACHEChanged = EnvOrAndChanged("GOMODCACHE", gopathDir("pkg/mod")) diff --git a/src/cmd/go/internal/envcmd/env.go b/src/cmd/go/internal/envcmd/env.go index ed23b856f132c6..3d72c368b088a2 100644 --- a/src/cmd/go/internal/envcmd/env.go +++ b/src/cmd/go/internal/envcmd/env.go @@ -83,7 +83,6 @@ func MkEnv() []cfg.EnvVar { {Name: "GO111MODULE", Value: cfg.Getenv("GO111MODULE")}, {Name: "GOARCH", Value: cfg.Goarch, Changed: cfg.Goarch != runtime.GOARCH}, {Name: "GOAUTH", Value: cfg.GOAUTH, Changed: cfg.GOAUTHChanged}, - {Name: "GOBIN", Value: cfg.GOBIN}, {Name: "GOCACHE"}, {Name: "GOCACHEPROG", Value: cfg.GOCACHEPROG, Changed: cfg.GOCACHEPROGChanged}, {Name: "GODEBUG", Value: os.Getenv("GODEBUG")}, @@ -133,7 +132,7 @@ func MkEnv() []cfg.EnvVar { if env[i].Value != "on" && env[i].Value != "" { env[i].Changed = true } - case "GOBIN", "GOEXPERIMENT", "GOFLAGS", "GOINSECURE", "GOPACKAGESDRIVER", "GOPRIVATE", "GOTMPDIR", "GOVCS": + case "GOEXPERIMENT", "GOFLAGS", "GOINSECURE", "GOPACKAGESDRIVER", "GOPRIVATE", "GOTMPDIR", "GOVCS": if env[i].Value != "" { env[i].Changed = true } @@ -210,9 +209,30 @@ func ExtraEnvVars(ld *modload.Loader) []cfg.EnvVar { if cfg.Getenv("GOWORK") == "off" { gowork = "off" } + gobin := cfg.GOBIN + if gobin == "" && cfg.ModulesEnabled { + gobin = modload.BinDir(ld) + } else if gobin == "" { + // Best effort guess of where the binary will be installed. + // go.dev/issue/23439 + gopaths := filepath.SplitList(cfg.BuildContext.GOPATH) + wd, err := os.Getwd() + if err == nil && len(gopaths) > 0 { + gopath := gopaths[0] + for _, p := range gopaths { + if strings.HasPrefix(wd, p) { + gopath = p + break + } + } + gobin = filepath.Join(gopath, "bin") + } + } + return []cfg.EnvVar{ {Name: "GOMOD", Value: gomod}, {Name: "GOWORK", Value: gowork}, + {Name: "GOBIN", Value: gobin, Changed: cfg.GOBINChanged}, } } diff --git a/src/cmd/go/testdata/script/env_gobin.txt b/src/cmd/go/testdata/script/env_gobin.txt new file mode 100644 index 00000000000000..3dfec3570e9ab6 --- /dev/null +++ b/src/cmd/go/testdata/script/env_gobin.txt @@ -0,0 +1,44 @@ + +go env GOBIN +stdout $WORK${/}gopath${/}bin +go env -changed GOBIN +! stdout $WORK + +# report explicit settings +env GOBIN=/tmp/gobin +go env GOBIN +stdout /tmp/gobin +go env -changed GOBIN +stdout /tmp/gobin + +# reset +env GOBIN= +go env -changed GOBIN +! stdout $WORK + +# In GOPATH mode +# the default install directory depends on the GOPATH +# technically it also depends on the install target +# but that's not something we can report. +env GO111MODULE=off +env GOPATH=$WORK${/}a${:}$WORK${/}b + +# report GOPATH[0]/bin outside of GOPATH +cd $WORK +go env GOBIN +stdout $WORK${/}a${/}bin + +# report the current GOPATH/bin inside of a GOPATH +cd $WORK/a/src/example1.com +go env GOBIN +stdout $WORK${/}a${/}bin + +cd $WORK/b/src/example2.com +go env GOBIN +stdout $WORK${/}b${/}bin + +-- $WORK/a/src/example1.com/main.go -- +package main + +-- $WORK/b/src/example2.com/main.go -- +package main