Commit 8b6534b7 authored by Russ Cox's avatar Russ Cox

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
parent 87c3c1b0
...@@ -79,6 +79,9 @@ The build flags are shared by the build, install, run, and test commands: ...@@ -79,6 +79,9 @@ The build flags are shared by the build, install, run, and test commands:
See the documentation for the go/build package for See the documentation for the go/build package for
more information about build tags. 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 specifying packages, see 'go help packages'.
For more about where packages and binaries are installed, For more about where packages and binaries are installed,
see 'go help gopath'. see 'go help gopath'.
...@@ -167,11 +170,52 @@ func addBuildFlagsNX(cmd *Command) { ...@@ -167,11 +170,52 @@ func addBuildFlagsNX(cmd *Command) {
cmd.Flag.BoolVar(&buildX, "x", false, "") cmd.Flag.BoolVar(&buildX, "x", false, "")
} }
func isSpaceByte(c byte) bool {
return c == ' ' || c == '\t' || c == '\n' || c == '\r'
}
type stringsFlag []string type stringsFlag []string
func (v *stringsFlag) Set(s string) error { func (v *stringsFlag) Set(s string) error {
*v = strings.Fields(s) var err error
return nil *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 { func (v *stringsFlag) String() string {
...@@ -244,7 +288,7 @@ func runInstall(cmd *Command, args []string) { ...@@ -244,7 +288,7 @@ func runInstall(cmd *Command, args []string) {
for _, p := range pkgs { for _, p := range pkgs {
if p.Target == "" && (!p.Standard || p.ImportPath != "unsafe") { 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() exitIfErrors()
...@@ -514,9 +558,18 @@ func (b *builder) action(mode buildMode, depMode buildMode, p *Package) *action ...@@ -514,9 +558,18 @@ func (b *builder) action(mode buildMode, depMode buildMode, p *Package) *action
a.f = (*builder).build a.f = (*builder).build
a.target = a.objpkg a.target = a.objpkg
if a.link { if a.link {
// An executable file. // An executable file. (This is the name of a temporary file.)
// (This is the name of a temporary file.) // Because we run the temporary file in 'go run' and 'go test',
a.target = a.objdir + "a.out" + exeSuffix // 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 @@ func (b *builder) build(a *action) (err error) { ...@@ -690,6 +743,14 @@ func (b *builder) build(a *action) (err error) {
return err 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 var gofiles, cfiles, sfiles, objects, cgoObjects []string
gofiles = append(gofiles, a.p.GoFiles...) gofiles = append(gofiles, a.p.GoFiles...)
cfiles = append(cfiles, a.p.CFiles...) cfiles = append(cfiles, a.p.CFiles...)
...@@ -914,7 +975,7 @@ func (b *builder) includeArgs(flag string, all []*action) []string { ...@@ -914,7 +975,7 @@ func (b *builder) includeArgs(flag string, all []*action) []string {
if dir := a1.pkgdir; dir == a1.p.build.PkgRoot && !incMap[dir] { if dir := a1.pkgdir; dir == a1.p.build.PkgRoot && !incMap[dir] {
incMap[dir] = true incMap[dir] = true
if _, ok := buildToolchain.(gccgcToolchain); ok { if _, ok := buildToolchain.(gccgcToolchain); ok {
dir = filepath.Join(dir, "gccgo") dir = filepath.Join(dir, "gccgo_"+goos+"_"+goarch)
} else { } else {
dir = filepath.Join(dir, goos+"_"+goarch) dir = filepath.Join(dir, goos+"_"+goarch)
if buildRace { if buildRace {
......
...@@ -95,6 +95,9 @@ The build flags are shared by the build, install, run, and test commands: ...@@ -95,6 +95,9 @@ The build flags are shared by the build, install, run, and test commands:
See the documentation for the go/build package for See the documentation for the go/build package for
more information about build tags. 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 specifying packages, see 'go help packages'.
For more about where packages and binaries are installed, For more about where packages and binaries are installed,
see 'go help gopath'. see 'go help gopath'.
...@@ -272,7 +275,7 @@ List packages ...@@ -272,7 +275,7 @@ List packages
Usage: 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. List lists the packages named by the import paths, one per line.
...@@ -301,6 +304,7 @@ which calls strings.Join. The struct being passed to the template is: ...@@ -301,6 +304,7 @@ which calls strings.Join. The struct being passed to the template is:
// Source files // Source files
GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles) GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
CgoFiles []string // .go sources files that import "C" CgoFiles []string // .go sources files that import "C"
IgnoredGoFiles []string // .go sources ignored due to build constraints
CFiles []string // .c source files CFiles []string // .c source files
HFiles []string // .h source files HFiles []string // .h source files
SFiles []string // .s source files SFiles []string // .s source files
...@@ -341,6 +345,9 @@ printing. Erroneous packages will have a non-empty ImportPath and ...@@ -341,6 +345,9 @@ printing. Erroneous packages will have a non-empty ImportPath and
a non-nil Error field; other information may or may not be missing a non-nil Error field; other information may or may not be missing
(zeroed). (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'. For more about specifying packages, see 'go help packages'.
...@@ -376,6 +383,7 @@ followed by detailed output for each failed package. ...@@ -376,6 +383,7 @@ followed by detailed output for each failed package.
'Go test' recompiles each package along with any files with names matching 'Go test' recompiles each package along with any files with names matching
the file pattern "*_test.go". These additional files can contain test functions, the file pattern "*_test.go". These additional files can contain test functions,
benchmark functions, and example functions. See 'go help testfunc' for more. 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 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. with source in the current directory, including tests, and runs the tests.
...@@ -748,6 +756,9 @@ will compile the test binary and then run it as ...@@ -748,6 +756,9 @@ will compile the test binary and then run it as
pkg.test -test.v -test.cpuprofile=prof.out -dir=testdata -update 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 Description of testing functions
...@@ -763,8 +774,8 @@ A benchmark function is one named BenchmarkXXX and should have the signature, ...@@ -763,8 +774,8 @@ A benchmark function is one named BenchmarkXXX and should have the signature,
func BenchmarkXXX(b *testing.B) { ... } func BenchmarkXXX(b *testing.B) { ... }
An example function is similar to a test function but, instead of using *testing.T An example function is similar to a test function but, instead of using
to report success or failure, prints output to os.Stdout and os.Stderr. *testing.T to report success or failure, prints output to os.Stdout.
That output is compared against the function's "Output:" comment, which That output is compared against the function's "Output:" comment, which
must be the last comment in the function body (see example below). An 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 example with no such comment, or with no text after "Output:" is compiled
......
...@@ -25,6 +25,6 @@ func runFix(cmd *Command, args []string) { ...@@ -25,6 +25,6 @@ func runFix(cmd *Command, args []string) {
// Use pkg.gofiles instead of pkg.Dir so that // Use pkg.gofiles instead of pkg.Dir so that
// the command only applies to this package, // the command only applies to this package,
// not to packages in subdirectories. // not to packages in subdirectories.
run(stringList(tool("fix"), relPaths(pkg.gofiles))) run(stringList(tool("fix"), relPaths(pkg.allgofiles)))
} }
} }
...@@ -34,7 +34,7 @@ func runFmt(cmd *Command, args []string) { ...@@ -34,7 +34,7 @@ func runFmt(cmd *Command, args []string) {
// Use pkg.gofiles instead of pkg.Dir so that // Use pkg.gofiles instead of pkg.Dir so that
// the command only applies to this package, // the command only applies to this package,
// not to packages in subdirectories. // not to packages in subdirectories.
run(stringList("gofmt", "-l", "-w", relPaths(pkg.gofiles))) run(stringList("gofmt", "-l", "-w", relPaths(pkg.allgofiles)))
} }
} }
......
...@@ -204,7 +204,7 @@ func download(arg string, stk *importStack) { ...@@ -204,7 +204,7 @@ func download(arg string, stk *importStack) {
// due to wildcard expansion. // due to wildcard expansion.
for _, p := range pkgs { for _, p := range pkgs {
if *getFix { if *getFix {
run(stringList(tool("fix"), relPaths(p.gofiles))) run(stringList(tool("fix"), relPaths(p.allgofiles)))
// The imports might have changed, so reload again. // The imports might have changed, so reload again.
p = reloadPackage(arg, stk) p = reloadPackage(arg, stk)
......
...@@ -14,7 +14,7 @@ import ( ...@@ -14,7 +14,7 @@ import (
) )
var cmdList = &Command{ var cmdList = &Command{
UsageLine: "list [-e] [-f format] [-json] [packages]", UsageLine: "list [-e] [-f format] [-json] [-tags 'tag list'] [packages]",
Short: "list packages", Short: "list packages",
Long: ` Long: `
List lists the packages named by the import paths, one per line. List lists the packages named by the import paths, one per line.
...@@ -44,6 +44,7 @@ which calls strings.Join. The struct being passed to the template is: ...@@ -44,6 +44,7 @@ which calls strings.Join. The struct being passed to the template is:
// Source files // Source files
GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles) GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
CgoFiles []string // .go sources files that import "C" CgoFiles []string // .go sources files that import "C"
IgnoredGoFiles []string // .go sources ignored due to build constraints
CFiles []string // .c source files CFiles []string // .c source files
HFiles []string // .h source files HFiles []string // .h source files
SFiles []string // .s source files SFiles []string // .s source files
...@@ -84,6 +85,9 @@ printing. Erroneous packages will have a non-empty ImportPath and ...@@ -84,6 +85,9 @@ printing. Erroneous packages will have a non-empty ImportPath and
a non-nil Error field; other information may or may not be missing a non-nil Error field; other information may or may not be missing
(zeroed). (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'. For more about specifying packages, see 'go help packages'.
`, `,
} }
...@@ -91,6 +95,7 @@ For more about specifying packages, see 'go help packages'. ...@@ -91,6 +95,7 @@ For more about specifying packages, see 'go help packages'.
func init() { func init() {
cmdList.Run = runList // break init cycle cmdList.Run = runList // break init cycle
cmdList.Flag.Var(buildCompiler{}, "compiler", "") cmdList.Flag.Var(buildCompiler{}, "compiler", "")
cmdList.Flag.Var((*stringsFlag)(&buildContext.BuildTags), "tags", "")
} }
var listE = cmdList.Flag.Bool("e", false, "") var listE = cmdList.Flag.Bool("e", false, "")
......
...@@ -129,6 +129,10 @@ func main() { ...@@ -129,6 +129,10 @@ func main() {
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) {
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) { 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) 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) os.Exit(2)
......
...@@ -38,6 +38,7 @@ type Package struct { ...@@ -38,6 +38,7 @@ type Package struct {
// Source files // Source files
GoFiles []string `json:",omitempty"` // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles) GoFiles []string `json:",omitempty"` // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
CgoFiles []string `json:",omitempty"` // .go sources files that import "C" 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 CFiles []string `json:",omitempty"` // .c source files
HFiles []string `json:",omitempty"` // .h source files HFiles []string `json:",omitempty"` // .h source files
SFiles []string `json:",omitempty"` // .s source files SFiles []string `json:",omitempty"` // .s source files
...@@ -71,12 +72,14 @@ type Package struct { ...@@ -71,12 +72,14 @@ type Package struct {
imports []*Package imports []*Package
deps []*Package deps []*Package
gofiles []string // GoFiles+CgoFiles+TestGoFiles+XTestGoFiles files, absolute paths 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) target string // installed file for this package (may be executable)
fake bool // synthesized package fake bool // synthesized package
forceBuild bool // this package must be rebuilt forceBuild bool // this package must be rebuilt
forceLibrary bool // this package is a library (even if named "main") forceLibrary bool // this package is a library (even if named "main")
local bool // imported via local path (./ or ../) local bool // imported via local path (./ or ../)
localPrefix string // interpret ./ and ../ imports relative to this prefix localPrefix string // interpret ./ and ../ imports relative to this prefix
exeName string // desired name for temporary executable
} }
func (p *Package) copyBuild(pp *build.Package) { func (p *Package) copyBuild(pp *build.Package) {
...@@ -92,6 +95,7 @@ func (p *Package) copyBuild(pp *build.Package) { ...@@ -92,6 +95,7 @@ func (p *Package) copyBuild(pp *build.Package) {
p.Standard = p.Goroot && p.ImportPath != "" && !strings.Contains(p.ImportPath, ".") p.Standard = p.Goroot && p.ImportPath != "" && !strings.Contains(p.ImportPath, ".")
p.GoFiles = pp.GoFiles p.GoFiles = pp.GoFiles
p.CgoFiles = pp.CgoFiles p.CgoFiles = pp.CgoFiles
p.IgnoredGoFiles = pp.IgnoredGoFiles
p.CFiles = pp.CFiles p.CFiles = pp.CFiles
p.HFiles = pp.HFiles p.HFiles = pp.HFiles
p.SFiles = pp.SFiles p.SFiles = pp.SFiles
...@@ -318,11 +322,13 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package ...@@ -318,11 +322,13 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package
// Install cross-compiled binaries to subdirectories of bin. // Install cross-compiled binaries to subdirectories of bin.
elem = full elem = full
} }
if p.build.BinDir != "" {
p.target = filepath.Join(p.build.BinDir, elem) p.target = filepath.Join(p.build.BinDir, elem)
}
if p.Goroot && (isGoTool[p.ImportPath] || strings.HasPrefix(p.ImportPath, "exp/")) { if p.Goroot && (isGoTool[p.ImportPath] || strings.HasPrefix(p.ImportPath, "exp/")) {
p.target = filepath.Join(gorootPkg, "tool", full) p.target = filepath.Join(gorootPkg, "tool", full)
} }
if buildContext.GOOS == "windows" { if p.target != "" && buildContext.GOOS == "windows" {
p.target += ".exe" p.target += ".exe"
} }
} else if p.local { } else if p.local {
...@@ -357,6 +363,13 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package ...@@ -357,6 +363,13 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package
} }
sort.Strings(p.gofiles) 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. // Build list of imported packages and full dependency list.
imports := make([]*Package, 0, len(p.Imports)) imports := make([]*Package, 0, len(p.Imports))
deps := make(map[string]bool) deps := make(map[string]bool)
...@@ -431,7 +444,7 @@ func (p *Package) swigSoname(file string) string { ...@@ -431,7 +444,7 @@ func (p *Package) swigSoname(file string) string {
func (p *Package) swigDir(ctxt *build.Context) string { func (p *Package) swigDir(ctxt *build.Context) string {
dir := p.build.PkgRoot dir := p.build.PkgRoot
if ctxt.Compiler == "gccgo" { if ctxt.Compiler == "gccgo" {
dir = filepath.Join(dir, "gccgo") dir = filepath.Join(dir, "gccgo_"+ctxt.GOOS+"_"+ctxt.GOARCH)
} else { } else {
dir = filepath.Join(dir, ctxt.GOOS+"_"+ctxt.GOARCH) dir = filepath.Join(dir, ctxt.GOOS+"_"+ctxt.GOARCH)
} }
...@@ -596,12 +609,23 @@ func loadPackage(arg string, stk *importStack) *Package { ...@@ -596,12 +609,23 @@ func loadPackage(arg string, stk *importStack) *Package {
arg = sub arg = sub
} }
} }
if strings.HasPrefix(arg, "cmd/") && !strings.Contains(arg[4:], "/") { if strings.HasPrefix(arg, "cmd/") {
if p := cmdCache[arg]; p != nil { if p := cmdCache[arg]; p != nil {
return p return p
} }
stk.push(arg) stk.push(arg)
defer stk.pop() 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, err := buildContext.ImportDir(filepath.Join(gorootSrc, arg), 0)
bp.ImportPath = arg bp.ImportPath = arg
bp.Goroot = true bp.Goroot = true
......
...@@ -46,6 +46,13 @@ func runRun(cmd *Command, args []string) { ...@@ -46,6 +46,13 @@ func runRun(cmd *Command, args []string) {
if len(files) == 0 { if len(files) == 0 {
fatalf("go run: no go files listed") 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) p := goFilesPackage(files)
if p.Error != nil { if p.Error != nil {
fatalf("%s", p.Error) fatalf("%s", p.Error)
...@@ -58,6 +65,13 @@ func runRun(cmd *Command, args []string) { ...@@ -58,6 +65,13 @@ func runRun(cmd *Command, args []string) {
fatalf("go run: cannot run non-main package") fatalf("go run: cannot run non-main package")
} }
p.target = "" // must build - not up to date 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) a1 := b.action(modeBuild, modeBuild, p)
a := &action{f: (*builder).runProgram, args: cmdArgs, deps: []*action{a1}} a := &action{f: (*builder).runProgram, args: cmdArgs, deps: []*action{a1}}
b.do(a) b.do(a)
......
...@@ -183,7 +183,7 @@ fi ...@@ -183,7 +183,7 @@ fi
# 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
d=$(mktemp -d) d=$(mktemp -d -t testgo)
mkdir -p $d/src/pkg mkdir -p $d/src/pkg
if GOPATH= GOROOT=$d ./testgo get -d code.google.com/p/go.codereview/cmd/hgpatch ; then 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' echo 'go get code.google.com/p/go.codereview/cmd/hgpatch should not succeed with $GOPATH unset'
...@@ -191,7 +191,7 @@ if GOPATH= GOROOT=$d ./testgo get -d code.google.com/p/go.codereview/cmd/hgpatch ...@@ -191,7 +191,7 @@ if GOPATH= GOROOT=$d ./testgo get -d code.google.com/p/go.codereview/cmd/hgpatch
fi fi
rm -rf $d rm -rf $d
# Test that with GOPATH=$GOROOT, go get should fail # Test that with GOPATH=$GOROOT, go get should fail
d=$(mktemp -d) d=$(mktemp -d -t testgo)
mkdir -p $d/src/pkg mkdir -p $d/src/pkg
if GOPATH=$d GOROOT=$d ./testgo get -d code.google.com/p/go.codereview/cmd/hgpatch ; then 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' echo 'go get code.google.com/p/go.codereview/cmd/hgpatch should not succeed with GOPATH=$GOROOT'
...@@ -199,6 +199,86 @@ if GOPATH=$d GOROOT=$d ./testgo get -d code.google.com/p/go.codereview/cmd/hgpat ...@@ -199,6 +199,86 @@ if GOPATH=$d GOROOT=$d ./testgo get -d code.google.com/p/go.codereview/cmd/hgpat
fi fi
rm -rf $d 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 # clean up
rm -rf testdata/bin testdata/bin1 rm -rf testdata/bin testdata/bin1
rm -f testgo rm -f testgo
......
...@@ -48,6 +48,7 @@ followed by detailed output for each failed package. ...@@ -48,6 +48,7 @@ followed by detailed output for each failed package.
'Go test' recompiles each package along with any files with names matching 'Go test' recompiles each package along with any files with names matching
the file pattern "*_test.go". These additional files can contain test functions, the file pattern "*_test.go". These additional files can contain test functions,
benchmark functions, and example functions. See 'go help testfunc' for more. 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 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. with source in the current directory, including tests, and runs the tests.
...@@ -156,6 +157,9 @@ here are passed through unaltered. For instance, the command ...@@ -156,6 +157,9 @@ here are passed through unaltered. For instance, the command
will compile the test binary and then run it as will compile the test binary and then run it as
pkg.test -test.v -test.cpuprofile=prof.out -dir=testdata -update 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 @@ See the documentation of the testing package for more information. ...@@ -206,6 +210,7 @@ See the documentation of the testing package for more information.
var ( var (
testC bool // -c flag testC bool // -c flag
testProfile bool // some profiling flag
testI bool // -i flag testI bool // -i flag
testV bool // -v flag testV bool // -v flag
testFiles []string // -file flag(s) TODO: not respected testFiles []string // -file flag(s) TODO: not respected
...@@ -231,12 +236,15 @@ func runTest(cmd *Command, args []string) { ...@@ -231,12 +236,15 @@ func runTest(cmd *Command, args []string) {
if testC && len(pkgs) != 1 { if testC && len(pkgs) != 1 {
fatalf("cannot use -c flag with multiple packages") 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 // 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 // to that timeout plus one minute. This is a backup alarm in case
// the test wedges with a goroutine spinning and its background // the test wedges with a goroutine spinning and its background
// timer does not get a chance to fire. // 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 testKillTimeout = dt + 1*time.Minute
} }
...@@ -427,7 +435,14 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action, ...@@ -427,7 +435,14 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,
// Use last element of import path, not package name. // Use last element of import path, not package name.
// They differ when package name is "main". // 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" testBinary := elem + ".test"
// The ptest package needs to be importable under the // The ptest package needs to be importable under the
...@@ -554,14 +569,17 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action, ...@@ -554,14 +569,17 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,
a.target = filepath.Join(testDir, testBinary) + exeSuffix a.target = filepath.Join(testDir, testBinary) + exeSuffix
pmainAction := a pmainAction := a
if testC { if testC || testProfile {
// -c flag: create action to copy binary to ./test.out. // -c or profiling flag: create action to copy binary to ./test.out.
runAction = &action{ runAction = &action{
f: (*builder).install, f: (*builder).install,
deps: []*action{pmainAction}, deps: []*action{pmainAction},
p: pmain, 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 printAction = &action{p: p, deps: []*action{runAction}} // nop
} else { } else {
// run test // run test
...@@ -655,7 +673,7 @@ func (b *builder) runTest(a *action) error { ...@@ -655,7 +673,7 @@ func (b *builder) runTest(a *action) error {
case <-tick.C: case <-tick.C:
cmd.Process.Kill() cmd.Process.Kill()
err = <-done err = <-done
fmt.Fprintf(&buf, "*** Test killed: ran too long.\n") fmt.Fprintf(&buf, "*** Test killed: ran too long (%v).\n", testKillTimeout)
} }
tick.Stop() tick.Stop()
} }
......
...@@ -134,6 +134,7 @@ func testFlags(args []string) (packageNames, passToTest []string) { ...@@ -134,6 +134,7 @@ func testFlags(args []string) (packageNames, passToTest []string) {
passToTest = append(passToTest, args[i]) passToTest = append(passToTest, args[i])
continue continue
} }
var err error
switch f.name { switch f.name {
// bool flags. // bool flags.
case "a", "c", "i", "n", "x", "v", "work", "race": case "a", "c", "i", "n", "x", "v", "work", "race":
...@@ -141,11 +142,20 @@ func testFlags(args []string) (packageNames, passToTest []string) { ...@@ -141,11 +142,20 @@ func testFlags(args []string) (packageNames, passToTest []string) {
case "p": case "p":
setIntFlag(&buildP, value) setIntFlag(&buildP, value)
case "gcflags": 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": 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": 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": case "tags":
buildContext.BuildTags = strings.Fields(value) buildContext.BuildTags = strings.Fields(value)
case "compiler": case "compiler":
...@@ -157,6 +167,8 @@ func testFlags(args []string) (packageNames, passToTest []string) { ...@@ -157,6 +167,8 @@ func testFlags(args []string) (packageNames, passToTest []string) {
testBench = true testBench = true
case "timeout": case "timeout":
testTimeout = value testTimeout = value
case "blockprofile", "cpuprofile", "memprofile":
testProfile = true
} }
if extraWord { if extraWord {
i++ i++
......
...@@ -100,7 +100,14 @@ func runTool(cmd *Command, args []string) { ...@@ -100,7 +100,14 @@ func runTool(cmd *Command, args []string) {
} }
err := toolCmd.Run() err := toolCmd.Run()
if err != nil { if err != nil {
// 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) fmt.Fprintf(os.Stderr, "go tool %s: %s\n", toolName, err)
}
setExitStatus(1) setExitStatus(1)
return return
} }
......
...@@ -32,6 +32,6 @@ func runVet(cmd *Command, args []string) { ...@@ -32,6 +32,6 @@ func runVet(cmd *Command, args []string) {
// Use pkg.gofiles instead of pkg.Dir so that // Use pkg.gofiles instead of pkg.Dir so that
// the command only applies to this package, // the command only applies to this package,
// not to packages in subdirectories. // not to packages in subdirectories.
run(tool("vet"), relPaths(pkg.gofiles)) run(tool("vet"), relPaths(stringList(pkg.allgofiles, pkg.IgnoredGoFiles)))
} }
} }
...@@ -117,12 +117,27 @@ func (ctxt *Context) hasSubdir(root, dir string) (rel string, ok bool) { ...@@ -117,12 +117,27 @@ func (ctxt *Context) hasSubdir(root, dir string) (rel string, ok bool) {
return f(root, dir) return f(root, dir)
} }
if p, err := filepath.EvalSymlinks(root); err == nil { // Try using paths we received.
root = p 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) const sep = string(filepath.Separator)
root = filepath.Clean(root) root = filepath.Clean(root)
if !strings.HasSuffix(root, sep) { if !strings.HasSuffix(root, sep) {
...@@ -181,6 +196,21 @@ func (ctxt *Context) gopath() []string { ...@@ -181,6 +196,21 @@ func (ctxt *Context) gopath() []string {
// Do not get confused by this common mistake. // Do not get confused by this common mistake.
continue 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) all = append(all, p)
} }
return all return all
...@@ -286,6 +316,7 @@ type Package struct { ...@@ -286,6 +316,7 @@ type Package struct {
// Source files // Source files
GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles) GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
CgoFiles []string // .go source files that import "C" CgoFiles []string // .go source files that import "C"
IgnoredGoFiles []string // .go source files ignored for this build
CFiles []string // .c source files CFiles []string // .c source files
HFiles []string // .h source files HFiles []string // .h source files
SFiles []string // .s source files SFiles []string // .s source files
...@@ -519,15 +550,20 @@ Found: ...@@ -519,15 +550,20 @@ Found:
strings.HasPrefix(name, ".") { strings.HasPrefix(name, ".") {
continue continue
} }
if !ctxt.UseAllFiles && !ctxt.goodOSArchFile(name) {
continue
}
i := strings.LastIndex(name, ".") i := strings.LastIndex(name, ".")
if i < 0 { if i < 0 {
i = len(name) i = len(name)
} }
ext := name[i:] ext := name[i:]
if !ctxt.UseAllFiles && !ctxt.goodOSArchFile(name) {
if ext == ".go" {
p.IgnoredGoFiles = append(p.IgnoredGoFiles, name)
}
continue
}
switch ext { switch ext {
case ".go", ".c", ".s", ".h", ".S", ".swig", ".swigcxx": case ".go", ".c", ".s", ".h", ".S", ".swig", ".swigcxx":
// tentatively okay - read to make sure // tentatively okay - read to make sure
...@@ -561,6 +597,9 @@ Found: ...@@ -561,6 +597,9 @@ Found:
// Look for +build comments to accept or reject the file. // Look for +build comments to accept or reject the file.
if !ctxt.UseAllFiles && !ctxt.shouldBuild(data) { if !ctxt.UseAllFiles && !ctxt.shouldBuild(data) {
if ext == ".go" {
p.IgnoredGoFiles = append(p.IgnoredGoFiles, name)
}
continue continue
} }
...@@ -593,6 +632,7 @@ Found: ...@@ -593,6 +632,7 @@ Found:
pkg := pf.Name.Name pkg := pf.Name.Name
if pkg == "documentation" { if pkg == "documentation" {
p.IgnoredGoFiles = append(p.IgnoredGoFiles, name)
continue continue
} }
......
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