cmd/go: many bug fixes
* Reject import paths of the form cmd/x/y.
* Reject 'go install' of command outside GOPATH
* Clearer error rejecting 'go install' of package outside GOPATH.
* Name temporary binary for first file in 'go run' list or for test.
* Provide a way to pass -ldflags arguments with spaces.
* Pass all Go files (even +build ignored ones) to go fix, go fmt, go vet.
* Reject 'go run foo_test.go'.
* Silence 'exit 1' prints from 'go tool' invocations.
* Make go test -xxxprofile leave binary behind for analysis.
* Reject ~ in GOPATH except on Windows.
* Get a little less confused by symlinks.
* Document that go test x y z runs three test binaries.
* Fix go test -timeout=0.
* Add -tags flag to 'go list'.
* Use pkg/gccgo_$GOOS_$GOARCH for gccgo output.
Fixes #3389.
Fixes #3500.
Fixes #3503.
Fixes #3760.
Fixes #3941.
Fixes #4007.
Fixes #4032.
Fixes #4074.
Fixes #4127.
Fixes #4140.
Fixes #4311.
Fixes #4568.
Fixes #4576.
Fixes #4702.
R=adg
CC=golang-dev
https://golang.org/cl/7225074
diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go
index 2d1f252..7bdbb09 100644
--- a/src/cmd/go/build.go
+++ b/src/cmd/go/build.go
@@ -79,6 +79,9 @@
See the documentation for the go/build package for
more information about build tags.
+The list flags accept a space-separated list of strings. To embed spaces
+in an element in the list, surround it with either single or double quotes.
+
For more about specifying packages, see 'go help packages'.
For more about where packages and binaries are installed,
see 'go help gopath'.
@@ -167,11 +170,52 @@
cmd.Flag.BoolVar(&buildX, "x", false, "")
}
+func isSpaceByte(c byte) bool {
+ return c == ' ' || c == '\t' || c == '\n' || c == '\r'
+}
+
type stringsFlag []string
func (v *stringsFlag) Set(s string) error {
- *v = strings.Fields(s)
- return nil
+ var err error
+ *v, err = splitQuotedFields(s)
+ return err
+}
+
+func splitQuotedFields(s string) ([]string, error) {
+ // Split fields allowing '' or "" around elements.
+ // Quotes further inside the string do not count.
+ var f []string
+ for len(s) > 0 {
+ for len(s) > 0 && isSpaceByte(s[0]) {
+ s = s[1:]
+ }
+ if len(s) == 0 {
+ break
+ }
+ // Accepted quoted string. No unescaping inside.
+ if s[0] == '"' || s[0] == '\'' {
+ quote := s[0]
+ s = s[1:]
+ i := 0
+ for i < len(s) && s[i] != quote {
+ i++
+ }
+ if i >= len(s) {
+ return nil, fmt.Errorf("unterminated %c string", quote)
+ }
+ f = append(f, s[:i])
+ s = s[i+1:]
+ continue
+ }
+ i := 0
+ for i < len(s) && !isSpaceByte(s[i]) {
+ i++
+ }
+ f = append(f, s[:i])
+ s = s[i:]
+ }
+ return f, nil
}
func (v *stringsFlag) String() string {
@@ -244,7 +288,7 @@
for _, p := range pkgs {
if p.Target == "" && (!p.Standard || p.ImportPath != "unsafe") {
- errorf("go install: no install location for %s", p.ImportPath)
+ errorf("go install: no install location for directory %s outside GOPATH", p.Dir)
}
}
exitIfErrors()
@@ -514,9 +558,18 @@
a.f = (*builder).build
a.target = a.objpkg
if a.link {
- // An executable file.
- // (This is the name of a temporary file.)
- a.target = a.objdir + "a.out" + exeSuffix
+ // An executable file. (This is the name of a temporary file.)
+ // Because we run the temporary file in 'go run' and 'go test',
+ // the name will show up in ps listings. If the caller has specified
+ // a name, use that instead of a.out. The binary is generated
+ // in an otherwise empty subdirectory named exe to avoid
+ // naming conflicts. The only possible conflict is if we were
+ // to create a top-level package named exe.
+ name := "a.out"
+ if p.exeName != "" {
+ name = p.exeName
+ }
+ a.target = a.objdir + filepath.Join("exe", name) + exeSuffix
}
}
@@ -690,6 +743,14 @@
return err
}
+ // make target directory
+ dir, _ := filepath.Split(a.target)
+ if dir != "" {
+ if err := b.mkdir(dir); err != nil {
+ return err
+ }
+ }
+
var gofiles, cfiles, sfiles, objects, cgoObjects []string
gofiles = append(gofiles, a.p.GoFiles...)
cfiles = append(cfiles, a.p.CFiles...)
@@ -914,7 +975,7 @@
if dir := a1.pkgdir; dir == a1.p.build.PkgRoot && !incMap[dir] {
incMap[dir] = true
if _, ok := buildToolchain.(gccgcToolchain); ok {
- dir = filepath.Join(dir, "gccgo")
+ dir = filepath.Join(dir, "gccgo_"+goos+"_"+goarch)
} else {
dir = filepath.Join(dir, goos+"_"+goarch)
if buildRace {
diff --git a/src/cmd/go/doc.go b/src/cmd/go/doc.go
index 09cf9a7..d54b4b2 100644
--- a/src/cmd/go/doc.go
+++ b/src/cmd/go/doc.go
@@ -95,6 +95,9 @@
See the documentation for the go/build package for
more information about build tags.
+The list flags accept a space-separated list of strings. To embed spaces
+in an element in the list, surround it with either single or double quotes.
+
For more about specifying packages, see 'go help packages'.
For more about where packages and binaries are installed,
see 'go help gopath'.
@@ -272,7 +275,7 @@
Usage:
- go list [-e] [-f format] [-json] [packages]
+ go list [-e] [-f format] [-json] [-tags 'tag list'] [packages]
List lists the packages named by the import paths, one per line.
@@ -299,14 +302,15 @@
Root string // Go root or Go path dir containing this package
// Source files
- GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
- CgoFiles []string // .go sources files that import "C"
- CFiles []string // .c source files
- HFiles []string // .h source files
- SFiles []string // .s source files
- SysoFiles []string // .syso object files to add to archive
- SwigFiles []string // .swig files
- SwigCXXFiles []string // .swigcxx files
+ GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
+ CgoFiles []string // .go sources files that import "C"
+ IgnoredGoFiles []string // .go sources ignored due to build constraints
+ CFiles []string // .c source files
+ HFiles []string // .h source files
+ SFiles []string // .s source files
+ SysoFiles []string // .syso object files to add to archive
+ SwigFiles []string // .swig files
+ SwigCXXFiles []string // .swigcxx files
// Cgo directives
CgoCFLAGS []string // cgo: flags for C compiler
@@ -341,6 +345,9 @@
a non-nil Error field; other information may or may not be missing
(zeroed).
+The -tags flag specifies a list of build tags, like in the 'go build'
+command.
+
For more about specifying packages, see 'go help packages'.
@@ -376,6 +383,7 @@
'Go test' recompiles each package along with any files with names matching
the file pattern "*_test.go". These additional files can contain test functions,
benchmark functions, and example functions. See 'go help testfunc' for more.
+Each listed package causes the execution of a separate test binary.
By default, go test needs no arguments. It compiles and tests the package
with source in the current directory, including tests, and runs the tests.
@@ -748,6 +756,9 @@
pkg.test -test.v -test.cpuprofile=prof.out -dir=testdata -update
+The test flags that generate profiles also leave the test binary in pkg.test
+for use when analyzing the profiles.
+
Description of testing functions
@@ -763,8 +774,8 @@
func BenchmarkXXX(b *testing.B) { ... }
-An example function is similar to a test function but, instead of using *testing.T
-to report success or failure, prints output to os.Stdout and os.Stderr.
+An example function is similar to a test function but, instead of using
+*testing.T to report success or failure, prints output to os.Stdout.
That output is compared against the function's "Output:" comment, which
must be the last comment in the function body (see example below). An
example with no such comment, or with no text after "Output:" is compiled
diff --git a/src/cmd/go/fix.go b/src/cmd/go/fix.go
index ef02b57..8736cce 100644
--- a/src/cmd/go/fix.go
+++ b/src/cmd/go/fix.go
@@ -25,6 +25,6 @@
// Use pkg.gofiles instead of pkg.Dir so that
// the command only applies to this package,
// not to packages in subdirectories.
- run(stringList(tool("fix"), relPaths(pkg.gofiles)))
+ run(stringList(tool("fix"), relPaths(pkg.allgofiles)))
}
}
diff --git a/src/cmd/go/fmt.go b/src/cmd/go/fmt.go
index b1aba32..9d3c911 100644
--- a/src/cmd/go/fmt.go
+++ b/src/cmd/go/fmt.go
@@ -34,7 +34,7 @@
// Use pkg.gofiles instead of pkg.Dir so that
// the command only applies to this package,
// not to packages in subdirectories.
- run(stringList("gofmt", "-l", "-w", relPaths(pkg.gofiles)))
+ run(stringList("gofmt", "-l", "-w", relPaths(pkg.allgofiles)))
}
}
diff --git a/src/cmd/go/get.go b/src/cmd/go/get.go
index abcc2ba..4741d5c 100644
--- a/src/cmd/go/get.go
+++ b/src/cmd/go/get.go
@@ -204,7 +204,7 @@
// due to wildcard expansion.
for _, p := range pkgs {
if *getFix {
- run(stringList(tool("fix"), relPaths(p.gofiles)))
+ run(stringList(tool("fix"), relPaths(p.allgofiles)))
// The imports might have changed, so reload again.
p = reloadPackage(arg, stk)
diff --git a/src/cmd/go/list.go b/src/cmd/go/list.go
index 25a6f45..2d23d07 100644
--- a/src/cmd/go/list.go
+++ b/src/cmd/go/list.go
@@ -14,7 +14,7 @@
)
var cmdList = &Command{
- UsageLine: "list [-e] [-f format] [-json] [packages]",
+ UsageLine: "list [-e] [-f format] [-json] [-tags 'tag list'] [packages]",
Short: "list packages",
Long: `
List lists the packages named by the import paths, one per line.
@@ -42,14 +42,15 @@
Root string // Go root or Go path dir containing this package
// Source files
- GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
- CgoFiles []string // .go sources files that import "C"
- CFiles []string // .c source files
- HFiles []string // .h source files
- SFiles []string // .s source files
- SysoFiles []string // .syso object files to add to archive
- SwigFiles []string // .swig files
- SwigCXXFiles []string // .swigcxx files
+ GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
+ CgoFiles []string // .go sources files that import "C"
+ IgnoredGoFiles []string // .go sources ignored due to build constraints
+ CFiles []string // .c source files
+ HFiles []string // .h source files
+ SFiles []string // .s source files
+ SysoFiles []string // .syso object files to add to archive
+ SwigFiles []string // .swig files
+ SwigCXXFiles []string // .swigcxx files
// Cgo directives
CgoCFLAGS []string // cgo: flags for C compiler
@@ -84,6 +85,9 @@
a non-nil Error field; other information may or may not be missing
(zeroed).
+The -tags flag specifies a list of build tags, like in the 'go build'
+command.
+
For more about specifying packages, see 'go help packages'.
`,
}
@@ -91,6 +95,7 @@
func init() {
cmdList.Run = runList // break init cycle
cmdList.Flag.Var(buildCompiler{}, "compiler", "")
+ cmdList.Flag.Var((*stringsFlag)(&buildContext.BuildTags), "tags", "")
}
var listE = cmdList.Flag.Bool("e", false, "")
diff --git a/src/cmd/go/main.go b/src/cmd/go/main.go
index 7e34fdf..bd5d889 100644
--- a/src/cmd/go/main.go
+++ b/src/cmd/go/main.go
@@ -129,6 +129,10 @@
fmt.Fprintf(os.Stderr, "warning: GOPATH set to GOROOT (%s) has no effect\n", gopath)
} else {
for _, p := range filepath.SplitList(gopath) {
+ if strings.Contains(p, "~") && runtime.GOOS != "windows" {
+ fmt.Fprintf(os.Stderr, "go: GOPATH entry cannot contain shell metacharacter '~': %q\n", p)
+ os.Exit(2)
+ }
if build.IsLocalImport(p) {
fmt.Fprintf(os.Stderr, "go: GOPATH entry is relative; must be absolute path: %q.\nRun 'go help gopath' for usage.\n", p)
os.Exit(2)
diff --git a/src/cmd/go/pkg.go b/src/cmd/go/pkg.go
index f05cf01..793a43d 100644
--- a/src/cmd/go/pkg.go
+++ b/src/cmd/go/pkg.go
@@ -36,14 +36,15 @@
Root string `json:",omitempty"` // Go root or Go path dir containing this package
// Source files
- GoFiles []string `json:",omitempty"` // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
- CgoFiles []string `json:",omitempty"` // .go sources files that import "C"
- CFiles []string `json:",omitempty"` // .c source files
- HFiles []string `json:",omitempty"` // .h source files
- SFiles []string `json:",omitempty"` // .s source files
- SysoFiles []string `json:",omitempty"` // .syso system object files added to package
- SwigFiles []string `json:",omitempty"` // .swig files
- SwigCXXFiles []string `json:",omitempty"` // .swigcxx files
+ GoFiles []string `json:",omitempty"` // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
+ CgoFiles []string `json:",omitempty"` // .go sources files that import "C"
+ IgnoredGoFiles []string `json:",omitempty"` // .go sources ignored due to build constraints
+ CFiles []string `json:",omitempty"` // .c source files
+ HFiles []string `json:",omitempty"` // .h source files
+ SFiles []string `json:",omitempty"` // .s source files
+ SysoFiles []string `json:",omitempty"` // .syso system object files added to package
+ SwigFiles []string `json:",omitempty"` // .swig files
+ SwigCXXFiles []string `json:",omitempty"` // .swigcxx files
// Cgo directives
CgoCFLAGS []string `json:",omitempty"` // cgo: flags for C compiler
@@ -71,12 +72,14 @@
imports []*Package
deps []*Package
gofiles []string // GoFiles+CgoFiles+TestGoFiles+XTestGoFiles files, absolute paths
+ allgofiles []string // gofiles + IgnoredGoFiles, absolute paths
target string // installed file for this package (may be executable)
fake bool // synthesized package
forceBuild bool // this package must be rebuilt
forceLibrary bool // this package is a library (even if named "main")
local bool // imported via local path (./ or ../)
localPrefix string // interpret ./ and ../ imports relative to this prefix
+ exeName string // desired name for temporary executable
}
func (p *Package) copyBuild(pp *build.Package) {
@@ -92,6 +95,7 @@
p.Standard = p.Goroot && p.ImportPath != "" && !strings.Contains(p.ImportPath, ".")
p.GoFiles = pp.GoFiles
p.CgoFiles = pp.CgoFiles
+ p.IgnoredGoFiles = pp.IgnoredGoFiles
p.CFiles = pp.CFiles
p.HFiles = pp.HFiles
p.SFiles = pp.SFiles
@@ -318,11 +322,13 @@
// Install cross-compiled binaries to subdirectories of bin.
elem = full
}
- p.target = filepath.Join(p.build.BinDir, elem)
+ if p.build.BinDir != "" {
+ p.target = filepath.Join(p.build.BinDir, elem)
+ }
if p.Goroot && (isGoTool[p.ImportPath] || strings.HasPrefix(p.ImportPath, "exp/")) {
p.target = filepath.Join(gorootPkg, "tool", full)
}
- if buildContext.GOOS == "windows" {
+ if p.target != "" && buildContext.GOOS == "windows" {
p.target += ".exe"
}
} else if p.local {
@@ -357,6 +363,13 @@
}
sort.Strings(p.gofiles)
+ p.allgofiles = stringList(p.IgnoredGoFiles)
+ for i := range p.allgofiles {
+ p.allgofiles[i] = filepath.Join(p.Dir, p.allgofiles[i])
+ }
+ p.allgofiles = append(p.allgofiles, p.gofiles...)
+ sort.Strings(p.allgofiles)
+
// Build list of imported packages and full dependency list.
imports := make([]*Package, 0, len(p.Imports))
deps := make(map[string]bool)
@@ -431,7 +444,7 @@
func (p *Package) swigDir(ctxt *build.Context) string {
dir := p.build.PkgRoot
if ctxt.Compiler == "gccgo" {
- dir = filepath.Join(dir, "gccgo")
+ dir = filepath.Join(dir, "gccgo_"+ctxt.GOOS+"_"+ctxt.GOARCH)
} else {
dir = filepath.Join(dir, ctxt.GOOS+"_"+ctxt.GOARCH)
}
@@ -596,12 +609,23 @@
arg = sub
}
}
- if strings.HasPrefix(arg, "cmd/") && !strings.Contains(arg[4:], "/") {
+ if strings.HasPrefix(arg, "cmd/") {
if p := cmdCache[arg]; p != nil {
return p
}
stk.push(arg)
defer stk.pop()
+
+ if strings.Contains(arg[4:], "/") {
+ p := &Package{
+ Error: &PackageError{
+ ImportStack: stk.copy(),
+ Err: fmt.Sprintf("invalid import path: cmd/... is reserved for Go commands"),
+ },
+ }
+ return p
+ }
+
bp, err := buildContext.ImportDir(filepath.Join(gorootSrc, arg), 0)
bp.ImportPath = arg
bp.Goroot = true
diff --git a/src/cmd/go/run.go b/src/cmd/go/run.go
index 88f5761..27f989f 100644
--- a/src/cmd/go/run.go
+++ b/src/cmd/go/run.go
@@ -46,6 +46,13 @@
if len(files) == 0 {
fatalf("go run: no go files listed")
}
+ for _, file := range files {
+ if strings.HasSuffix(file, "_test.go") {
+ // goFilesPackage is going to assign this to TestGoFiles.
+ // Reject since it won't be part of the build.
+ fatalf("go run: cannot run *_test.go files (%s)", file)
+ }
+ }
p := goFilesPackage(files)
if p.Error != nil {
fatalf("%s", p.Error)
@@ -58,6 +65,13 @@
fatalf("go run: cannot run non-main package")
}
p.target = "" // must build - not up to date
+ var src string
+ if len(p.GoFiles) > 0 {
+ src = p.GoFiles[0]
+ } else {
+ src = p.CgoFiles[0]
+ }
+ p.exeName = src[:len(src)-len(".go")] // name temporary executable for first go file
a1 := b.action(modeBuild, modeBuild, p)
a := &action{f: (*builder).runProgram, args: cmdArgs, deps: []*action{a1}}
b.do(a)
diff --git a/src/cmd/go/test.bash b/src/cmd/go/test.bash
index 11e1f3b..5b0defd 100755
--- a/src/cmd/go/test.bash
+++ b/src/cmd/go/test.bash
@@ -183,7 +183,7 @@
# issue 4186. go get cannot be used to download packages to $GOROOT
# Test that without GOPATH set, go get should fail
-d=$(mktemp -d)
+d=$(mktemp -d -t testgo)
mkdir -p $d/src/pkg
if GOPATH= GOROOT=$d ./testgo get -d code.google.com/p/go.codereview/cmd/hgpatch ; then
echo 'go get code.google.com/p/go.codereview/cmd/hgpatch should not succeed with $GOPATH unset'
@@ -191,7 +191,7 @@
fi
rm -rf $d
# Test that with GOPATH=$GOROOT, go get should fail
-d=$(mktemp -d)
+d=$(mktemp -d -t testgo)
mkdir -p $d/src/pkg
if GOPATH=$d GOROOT=$d ./testgo get -d code.google.com/p/go.codereview/cmd/hgpatch ; then
echo 'go get code.google.com/p/go.codereview/cmd/hgpatch should not succeed with GOPATH=$GOROOT'
@@ -199,6 +199,86 @@
fi
rm -rf $d
+# issue 3941: args with spaces
+d=$(mktemp -d -t testgo)
+cat >$d/main.go<<EOF
+package main
+var extern string
+func main() {
+ println(extern)
+}
+EOF
+./testgo run -ldflags '-X main.extern "hello world"' $d/main.go 2>hello.out
+if ! grep -q '^hello world' hello.out; then
+ echo "ldflags -X main.extern 'hello world' failed. Output:"
+ cat hello.out
+ ok=false
+fi
+rm -rf $d
+
+# test that go test -cpuprofile leaves binary behind
+./testgo test -cpuprofile strings.prof strings || ok=false
+if [ ! -x strings.test ]; then
+ echo "go test -cpuprofile did not create strings.test"
+ ok=false
+fi
+rm -f strings.prof strings.test
+
+# issue 4568. test that symlinks don't screw things up too badly.
+old=$(pwd)
+d=$(mktemp -d -t testgo)
+mkdir -p $d/src
+(
+ ln -s $d $d/src/dir1
+ cd $d/src/dir1
+ echo package p >p.go
+ export GOPATH=$d
+ if [ "$($old/testgo list -f '{{.Root}}' .)" != "$d" ]; then
+ echo got lost in symlink tree:
+ pwd
+ env|grep WD
+ $old/testgo list -json . dir1
+ touch $d/failed
+ fi
+)
+if [ -f $d/failed ]; then
+ ok=false
+fi
+rm -rf $d
+
+# issue 4515.
+d=$(mktemp -d -t testgo)
+mkdir -p $d/src/example/a $d/src/example/b $d/bin
+cat >$d/src/example/a/main.go <<EOF
+package main
+func main() {}
+EOF
+cat >$d/src/example/b/main.go <<EOF
+// +build mytag
+
+package main
+func main() {}
+EOF
+GOPATH=$d ./testgo install -tags mytag example/a example/b || ok=false
+if [ ! -x $d/bin/a -o ! -x $d/bin/b ]; then
+ echo go install example/a example/b did not install binaries
+ ok=false
+fi
+rm -f $d/bin/*
+GOPATH=$d ./testgo install -tags mytag example/... || ok=false
+if [ ! -x $d/bin/a -o ! -x $d/bin/b ]; then
+ echo go install example/... did not install binaries
+ ok=false
+fi
+rm -f $d/bin/*go
+export GOPATH=$d
+if [ "$(./testgo list -tags mytag example/b...)" != "example/b" ]; then
+ echo go list example/b did not find example/b
+ ok=false
+fi
+unset GOPATH
+rm -rf $d
+
# clean up
rm -rf testdata/bin testdata/bin1
rm -f testgo
diff --git a/src/cmd/go/test.go b/src/cmd/go/test.go
index d2498ca..10082ce 100644
--- a/src/cmd/go/test.go
+++ b/src/cmd/go/test.go
@@ -48,6 +48,7 @@
'Go test' recompiles each package along with any files with names matching
the file pattern "*_test.go". These additional files can contain test functions,
benchmark functions, and example functions. See 'go help testfunc' for more.
+Each listed package causes the execution of a separate test binary.
By default, go test needs no arguments. It compiles and tests the package
with source in the current directory, including tests, and runs the tests.
@@ -156,6 +157,9 @@
will compile the test binary and then run it as
pkg.test -test.v -test.cpuprofile=prof.out -dir=testdata -update
+
+The test flags that generate profiles also leave the test binary in pkg.test
+for use when analyzing the profiles.
`,
}
@@ -206,6 +210,7 @@
var (
testC bool // -c flag
+ testProfile bool // some profiling flag
testI bool // -i flag
testV bool // -v flag
testFiles []string // -file flag(s) TODO: not respected
@@ -231,12 +236,15 @@
if testC && len(pkgs) != 1 {
fatalf("cannot use -c flag with multiple packages")
}
+ if testProfile && len(pkgs) != 1 {
+ fatalf("cannot use test profile flag with multiple packages")
+ }
// If a test timeout was given and is parseable, set our kill timeout
// to that timeout plus one minute. This is a backup alarm in case
// the test wedges with a goroutine spinning and its background
// timer does not get a chance to fire.
- if dt, err := time.ParseDuration(testTimeout); err == nil {
+ if dt, err := time.ParseDuration(testTimeout); err == nil && dt > 0 {
testKillTimeout = dt + 1*time.Minute
}
@@ -427,7 +435,14 @@
// Use last element of import path, not package name.
// They differ when package name is "main".
- _, elem := path.Split(p.ImportPath)
+ // But if the import path is "command-line-arguments",
+ // like it is during 'go run', use the package name.
+ var elem string
+ if p.ImportPath == "command-line-arguments" {
+ elem = p.Name
+ } else {
+ _, elem = path.Split(p.ImportPath)
+ }
testBinary := elem + ".test"
// The ptest package needs to be importable under the
@@ -554,14 +569,17 @@
a.target = filepath.Join(testDir, testBinary) + exeSuffix
pmainAction := a
- if testC {
- // -c flag: create action to copy binary to ./test.out.
+ if testC || testProfile {
+ // -c or profiling flag: create action to copy binary to ./test.out.
runAction = &action{
f: (*builder).install,
deps: []*action{pmainAction},
p: pmain,
- target: testBinary + exeSuffix,
+ target: filepath.Join(cwd, testBinary+exeSuffix),
}
+ pmainAction = runAction // in case we are running the test
+ }
+ if testC {
printAction = &action{p: p, deps: []*action{runAction}} // nop
} else {
// run test
@@ -655,7 +673,7 @@
case <-tick.C:
cmd.Process.Kill()
err = <-done
- fmt.Fprintf(&buf, "*** Test killed: ran too long.\n")
+ fmt.Fprintf(&buf, "*** Test killed: ran too long (%v).\n", testKillTimeout)
}
tick.Stop()
}
diff --git a/src/cmd/go/testflag.go b/src/cmd/go/testflag.go
index 6d3b2be..8dd5143 100644
--- a/src/cmd/go/testflag.go
+++ b/src/cmd/go/testflag.go
@@ -134,6 +134,7 @@
passToTest = append(passToTest, args[i])
continue
}
+ var err error
switch f.name {
// bool flags.
case "a", "c", "i", "n", "x", "v", "work", "race":
@@ -141,11 +142,20 @@
case "p":
setIntFlag(&buildP, value)
case "gcflags":
- buildGcflags = strings.Fields(value)
+ buildGcflags, err = splitQuotedFields(value)
+ if err != nil {
+ fatalf("invalid flag argument for -%s: %v", f.name, err)
+ }
case "ldflags":
- buildLdflags = strings.Fields(value)
+ buildLdflags, err = splitQuotedFields(value)
+ if err != nil {
+ fatalf("invalid flag argument for -%s: %v", f.name, err)
+ }
case "gccgoflags":
- buildGccgoflags = strings.Fields(value)
+ buildGccgoflags, err = splitQuotedFields(value)
+ if err != nil {
+ fatalf("invalid flag argument for -%s: %v", f.name, err)
+ }
case "tags":
buildContext.BuildTags = strings.Fields(value)
case "compiler":
@@ -157,6 +167,8 @@
testBench = true
case "timeout":
testTimeout = value
+ case "blockprofile", "cpuprofile", "memprofile":
+ testProfile = true
}
if extraWord {
i++
diff --git a/src/cmd/go/tool.go b/src/cmd/go/tool.go
index 01e8ff6..299b94c 100644
--- a/src/cmd/go/tool.go
+++ b/src/cmd/go/tool.go
@@ -100,7 +100,14 @@
}
err := toolCmd.Run()
if err != nil {
- fmt.Fprintf(os.Stderr, "go tool %s: %s\n", toolName, err)
+ // Only print about the exit status if the command
+ // didn't even run (not an ExitError) or it didn't exit cleanly
+ // or we're printing command lines too (-x mode).
+ // Assume if command exited cleanly (even with non-zero status)
+ // it printed any messages it wanted to print.
+ if e, ok := err.(*exec.ExitError); !ok || !e.Exited() || buildX {
+ fmt.Fprintf(os.Stderr, "go tool %s: %s\n", toolName, err)
+ }
setExitStatus(1)
return
}
diff --git a/src/cmd/go/vet.go b/src/cmd/go/vet.go
index eb0b89c..40e2726 100644
--- a/src/cmd/go/vet.go
+++ b/src/cmd/go/vet.go
@@ -32,6 +32,6 @@
// Use pkg.gofiles instead of pkg.Dir so that
// the command only applies to this package,
// not to packages in subdirectories.
- run(tool("vet"), relPaths(pkg.gofiles))
+ run(tool("vet"), relPaths(stringList(pkg.allgofiles, pkg.IgnoredGoFiles)))
}
}
diff --git a/src/pkg/go/build/build.go b/src/pkg/go/build/build.go
index e2a47a5..4dedee6 100644
--- a/src/pkg/go/build/build.go
+++ b/src/pkg/go/build/build.go
@@ -117,12 +117,27 @@
return f(root, dir)
}
- if p, err := filepath.EvalSymlinks(root); err == nil {
- root = p
+ // Try using paths we received.
+ if rel, ok = hasSubdir(root, dir); ok {
+ return
}
- if p, err := filepath.EvalSymlinks(dir); err == nil {
- dir = p
+
+ // Try expanding symlinks and comparing
+ // expanded against unexpanded and
+ // expanded against expanded.
+ rootSym, _ := filepath.EvalSymlinks(root)
+ dirSym, _ := filepath.EvalSymlinks(dir)
+
+ if rel, ok = hasSubdir(rootSym, dir); ok {
+ return
}
+ if rel, ok = hasSubdir(root, dirSym); ok {
+ return
+ }
+ return hasSubdir(rootSym, dirSym)
+}
+
+func hasSubdir(root, dir string) (rel string, ok bool) {
const sep = string(filepath.Separator)
root = filepath.Clean(root)
if !strings.HasSuffix(root, sep) {
@@ -181,6 +196,21 @@
// Do not get confused by this common mistake.
continue
}
+ if strings.Contains(p, "~") && runtime.GOOS != "windows" {
+ // Path segments containing ~ on Unix are almost always
+ // users who have incorrectly quoted ~ while setting GOPATH,
+ // preventing it from expanding to $HOME.
+ // The situation is made more confusing by the fact that
+ // bash allows quoted ~ in $PATH (most shells do not).
+ // Do not get confused by this, and do not try to use the path.
+ // It does not exist, and printing errors about it confuses
+ // those users even more, because they think "sure ~ exists!".
+ // The go command diagnoses this situation and prints a
+ // useful error.
+ // On Windows, ~ is used in short names, such as c:\progra~1
+ // for c:\program files.
+ continue
+ }
all = append(all, p)
}
return all
@@ -284,14 +314,15 @@
PkgObj string // installed .a file
// Source files
- GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
- CgoFiles []string // .go source files that import "C"
- CFiles []string // .c source files
- HFiles []string // .h source files
- SFiles []string // .s source files
- SysoFiles []string // .syso system object files to add to archive
- SwigFiles []string // .swig files
- SwigCXXFiles []string // .swigcxx files
+ GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
+ CgoFiles []string // .go source files that import "C"
+ IgnoredGoFiles []string // .go source files ignored for this build
+ CFiles []string // .c source files
+ HFiles []string // .h source files
+ SFiles []string // .s source files
+ SysoFiles []string // .syso system object files to add to archive
+ SwigFiles []string // .swig files
+ SwigCXXFiles []string // .swigcxx files
// Cgo directives
CgoPkgConfig []string // Cgo pkg-config directives
@@ -519,15 +550,20 @@
strings.HasPrefix(name, ".") {
continue
}
- if !ctxt.UseAllFiles && !ctxt.goodOSArchFile(name) {
- continue
- }
i := strings.LastIndex(name, ".")
if i < 0 {
i = len(name)
}
ext := name[i:]
+
+ if !ctxt.UseAllFiles && !ctxt.goodOSArchFile(name) {
+ if ext == ".go" {
+ p.IgnoredGoFiles = append(p.IgnoredGoFiles, name)
+ }
+ continue
+ }
+
switch ext {
case ".go", ".c", ".s", ".h", ".S", ".swig", ".swigcxx":
// tentatively okay - read to make sure
@@ -561,6 +597,9 @@
// Look for +build comments to accept or reject the file.
if !ctxt.UseAllFiles && !ctxt.shouldBuild(data) {
+ if ext == ".go" {
+ p.IgnoredGoFiles = append(p.IgnoredGoFiles, name)
+ }
continue
}
@@ -593,6 +632,7 @@
pkg := pf.Name.Name
if pkg == "documentation" {
+ p.IgnoredGoFiles = append(p.IgnoredGoFiles, name)
continue
}