From 9d7076b178e3b688a8421a8ce02466a3701d31a0 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Tue, 27 Mar 2012 11:57:39 -0400 Subject: [PATCH] cmd/go: respect $GOBIN always Another attempt at https://golang.org/cl/5754088. Before, we only consulted $GOBIN for source code found in $GOROOT, but that's confusing to explain and less useful. The new behavior lets users set GOBIN=$HOME/bin and have all go-compiled binaries installed there. Tested a few cases in test.bash. Ran all.bash with and without $GOBIN and it works. Even so, I expect it to break the builders, like it did last time, we can debug from there. Fixes #3269 (again). Fixes #3396. Fixes #3397. R=golang-dev, bradfitz CC=golang-dev https://golang.org/cl/5927051 --- doc/install-source.html | 5 ++- src/cmd/go/build.go | 34 ++++++++-------- src/cmd/go/doc.go | 6 ++- src/cmd/go/help.go | 4 +- src/cmd/go/pkg.go | 8 +++- src/cmd/go/test.bash | 39 +++++++++++++++++++ .../go/testdata/src/go-cmd-test/helloworld.go | 5 +++ 7 files changed, 80 insertions(+), 21 deletions(-) create mode 100644 src/cmd/go/testdata/src/go-cmd-test/helloworld.go diff --git a/doc/install-source.html b/doc/install-source.html index 82ff8e740d0..4673850f42d 100644 --- a/doc/install-source.html +++ b/doc/install-source.html @@ -393,11 +393,12 @@ For example, you should not set $GOHOSTARCH to

$GOBIN

-The location where binaries from the main repository will be installed. -XXX THIS MAY CHANGE TO BE AN OVERRIDE EVEN FOR GOPATH ENTRIES XXX +The location where Go binaries will be installed. The default is $GOROOT/bin. After installing, you will want to arrange to add this directory to your $PATH, so you can use the tools. +If $GOBIN is set, the go command +installs all commands there.

$GOARM (arm, default=6)

diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go index 16177c12772..4bb83f16188 100644 --- a/src/cmd/go/build.go +++ b/src/cmd/go/build.go @@ -74,6 +74,8 @@ The build flags are shared by the build, install, run, and test commands: more information about build tags. For more about specifying packages, see 'go help packages'. +For more about where packages and binaries are installed, +see 'go help gopath'. See also: go install, go get, go clean. `, @@ -304,19 +306,13 @@ const ( var ( goroot = filepath.Clean(runtime.GOROOT()) - gobin = defaultGobin() + gobin = os.Getenv("GOBIN") + gorootBin = filepath.Join(goroot, "bin") gorootSrcPkg = filepath.Join(goroot, "src/pkg") gorootPkg = filepath.Join(goroot, "pkg") gorootSrc = filepath.Join(goroot, "src") ) -func defaultGobin() string { - if s := os.Getenv("GOBIN"); s != "" { - return s - } - return filepath.Join(goroot, "bin") -} - func (b *builder) init() { var err error b.print = fmt.Print @@ -388,17 +384,23 @@ func goFilesPackage(gofiles []string) *Package { pkg.load(&stk, bp, err) pkg.localPrefix = dirToImportPath(dir) pkg.ImportPath = "command-line-arguments" + pkg.target = "" - if *buildO == "" { - if pkg.Name == "main" { - _, elem := filepath.Split(gofiles[0]) - *buildO = elem[:len(elem)-len(".go")] + exeSuffix - } else { + if pkg.Name == "main" { + _, elem := filepath.Split(gofiles[0]) + exe := elem[:len(elem)-len(".go")] + exeSuffix + if *buildO == "" { + *buildO = exe + } + if gobin != "" { + pkg.target = filepath.Join(gobin, exe) + } + } else { + if *buildO == "" { *buildO = pkg.Name + ".a" } } - pkg.target = "" - pkg.Target = "" + pkg.Target = pkg.target pkg.Stale = true computeStale(pkg) @@ -463,7 +465,7 @@ func (b *builder) action(mode buildMode, depMode buildMode, p *Package) *action return a } - if p.local { + if p.local && p.target == "" { // Imported via local path. No permanent target. mode = modeBuild } diff --git a/src/cmd/go/doc.go b/src/cmd/go/doc.go index 162eecfcc92..4bfd5236d8f 100644 --- a/src/cmd/go/doc.go +++ b/src/cmd/go/doc.go @@ -91,6 +91,8 @@ The build flags are shared by the build, install, run, and test commands: more information about build tags. For more about specifying packages, see 'go help packages'. +For more about where packages and binaries are installed, +see 'go help gopath'. See also: go install, go get, go clean. @@ -461,7 +463,9 @@ the final element, not the entire path. That is, the command with source in DIR/src/foo/quux is installed into DIR/bin/quux, not DIR/bin/foo/quux. The foo/ is stripped so that you can add DIR/bin to your PATH to get at the -installed commands. +installed commands. If the GOBIN environment variable is +set, commands are installed to the directory it names instead +of DIR/bin. Here's an example directory layout: diff --git a/src/cmd/go/help.go b/src/cmd/go/help.go index 26640d833c5..47ea0c7110f 100644 --- a/src/cmd/go/help.go +++ b/src/cmd/go/help.go @@ -209,7 +209,9 @@ the final element, not the entire path. That is, the command with source in DIR/src/foo/quux is installed into DIR/bin/quux, not DIR/bin/foo/quux. The foo/ is stripped so that you can add DIR/bin to your PATH to get at the -installed commands. +installed commands. If the GOBIN environment variable is +set, commands are installed to the directory it names instead +of DIR/bin. Here's an example directory layout: diff --git a/src/cmd/go/pkg.go b/src/cmd/go/pkg.go index 1a75019aca3..30bbfad55a9 100644 --- a/src/cmd/go/pkg.go +++ b/src/cmd/go/pkg.go @@ -222,6 +222,9 @@ func loadImport(path string, srcDir string, stk *importStack, importPos []token. // See issue 3268 for mistakes to avoid. bp, err := buildContext.Import(path, srcDir, 0) bp.ImportPath = importPath + if gobin != "" { + bp.BinDir = gobin + } p.load(stk, bp, err) if p.Error != nil && len(importPos) > 0 { pos := importPos[0] @@ -552,7 +555,10 @@ func loadPackage(arg string, stk *importStack) *Package { bp, err := build.ImportDir(filepath.Join(gorootSrc, arg), 0) bp.ImportPath = arg bp.Goroot = true - bp.BinDir = gobin + bp.BinDir = gorootBin + if gobin != "" { + bp.BinDir = gobin + } bp.Root = goroot bp.SrcRoot = gorootSrc p := new(Package) diff --git a/src/cmd/go/test.bash b/src/cmd/go/test.bash index 54153510151..fe186d4bbcb 100755 --- a/src/cmd/go/test.bash +++ b/src/cmd/go/test.bash @@ -8,6 +8,9 @@ go build -o testgo ok=true +unset GOPATH +unset GOBIN + # Test that error messages have file:line information # at beginning of line. for i in testdata/errmsg/*.go @@ -80,6 +83,42 @@ if ! ./testgo test ./testdata/testimport/*.go; then ok=false fi +# Test that without $GOBIN set, binaries get installed +# into the GOPATH bin directory. +rm -rf testdata/bin +if ! GOPATH=$(pwd)/testdata ./testgo install go-cmd-test; then + echo "go install go-cmd-test failed" + ok=false +elif ! test -x testdata/bin/go-cmd-test; then + echo "go install go-cmd-test did not write to testdata/bin/go-cmd-test" + ok=false +fi + +# And with $GOBIN set, binaries get installed to $GOBIN. +if ! GOBIN=$(pwd)/testdata/bin1 GOPATH=$(pwd)/testdata ./testgo install go-cmd-test; then + echo "go install go-cmd-test failed" + ok=false +elif ! test -x testdata/bin1/go-cmd-test; then + echo "go install go-cmd-test did not write to testdata/bin1/go-cmd-test" + ok=false +fi + +# Without $GOBIN set, installing a program outside $GOPATH should fail +# (there is nowhere to install it). +if ./testgo install testdata/src/go-cmd-test/helloworld.go; then + echo "go install testdata/src/go-cmd-test/helloworld.go should have failed, did not" + ok=false +fi + +# With $GOBIN set, should install there. +if ! GOBIN=$(pwd)/testdata/bin1 ./testgo install testdata/src/go-cmd-test/helloworld.go; then + echo "go install testdata/src/go-cmd-test/helloworld.go failed" + ok=false +elif ! test -x testdata/bin1/helloworld; then + echo "go install testdata/src/go-cmd-test/helloworld.go did not write testdata/bin1/helloworld" + ok=false +fi + if $ok; then echo PASS else diff --git a/src/cmd/go/testdata/src/go-cmd-test/helloworld.go b/src/cmd/go/testdata/src/go-cmd-test/helloworld.go new file mode 100644 index 00000000000..002a5c740c7 --- /dev/null +++ b/src/cmd/go/testdata/src/go-cmd-test/helloworld.go @@ -0,0 +1,5 @@ +package main + +func main() { + println("hello world") +}