diff options
author | Michael Stapelberg <stapelberg@debian.org> | 2013-03-04 21:27:36 +0100 |
---|---|---|
committer | Michael Stapelberg <michael@stapelberg.de> | 2013-03-04 21:27:36 +0100 |
commit | 04b08da9af0c450d645ab7389d1467308cfc2db8 (patch) | |
tree | db247935fa4f2f94408edc3acd5d0d4f997aa0d8 /src/cmd/go | |
parent | 917c5fb8ec48e22459d77e3849e6d388f93d3260 (diff) | |
download | golang-04b08da9af0c450d645ab7389d1467308cfc2db8.tar.gz |
Imported Upstream version 1.1~hg20130304upstream/1.1_hg20130304
Diffstat (limited to 'src/cmd/go')
-rw-r--r-- | src/cmd/go/build.go | 679 | ||||
-rw-r--r-- | src/cmd/go/clean.go | 42 | ||||
-rw-r--r-- | src/cmd/go/doc.go | 143 | ||||
-rw-r--r-- | src/cmd/go/env.go | 15 | ||||
-rw-r--r-- | src/cmd/go/fix.go | 2 | ||||
-rw-r--r-- | src/cmd/go/fmt.go | 2 | ||||
-rw-r--r-- | src/cmd/go/get.go | 33 | ||||
-rw-r--r-- | src/cmd/go/help.go | 2 | ||||
-rw-r--r-- | src/cmd/go/http.go | 10 | ||||
-rw-r--r-- | src/cmd/go/list.go | 78 | ||||
-rw-r--r-- | src/cmd/go/main.go | 120 | ||||
-rw-r--r-- | src/cmd/go/pkg.go | 183 | ||||
-rw-r--r-- | src/cmd/go/run.go | 24 | ||||
-rw-r--r-- | src/cmd/go/signal.go | 31 | ||||
-rw-r--r-- | src/cmd/go/signal_notunix.go | 13 | ||||
-rw-r--r-- | src/cmd/go/signal_unix.go | 14 | ||||
-rwxr-xr-x | src/cmd/go/test.bash | 208 | ||||
-rw-r--r-- | src/cmd/go/test.go | 161 | ||||
-rw-r--r-- | src/cmd/go/testdata/example1_test.go | 23 | ||||
-rw-r--r-- | src/cmd/go/testdata/example2_test.go | 21 | ||||
-rw-r--r-- | src/cmd/go/testflag.go | 35 | ||||
-rw-r--r-- | src/cmd/go/tool.go | 21 | ||||
-rw-r--r-- | src/cmd/go/vcs.go | 13 | ||||
-rw-r--r-- | src/cmd/go/version.go | 2 |
24 files changed, 1569 insertions, 306 deletions
diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go index 767ddfd40..38fc43ef1 100644 --- a/src/cmd/go/build.go +++ b/src/cmd/go/build.go @@ -8,6 +8,7 @@ import ( "bytes" "container/heap" "errors" + "flag" "fmt" "go/build" "io" @@ -59,7 +60,12 @@ The build flags are shared by the build, install, run, and test commands: do not delete it when exiting. -x print the commands. + -race + enable data race detection. + Supported only on linux/amd64, darwin/amd64 and windows/amd64. + -ccflags 'arg list' + arguments to pass on each 5c, 6c, or 8c compiler invocation -compiler name name of compiler to use, as in runtime.Compiler (gccgo or gc) -gccgoflags 'arg list' @@ -73,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 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'. @@ -99,8 +108,10 @@ var buildX bool // -x flag var buildO = cmdBuild.Flag.String("o", "", "output file") var buildWork bool // -work flag var buildGcflags []string // -gcflags flag +var buildCcflags []string // -ccflags flag var buildLdflags []string // -ldflags flag var buildGccgoflags []string // -gccgoflags flag +var buildRace bool // -race flag var buildContext = build.Default var buildToolchain toolchain = noToolchain{} @@ -115,7 +126,7 @@ func (c buildCompiler) Set(value string) error { case "gc": buildToolchain = gcToolchain{} case "gccgo": - buildToolchain = gccgcToolchain{} + buildToolchain = gccgoToolchain{} default: return fmt.Errorf("unknown compiler %q", value) } @@ -132,7 +143,7 @@ func init() { case "gc": buildToolchain = gcToolchain{} case "gccgo": - buildToolchain = gccgcToolchain{} + buildToolchain = gccgoToolchain{} } } @@ -146,10 +157,12 @@ func addBuildFlags(cmd *Command) { cmd.Flag.BoolVar(&buildX, "x", false, "") cmd.Flag.BoolVar(&buildWork, "work", false, "") cmd.Flag.Var((*stringsFlag)(&buildGcflags), "gcflags", "") + cmd.Flag.Var((*stringsFlag)(&buildCcflags), "ccflags", "") cmd.Flag.Var((*stringsFlag)(&buildLdflags), "ldflags", "") cmd.Flag.Var((*stringsFlag)(&buildGccgoflags), "gccgoflags", "") cmd.Flag.Var((*stringsFlag)(&buildContext.BuildTags), "tags", "") cmd.Flag.Var(buildCompiler{}, "compiler", "") + cmd.Flag.BoolVar(&buildRace, "race", false, "") } func addBuildFlagsNX(cmd *Command) { @@ -157,11 +170,52 @@ func addBuildFlagsNX(cmd *Command) { 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 { @@ -169,6 +223,7 @@ func (v *stringsFlag) String() string { } func runBuild(cmd *Command, args []string) { + raceInit() var b builder b.init() @@ -179,6 +234,21 @@ func runBuild(cmd *Command, args []string) { *buildO += exeSuffix } + // sanity check some often mis-used options + switch buildContext.Compiler { + case "gccgo": + if len(buildGcflags) != 0 { + fmt.Println("go build: when using gccgo toolchain, please pass compiler flags using -gccgoflags, not -gcflags") + } + if len(buildLdflags) != 0 { + fmt.Println("go build: when using gccgo toolchain, please pass linker flags using -gccgoflags, not -ldflags") + } + case "gc": + if len(buildGccgoflags) != 0 { + fmt.Println("go build: when using gc toolchain, please pass compile flags using -gcflags, and linker flags using -ldflags") + } + } + if *buildO != "" { if len(pkgs) > 1 { fatalf("go build: cannot use -o with multiple packages") @@ -213,11 +283,12 @@ See also: go build, go get, go clean. } func runInstall(cmd *Command, args []string) { + raceInit() pkgs := packagesForBuild(args) 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() @@ -437,7 +508,7 @@ func (b *builder) action(mode buildMode, depMode buildMode, p *Package) *action // using cgo, to make sure we do not overwrite the binary while // a package is using it. If this is a cross-build, then the cgo we // are writing is not the cgo we need to use. - if goos == runtime.GOOS && goarch == runtime.GOARCH { + if goos == runtime.GOOS && goarch == runtime.GOARCH && !buildRace { if len(p.CgoFiles) > 0 || p.Standard && p.ImportPath == "runtime/cgo" { var stk importStack p1 := loadPackage("cmd/cgo", &stk) @@ -456,7 +527,7 @@ func (b *builder) action(mode buildMode, depMode buildMode, p *Package) *action return a } // gccgo standard library is "fake" too. - if _, ok := buildToolchain.(gccgcToolchain); ok { + if _, ok := buildToolchain.(gccgoToolchain); ok { // the target name is needed for cgo. a.target = p.target return a @@ -487,9 +558,18 @@ func (b *builder) action(mode buildMode, depMode buildMode, p *Package) *action 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 } } @@ -535,7 +615,6 @@ func (b *builder) do(root *action) { } b.readySema = make(chan bool, len(all)) - done := make(chan bool) // Initialize per-action execution state. for _, a := range all { @@ -583,10 +662,11 @@ func (b *builder) do(root *action) { if a == root { close(b.readySema) - done <- true } } + var wg sync.WaitGroup + // Kick off goroutines according to parallelism. // If we are using the -n flag (just printing commands) // drop the parallelism to 1, both to make the output @@ -596,19 +676,40 @@ func (b *builder) do(root *action) { par = 1 } for i := 0; i < par; i++ { + wg.Add(1) go func() { - for _ = range b.readySema { - // Receiving a value from b.sema entitles - // us to take from the ready queue. - b.exec.Lock() - a := b.ready.pop() - b.exec.Unlock() - handle(a) + defer wg.Done() + for { + select { + case _, ok := <-b.readySema: + if !ok { + return + } + // Receiving a value from b.readySema entitles + // us to take from the ready queue. + b.exec.Lock() + a := b.ready.pop() + b.exec.Unlock() + handle(a) + case <-interrupted: + setExitStatus(1) + return + } } }() } - <-done + wg.Wait() +} + +// hasString reports whether s appears in the list of strings. +func hasString(strings []string, s string) bool { + for _, t := range strings { + if s == t { + return true + } + } + return false } // build is the action for building a single package or command. @@ -631,12 +732,25 @@ func (b *builder) build(a *action) (err error) { fmt.Fprintf(os.Stderr, "%s\n", a.p.ImportPath) } + if a.p.Standard && a.p.ImportPath == "runtime" && buildContext.Compiler == "gc" && + !hasString(a.p.HFiles, "zasm_"+buildContext.GOOS+"_"+buildContext.GOARCH+".h") { + return fmt.Errorf("%s/%s must be bootstrapped using make.bash", buildContext.GOOS, buildContext.GOARCH) + } + // Make build directory. obj := a.objdir if err := b.mkdir(obj); err != nil { 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...) @@ -680,16 +794,37 @@ func (b *builder) build(a *action) (err error) { gofiles = append(gofiles, outGo...) } + // Run SWIG. + if a.p.usesSwig() { + // In a package using SWIG, any .c or .s files are + // compiled with gcc. + gccfiles := append(cfiles, sfiles...) + cfiles = nil + sfiles = nil + outGo, outObj, err := b.swig(a.p, obj, gccfiles) + if err != nil { + return err + } + cgoObjects = append(cgoObjects, outObj...) + gofiles = append(gofiles, outGo...) + } + // Prepare Go import path list. inc := b.includeArgs("-I", a.deps) // Compile Go. if len(gofiles) > 0 { - if out, err := buildToolchain.gc(b, a.p, obj, inc, gofiles); err != nil { + ofile, out, err := buildToolchain.gc(b, a.p, obj, inc, gofiles) + if len(out) > 0 { + b.showOutput(a.p.Dir, a.p.ImportPath, b.processOutput(out)) + if err != nil { + return errPrintedOutput + } + } + if err != nil { return err - } else { - objects = append(objects, out) } + objects = append(objects, ofile) } // Copy .h files named for goos or goarch or goos_goarch @@ -718,8 +853,13 @@ func (b *builder) build(a *action) (err error) { } } + objExt := archChar + if _, ok := buildToolchain.(gccgoToolchain); ok { + objExt = "o" + } + for _, file := range cfiles { - out := file[:len(file)-len(".c")] + "." + archChar + out := file[:len(file)-len(".c")] + "." + objExt if err := buildToolchain.cc(b, a.p, obj, obj+out, file); err != nil { return err } @@ -728,7 +868,7 @@ func (b *builder) build(a *action) (err error) { // Assemble .s files. for _, file := range sfiles { - out := file[:len(file)-len(".s")] + "." + archChar + out := file[:len(file)-len(".s")] + "." + objExt if err := buildToolchain.asm(b, a.p, obj, obj+out, file); err != nil { return err } @@ -795,6 +935,20 @@ func (b *builder) install(a *action) (err error) { defer os.Remove(a1.target) } + if a.p.usesSwig() { + for _, f := range stringList(a.p.SwigFiles, a.p.SwigCXXFiles) { + dir = a.p.swigDir(&buildContext) + if err := b.mkdir(dir); err != nil { + return err + } + soname := a.p.swigSoname(f) + target := filepath.Join(dir, soname) + if err = b.copyFile(a, target, soname, perm); err != nil { + return err + } + } + } + return b.copyFile(a, a.target, a1.target, perm) } @@ -826,10 +980,13 @@ func (b *builder) includeArgs(flag string, all []*action) []string { for _, a1 := range all { if dir := a1.pkgdir; dir == a1.p.build.PkgRoot && !incMap[dir] { incMap[dir] = true - if _, ok := buildToolchain.(gccgcToolchain); ok { - dir = filepath.Join(dir, "gccgo") + if _, ok := buildToolchain.(gccgoToolchain); ok { + dir = filepath.Join(dir, "gccgo_"+goos+"_"+goarch) } else { dir = filepath.Join(dir, goos+"_"+goarch) + if buildRace { + dir += "_race" + } } inc = append(inc, flag, dir) } @@ -905,6 +1062,8 @@ var objectMagic = [][]byte{ {0xCE, 0xFA, 0xED, 0xFE}, // Mach-O little-endian 32-bit {0xCF, 0xFA, 0xED, 0xFE}, // Mach-O little-endian 64-bit {0x4d, 0x5a, 0x90, 0x00, 0x03, 0x00, 0x04, 0x00}, // PE (Windows) as generated by 6l/8l + {0x00, 0x00, 0x01, 0xEB}, // Plan 9 i386 + {0x00, 0x00, 0x8a, 0x97}, // Plan 9 amd64 } func isObject(s string) bool { @@ -1024,19 +1183,18 @@ func relPaths(paths []string) []string { // print this error. var errPrintedOutput = errors.New("already printed output - no need to show error") +var cgoLine = regexp.MustCompile(`\[[^\[\]]+\.cgo1\.go:[0-9]+\]`) + // run runs the command given by cmdline in the directory dir. // If the command fails, run prints information about the failure // and returns a non-nil error. func (b *builder) run(dir string, desc string, cmdargs ...interface{}) error { out, err := b.runOut(dir, desc, cmdargs...) if len(out) > 0 { - if out[len(out)-1] != '\n' { - out = append(out, '\n') - } if desc == "" { desc = b.fmtcmd(dir, "%s", strings.Join(stringList(cmdargs...), " ")) } - b.showOutput(dir, desc, string(out)) + b.showOutput(dir, desc, b.processOutput(out)) if err != nil { err = errPrintedOutput } @@ -1044,6 +1202,23 @@ func (b *builder) run(dir string, desc string, cmdargs ...interface{}) error { return err } +// processOutput prepares the output of runOut to be output to the console. +func (b *builder) processOutput(out []byte) string { + if out[len(out)-1] != '\n' { + out = append(out, '\n') + } + messages := string(out) + // Fix up output referring to cgo-generated code to be more readable. + // Replace x.go:19[/tmp/.../x.cgo1.go:18] with x.go:19. + // Replace _Ctype_foo with C.foo. + // If we're using -x, assume we're debugging and want the full dump, so disable the rewrite. + if !buildX && cgoLine.MatchString(messages) { + messages = cgoLine.ReplaceAllString(messages, "") + messages = strings.Replace(messages, "type _Ctype_", "type C.", -1) + } + return messages +} + // runOut runs the command given by cmdline in the directory dir. // It returns the command output and any errors that occurred. func (b *builder) runOut(dir string, desc string, cmdargs ...interface{}) ([]byte, error) { @@ -1062,7 +1237,7 @@ func (b *builder) runOut(dir string, desc string, cmdargs ...interface{}) ([]byt cmd.Stdout = &buf cmd.Stderr = &buf cmd.Dir = dir - // TODO: cmd.Env + cmd.Env = envForDir(cmd.Dir) err := cmd.Run() // cmd.Run will fail on Unix if some other process has the binary @@ -1077,7 +1252,7 @@ func (b *builder) runOut(dir string, desc string, cmdargs ...interface{}) ([]byt // not worry about other processes inheriting the fd accidentally. // The answer is that running a command is fork and exec. // A child forked while the cgo fd is open inherits that fd. - // Until the child has called exec, it holds the fd open and the + // Until the child has called exec, it holds the fd open and the // kernel will not let us run cgo. Even if the child were to close // the fd explicitly, it would still be open from the time of the fork // until the time of the explicit close, and the race would remain. @@ -1161,7 +1336,7 @@ type toolchain interface { // gc runs the compiler in a specific directory on a set of files // and returns the name of the generated output file. // The compiler runs in the directory dir. - gc(b *builder, p *Package, obj string, importArgs []string, gofiles []string) (ofile string, err error) + gc(b *builder, p *Package, obj string, importArgs []string, gofiles []string) (ofile string, out []byte, err error) // cc runs the toolchain's C compiler in a directory on a C file // to produce an output file. cc(b *builder, p *Package, objdir, ofile, cfile string) error @@ -1198,8 +1373,8 @@ func (noToolchain) linker() string { return "" } -func (noToolchain) gc(b *builder, p *Package, obj string, importArgs []string, gofiles []string) (ofile string, err error) { - return "", noCompiler() +func (noToolchain) gc(b *builder, p *Package, obj string, importArgs []string, gofiles []string) (ofile string, out []byte, err error) { + return "", nil, noCompiler() } func (noToolchain) asm(b *builder, p *Package, obj, ofile, sfile string) error { @@ -1234,7 +1409,7 @@ func (gcToolchain) linker() string { return tool(archChar + "l") } -func (gcToolchain) gc(b *builder, p *Package, obj string, importArgs []string, gofiles []string) (ofile string, err error) { +func (gcToolchain) gc(b *builder, p *Package, obj string, importArgs []string, gofiles []string) (ofile string, output []byte, err error) { out := "_go_." + archChar ofile = obj + out gcargs := []string{"-p", p.ImportPath} @@ -1244,16 +1419,33 @@ func (gcToolchain) gc(b *builder, p *Package, obj string, importArgs []string, g gcargs = append(gcargs, "-+") } + // If we're giving the compiler the entire package (no C etc files), tell it that, + // so that it can give good error messages about forward declarations. + // Exceptions: a few standard packages have forward declarations for + // pieces supplied behind-the-scenes by package runtime. + extFiles := len(p.CgoFiles) + len(p.CFiles) + len(p.SFiles) + len(p.SysoFiles) + len(p.SwigFiles) + len(p.SwigCXXFiles) + if p.Standard { + switch p.ImportPath { + case "os", "runtime/pprof", "sync", "time": + extFiles++ + } + } + if extFiles == 0 { + gcargs = append(gcargs, "-complete") + } + args := stringList(tool(archChar+"g"), "-o", ofile, buildGcflags, gcargs, "-D", p.localPrefix, importArgs) for _, f := range gofiles { args = append(args, mkAbs(p.Dir, f)) } - return ofile, b.run(p.Dir, p.ImportPath, args) + + output, err = b.runOut(p.Dir, p.ImportPath, args) + return ofile, output, err } func (gcToolchain) asm(b *builder, p *Package, obj, ofile, sfile string) error { sfile = mkAbs(p.Dir, sfile) - return b.run(p.Dir, p.ImportPath, tool(archChar+"a"), "-I", obj, "-o", ofile, "-DGOOS_"+goos, "-DGOARCH_"+goarch, sfile) + return b.run(p.Dir, p.ImportPath, tool(archChar+"a"), "-I", obj, "-o", ofile, "-D", "GOOS_"+goos, "-D", "GOARCH_"+goarch, sfile) } func (gcToolchain) pkgpath(basedir string, p *Package) string { @@ -1266,62 +1458,86 @@ func (gcToolchain) pack(b *builder, p *Package, objDir, afile string, ofiles []s for _, f := range ofiles { absOfiles = append(absOfiles, mkAbs(objDir, f)) } - return b.run(p.Dir, p.ImportPath, tool("pack"), "grc", mkAbs(objDir, afile), absOfiles) + return b.run(p.Dir, p.ImportPath, tool("pack"), "grcP", b.work, mkAbs(objDir, afile), absOfiles) } func (gcToolchain) ld(b *builder, p *Package, out string, allactions []*action, mainpkg string, ofiles []string) error { importArgs := b.includeArgs("-L", allactions) - return b.run(".", p.ImportPath, tool(archChar+"l"), "-o", out, importArgs, buildLdflags, mainpkg) + swigDirs := make(map[string]bool) + swigArg := []string{} + for _, a := range allactions { + if a.p != nil && a.p.usesSwig() { + sd := a.p.swigDir(&buildContext) + if len(swigArg) == 0 { + swigArg = []string{"-r", sd} + } else if !swigDirs[sd] { + swigArg[1] += ":" + swigArg[1] += sd + } + swigDirs[sd] = true + } + } + return b.run(".", p.ImportPath, tool(archChar+"l"), "-o", out, importArgs, swigArg, buildLdflags, mainpkg) } func (gcToolchain) cc(b *builder, p *Package, objdir, ofile, cfile string) error { inc := filepath.Join(goroot, "pkg", fmt.Sprintf("%s_%s", goos, goarch)) cfile = mkAbs(p.Dir, cfile) - return b.run(p.Dir, p.ImportPath, tool(archChar+"c"), "-FVw", - "-I", objdir, "-I", inc, "-o", ofile, - "-DGOOS_"+goos, "-DGOARCH_"+goarch, cfile) + args := stringList(tool(archChar+"c"), "-F", "-V", "-w", "-I", objdir, "-I", inc, "-o", ofile, buildCcflags, "-D", "GOOS_"+goos, "-D", "GOARCH_"+goarch, cfile) + return b.run(p.Dir, p.ImportPath, args) } // The Gccgo toolchain. -type gccgcToolchain struct{} +type gccgoToolchain struct{} var gccgoBin, _ = exec.LookPath("gccgo") -func (gccgcToolchain) compiler() string { +func (gccgoToolchain) compiler() string { return gccgoBin } -func (gccgcToolchain) linker() string { +func (gccgoToolchain) linker() string { return gccgoBin } -func (gccgcToolchain) gc(b *builder, p *Package, obj string, importArgs []string, gofiles []string) (ofile string, err error) { +func (gccgoToolchain) gc(b *builder, p *Package, obj string, importArgs []string, gofiles []string) (ofile string, output []byte, err error) { out := p.Name + ".o" ofile = obj + out gcargs := []string{"-g"} - if prefix := gccgoPrefix(p); prefix != "" { - gcargs = append(gcargs, "-fgo-prefix="+gccgoPrefix(p)) + gcargs = append(gcargs, b.gccArchArgs()...) + if pkgpath := gccgoPkgpath(p); pkgpath != "" { + gcargs = append(gcargs, "-fgo-pkgpath="+pkgpath) + } + if p.localPrefix != "" { + gcargs = append(gcargs, "-fgo-relative-import-path="+p.localPrefix) } args := stringList("gccgo", importArgs, "-c", gcargs, "-o", ofile, buildGccgoflags) for _, f := range gofiles { args = append(args, mkAbs(p.Dir, f)) } - return ofile, b.run(p.Dir, p.ImportPath, args) + + output, err = b.runOut(p.Dir, p.ImportPath, args) + return ofile, output, err } -func (gccgcToolchain) asm(b *builder, p *Package, obj, ofile, sfile string) error { +func (gccgoToolchain) asm(b *builder, p *Package, obj, ofile, sfile string) error { sfile = mkAbs(p.Dir, sfile) - return b.run(p.Dir, p.ImportPath, "gccgo", "-I", obj, "-o", ofile, "-DGOOS_"+goos, "-DGOARCH_"+goarch, sfile) + defs := []string{"-D", "GOOS_" + goos, "-D", "GOARCH_" + goarch} + if pkgpath := gccgoCleanPkgpath(p); pkgpath != "" { + defs = append(defs, `-D`, `GOPKGPATH="`+pkgpath+`"`) + } + defs = append(defs, b.gccArchArgs()...) + return b.run(p.Dir, p.ImportPath, "gccgo", "-I", obj, "-o", ofile, defs, sfile) } -func (gccgcToolchain) pkgpath(basedir string, p *Package) string { +func (gccgoToolchain) pkgpath(basedir string, p *Package) string { end := filepath.FromSlash(p.ImportPath + ".a") afile := filepath.Join(basedir, end) // add "lib" to the final element return filepath.Join(filepath.Dir(afile), "lib"+filepath.Base(afile)) } -func (gccgcToolchain) pack(b *builder, p *Package, objDir, afile string, ofiles []string) error { +func (gccgoToolchain) pack(b *builder, p *Package, objDir, afile string, ofiles []string) error { var absOfiles []string for _, f := range ofiles { absOfiles = append(absOfiles, mkAbs(objDir, f)) @@ -1329,12 +1545,14 @@ func (gccgcToolchain) pack(b *builder, p *Package, objDir, afile string, ofiles return b.run(p.Dir, p.ImportPath, "ar", "cru", mkAbs(objDir, afile), absOfiles) } -func (tools gccgcToolchain) ld(b *builder, p *Package, out string, allactions []*action, mainpkg string, ofiles []string) error { +func (tools gccgoToolchain) ld(b *builder, p *Package, out string, allactions []*action, mainpkg string, ofiles []string) error { // gccgo needs explicit linking with all package dependencies, // and all LDFLAGS from cgo dependencies. afiles := make(map[*Package]string) - ldflags := []string{} + sfiles := make(map[*Package][]string) + ldflags := b.gccArchArgs() cgoldflags := []string{} + usesCgo := false for _, a := range allactions { if a.p != nil { if !a.p.Standard { @@ -1343,31 +1561,99 @@ func (tools gccgcToolchain) ld(b *builder, p *Package, out string, allactions [] } } cgoldflags = append(cgoldflags, a.p.CgoLDFLAGS...) + if len(a.p.CgoFiles) > 0 { + usesCgo = true + } + if a.p.usesSwig() { + sd := a.p.swigDir(&buildContext) + for _, f := range stringList(a.p.SwigFiles, a.p.SwigCXXFiles) { + soname := a.p.swigSoname(f) + sfiles[a.p] = append(sfiles[a.p], filepath.Join(sd, soname)) + } + usesCgo = true + } } } for _, afile := range afiles { ldflags = append(ldflags, afile) } + for _, sfiles := range sfiles { + ldflags = append(ldflags, sfiles...) + } ldflags = append(ldflags, cgoldflags...) - return b.run(".", p.ImportPath, "gccgo", "-o", out, buildGccgoflags, ofiles, "-Wl,-(", ldflags, "-Wl,-)") + if usesCgo && goos == "linux" { + ldflags = append(ldflags, "-Wl,-E") + } + return b.run(".", p.ImportPath, "gccgo", "-o", out, ofiles, "-Wl,-(", ldflags, "-Wl,-)", buildGccgoflags) } -func (gccgcToolchain) cc(b *builder, p *Package, objdir, ofile, cfile string) error { +func (gccgoToolchain) cc(b *builder, p *Package, objdir, ofile, cfile string) error { inc := filepath.Join(goroot, "pkg", fmt.Sprintf("%s_%s", goos, goarch)) cfile = mkAbs(p.Dir, cfile) + defs := []string{"-D", "GOOS_" + goos, "-D", "GOARCH_" + goarch} + defs = append(defs, b.gccArchArgs()...) + if pkgpath := gccgoCleanPkgpath(p); pkgpath != "" { + defs = append(defs, `-D`, `GOPKGPATH="`+pkgpath+`"`) + } + // TODO: Support using clang here (during gccgo build)? return b.run(p.Dir, p.ImportPath, "gcc", "-Wall", "-g", - "-I", objdir, "-I", inc, "-o", ofile, - "-DGOOS_"+goos, "-DGOARCH_"+goarch, "-c", cfile) + "-I", objdir, "-I", inc, "-o", ofile, defs, "-c", cfile) } -func gccgoPrefix(p *Package) string { - switch { - case p.build.IsCommand() && !p.forceLibrary: +func gccgoPkgpath(p *Package) string { + if p.build.IsCommand() && !p.forceLibrary { return "" - case p.fake: - return "fake_" + p.ImportPath } - return "go_" + p.ImportPath + return p.ImportPath +} + +func gccgoCleanPkgpath(p *Package) string { + clean := func(r rune) rune { + switch { + case 'A' <= r && r <= 'Z', 'a' <= r && r <= 'z', + '0' <= r && r <= '9': + return r + } + return '_' + } + return strings.Map(clean, gccgoPkgpath(p)) +} + +// libgcc returns the filename for libgcc, as determined by invoking gcc with +// the -print-libgcc-file-name option. +func (b *builder) libgcc(p *Package) (string, error) { + var buf bytes.Buffer + + gccCmd := b.gccCmd(p.Dir) + + prev := b.print + if buildN { + // In -n mode we temporarily swap out the builder's + // print function to capture the command-line. This + // let's us assign it to $LIBGCC and produce a valid + // buildscript for cgo packages. + b.print = func(a ...interface{}) (n int, err error) { + return fmt.Fprint(&buf, a...) + } + } + f, err := b.runOut(p.Dir, p.ImportPath, gccCmd, "-print-libgcc-file-name") + if err != nil { + return "", fmt.Errorf("gcc -print-libgcc-file-name: %v (%s)", err, f) + } + if buildN { + s := fmt.Sprintf("LIBGCC=$(%s)\n", buf.Next(buf.Len()-1)) + b.print = prev + b.print(s) + return "$LIBGCC", nil + } + + // clang might not be able to find libgcc, and in that case, + // it will simply return "libgcc.a", which is of no use to us. + if strings.Contains(gccCmd[0], "clang") && !filepath.IsAbs(string(f)) { + return "", nil + } + + return strings.Trim(string(f), "\r\n"), nil } // gcc runs the gcc C compiler to create an object from a single C file. @@ -1386,20 +1672,19 @@ func (b *builder) gccCmd(objdir string) []string { // NOTE: env.go's mkEnv knows that the first three // strings returned are "gcc", "-I", objdir (and cuts them off). - // TODO: HOST_CC? - a := []string{"gcc", "-I", objdir, "-g", "-O2"} + gcc := strings.Fields(os.Getenv("CC")) + if len(gcc) == 0 { + gcc = append(gcc, "gcc") + } + a := []string{gcc[0], "-I", objdir, "-g", "-O2"} + a = append(a, gcc[1:]...) // Definitely want -fPIC but on Windows gcc complains // "-fPIC ignored for target (all code is position independent)" if goos != "windows" { a = append(a, "-fPIC") } - switch archChar { - case "8": - a = append(a, "-m32") - case "6": - a = append(a, "-m64") - } + a = append(a, b.gccArchArgs()...) // gcc-4.5 and beyond require explicit "-pthread" flag // for multithreading with pthread library. if buildContext.CgoEnabled { @@ -1411,6 +1696,11 @@ func (b *builder) gccCmd(objdir string) []string { } } + // clang is too smart about command-line arguments + if strings.Contains(a[0], "clang") { + a = append(a, "-Qunused-arguments") + } + // On OS X, some of the compilers behave as if -fno-common // is always set, and the Mach-O linker in 6l/8l assumes this. // See http://golang.org/issue/3253. @@ -1421,12 +1711,31 @@ func (b *builder) gccCmd(objdir string) []string { return a } +// gccArchArgs returns arguments to pass to gcc based on the architecture. +func (b *builder) gccArchArgs() []string { + switch archChar { + case "8": + return []string{"-m32"} + case "6": + return []string{"-m64"} + case "5": + return []string{"-marm"} // not thumb + } + return nil +} + func envList(key string) []string { return strings.Fields(os.Getenv(key)) } var cgoRe = regexp.MustCompile(`[/\\:]`) +var ( + cgoLibGccFile string + cgoLibGccErr error + cgoLibGccFileOnce sync.Once +) + func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo, outObj []string, err error) { if goos != toolGOOS { return nil, nil, errors.New("cannot use cgo when compiling for a different operating system") @@ -1478,10 +1787,14 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo, if p.Standard && p.ImportPath == "runtime/cgo" { cgoflags = append(cgoflags, "-import_runtime_cgo=false") } - if _, ok := buildToolchain.(gccgcToolchain); ok { + if p.Standard && (p.ImportPath == "runtime/race" || p.ImportPath == "runtime/cgo") { + cgoflags = append(cgoflags, "-import_syscall=false") + } + + if _, ok := buildToolchain.(gccgoToolchain); ok { cgoflags = append(cgoflags, "-gccgo") - if prefix := gccgoPrefix(p); prefix != "" { - cgoflags = append(cgoflags, "-gccgoprefix="+gccgoPrefix(p)) + if pkgpath := gccgoPkgpath(p); pkgpath != "" { + cgoflags = append(cgoflags, "-gccgopkgpath="+pkgpath) } objExt = "o" } @@ -1499,6 +1812,36 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo, // gcc var linkobj []string + + var bareLDFLAGS []string + // filter out -lsomelib, and -framework X if on Darwin + for i := 0; i < len(cgoLDFLAGS); i++ { + f := cgoLDFLAGS[i] + if !strings.HasPrefix(f, "-l") { + if goos == "darwin" && f == "-framework" { // skip the -framework X + i += 1 + continue + } + bareLDFLAGS = append(bareLDFLAGS, f) + } + } + + cgoLibGccFileOnce.Do(func() { + cgoLibGccFile, cgoLibGccErr = b.libgcc(p) + }) + if cgoLibGccFile == "" && cgoLibGccErr != nil { + return nil, nil, err + } + + var staticLibs []string + if goos == "windows" { + // libmingw32 and libmingwex might also use libgcc, so libgcc must come last + staticLibs = []string{"-lmingwex", "-lmingw32"} + } + if cgoLibGccFile != "" { + staticLibs = append(staticLibs, cgoLibGccFile) + } + for _, cfile := range cfiles { ofile := obj + cfile[:len(cfile)-1] + "o" if err := b.gcc(p, ofile, cgoCFLAGS, obj+cfile); err != nil { @@ -1517,19 +1860,30 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo, linkobj = append(linkobj, ofile) outObj = append(outObj, ofile) } + linkobj = append(linkobj, p.SysoFiles...) dynobj := obj + "_cgo_.o" + if goarch == "arm" && goos == "linux" { // we need to use -pie for Linux/ARM to get accurate imported sym + cgoLDFLAGS = append(cgoLDFLAGS, "-pie") + } if err := b.gccld(p, dynobj, cgoLDFLAGS, linkobj); err != nil { return nil, nil, err } + if goarch == "arm" && goos == "linux" { // but we don't need -pie for normal cgo programs + cgoLDFLAGS = cgoLDFLAGS[0 : len(cgoLDFLAGS)-1] + } - if _, ok := buildToolchain.(gccgcToolchain); ok { + if _, ok := buildToolchain.(gccgoToolchain); ok { // we don't use dynimport when using gccgo. return outGo, outObj, nil } // cgo -dynimport importC := obj + "_cgo_import.c" - if err := b.run(p.Dir, p.ImportPath, cgoExe, "-objdir", obj, "-dynimport", dynobj, "-dynout", importC); err != nil { + cgoflags = []string{} + if p.Standard && p.ImportPath == "runtime/cgo" { + cgoflags = append(cgoflags, "-dynlinker") // record path to dynamic linker + } + if err := b.run(p.Dir, p.ImportPath, cgoExe, "-objdir", obj, "-dynimport", dynobj, "-dynout", importC, cgoflags); err != nil { return nil, nil, err } @@ -1539,14 +1893,162 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo, return nil, nil, err } + ofile := obj + "_all.o" + var gccObjs, nonGccObjs []string + for _, f := range outObj { + if strings.HasSuffix(f, ".o") { + gccObjs = append(gccObjs, f) + } else { + nonGccObjs = append(nonGccObjs, f) + } + } + if err := b.gccld(p, ofile, stringList(bareLDFLAGS, "-Wl,-r", "-nostdlib", staticLibs), gccObjs); err != nil { + return nil, nil, err + } + // NOTE(rsc): The importObj is a 5c/6c/8c object and on Windows // must be processed before the gcc-generated objects. // Put it first. http://golang.org/issue/2601 - outObj = append([]string{importObj}, outObj...) + outObj = stringList(importObj, nonGccObjs, ofile) return outGo, outObj, nil } +// Run SWIG on all SWIG input files. +func (b *builder) swig(p *Package, obj string, gccfiles []string) (outGo, outObj []string, err error) { + + intgosize, err := b.swigIntSize(obj) + if err != nil { + return nil, nil, err + } + + for _, f := range p.SwigFiles { + goFile, objFile, err := b.swigOne(p, f, obj, false, intgosize) + if err != nil { + return nil, nil, err + } + if goFile != "" { + outGo = append(outGo, goFile) + } + if objFile != "" { + outObj = append(outObj, objFile) + } + } + for _, f := range p.SwigCXXFiles { + goFile, objFile, err := b.swigOne(p, f, obj, true, intgosize) + if err != nil { + return nil, nil, err + } + if goFile != "" { + outGo = append(outGo, goFile) + } + if objFile != "" { + outObj = append(outObj, objFile) + } + } + return outGo, outObj, nil +} + +// This code fails to build if sizeof(int) <= 32 +const swigIntSizeCode = ` +package main +const i int = 1 << 32 +` + +// Determine the size of int on the target system for the -intgosize option +// of swig >= 2.0.9 +func (b *builder) swigIntSize(obj string) (intsize string, err error) { + src := filepath.Join(b.work, "swig_intsize.go") + if err = ioutil.WriteFile(src, []byte(swigIntSizeCode), 0644); err != nil { + return + } + srcs := []string{src} + + p := goFilesPackage(srcs) + + if _, _, e := buildToolchain.gc(b, p, obj, nil, srcs); e != nil { + return "32", nil + } + return "64", nil +} + +// Run SWIG on one SWIG input file. +func (b *builder) swigOne(p *Package, file, obj string, cxx bool, intgosize string) (outGo, outObj string, err error) { + n := 5 // length of ".swig" + if cxx { + n = 8 // length of ".swigcxx" + } + base := file[:len(file)-n] + goFile := base + ".go" + cBase := base + "_gc." + gccBase := base + "_wrap." + gccExt := "c" + if cxx { + gccExt = "cxx" + } + soname := p.swigSoname(file) + + _, gccgo := buildToolchain.(gccgoToolchain) + + // swig + args := []string{ + "-go", + "-intgosize", intgosize, + "-module", base, + "-soname", soname, + "-o", obj + gccBase + gccExt, + "-outdir", obj, + } + if gccgo { + args = append(args, "-gccgo") + } + if cxx { + args = append(args, "-c++") + } + + if out, err := b.runOut(p.Dir, p.ImportPath, "swig", args, file); err != nil { + if len(out) > 0 { + if bytes.Contains(out, []byte("Unrecognized option -intgosize")) { + return "", "", errors.New("must have SWIG version >= 2.0.9\n") + } + b.showOutput(p.Dir, p.ImportPath, b.processOutput(out)) + return "", "", errPrintedOutput + } + return "", "", err + } + + var cObj string + if !gccgo { + // cc + cObj = obj + cBase + archChar + if err := buildToolchain.cc(b, p, obj, cObj, obj+cBase+"c"); err != nil { + return "", "", err + } + } + + // gcc + gccObj := obj + gccBase + "o" + if err := b.gcc(p, gccObj, []string{"-g", "-fPIC", "-O2"}, obj+gccBase+gccExt); err != nil { + return "", "", err + } + + // create shared library + osldflags := map[string][]string{ + "darwin": {"-dynamiclib", "-Wl,-undefined,dynamic_lookup"}, + "freebsd": {"-shared", "-lpthread", "-lm"}, + "linux": {"-shared", "-lpthread", "-lm"}, + "windows": {"-shared", "-lm", "-mthreads"}, + } + var cxxlib []string + if cxx { + cxxlib = []string{"-lstdc++"} + } + ldflags := stringList(osldflags[goos], cxxlib) + b.run(p.Dir, p.ImportPath, b.gccCmd(p.Dir), "-o", soname, gccObj, ldflags) + + return obj + goFile, cObj, nil +} + // An actionQueue is a priority queue of actions. type actionQueue []*action @@ -1569,3 +2071,18 @@ func (q *actionQueue) push(a *action) { func (q *actionQueue) pop() *action { return heap.Pop(q).(*action) } + +func raceInit() { + if !buildRace { + return + } + if goarch != "amd64" || goos != "linux" && goos != "darwin" && goos != "windows" { + fmt.Fprintf(os.Stderr, "go %s: -race is only supported on linux/amd64, darwin/amd64 and windows/amd64\n", flag.Args()[0]) + os.Exit(2) + } + buildGcflags = append(buildGcflags, "-race") + buildLdflags = append(buildLdflags, "-race") + buildCcflags = append(buildCcflags, "-D", "RACE") + buildContext.InstallTag = "race" + buildContext.BuildTags = append(buildContext.BuildTags, "race") +} diff --git a/src/cmd/go/clean.go b/src/cmd/go/clean.go index 773951826..ba600d3bb 100644 --- a/src/cmd/go/clean.go +++ b/src/cmd/go/clean.go @@ -34,6 +34,7 @@ source directories corresponding to the import paths: DIR(.exe) from go build DIR.test(.exe) from go test -c MAINFILE(.exe) from go build MAINFILE.go + *.so from SWIG In the list, DIR represents the final path element of the directory, and MAINFILE is the base name of any Go source @@ -93,11 +94,12 @@ var cleanFile = map[string]bool{ } var cleanExt = map[string]bool{ - ".5": true, - ".6": true, - ".8": true, - ".a": true, - ".o": true, + ".5": true, + ".6": true, + ".8": true, + ".a": true, + ".o": true, + ".so": true, } func clean(p *Package) { @@ -168,7 +170,9 @@ func clean(p *Package) { continue } } - os.RemoveAll(filepath.Join(p.Dir, name)) + if err := os.RemoveAll(filepath.Join(p.Dir, name)); err != nil { + errorf("go clean: %v", err) + } } continue } @@ -178,7 +182,7 @@ func clean(p *Package) { } if cleanFile[name] || cleanExt[filepath.Ext(name)] || toRemove[name] { - os.Remove(filepath.Join(p.Dir, name)) + removeFile(filepath.Join(p.Dir, name)) } } @@ -187,7 +191,21 @@ func clean(p *Package) { b.showcmd("", "rm -f %s", p.target) } if !cleanN { - os.Remove(p.target) + removeFile(p.target) + } + } + + if cleanI && p.usesSwig() { + for _, f := range stringList(p.SwigFiles, p.SwigCXXFiles) { + dir := p.swigDir(&buildContext) + soname := p.swigSoname(f) + target := filepath.Join(dir, soname) + if cleanN || cleanX { + b.showcmd("", "rm -f %s", target) + } + if !cleanN { + removeFile(target) + } } } @@ -197,3 +215,11 @@ func clean(p *Package) { } } } + +// removeFile tries to remove file f, if error other than file doesn't exist +// occurs, it will report the error. +func removeFile(f string) { + if err := os.Remove(f); err != nil && !os.IsNotExist(err) { + errorf("go clean: %v", err) + } +} diff --git a/src/cmd/go/doc.go b/src/cmd/go/doc.go index 5e7b10692..0297b7602 100644 --- a/src/cmd/go/doc.go +++ b/src/cmd/go/doc.go @@ -76,7 +76,12 @@ The build flags are shared by the build, install, run, and test commands: do not delete it when exiting. -x print the commands. + -race + enable data race detection. + Supported only on linux/amd64, darwin/amd64 and windows/amd64. + -ccflags 'arg list' + arguments to pass on each 5c, 6c, or 8c compiler invocation -compiler name name of compiler to use, as in runtime.Compiler (gccgo or gc) -gccgoflags 'arg list' @@ -90,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 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'. @@ -121,6 +129,7 @@ source directories corresponding to the import paths: DIR(.exe) from go build DIR.test(.exe) from go test -c MAINFILE(.exe) from go build MAINFILE.go + *.so from SWIG In the list, DIR represents the final path element of the directory, and MAINFILE is the base name of any Go source @@ -215,14 +224,11 @@ Download and install packages and dependencies Usage: - go get [-a] [-d] [-fix] [-n] [-p n] [-u] [-v] [-x] [packages] + go get [-d] [-fix] [-u] [build flags] [packages] Get downloads and installs the packages named by the import paths, along with their dependencies. -The -a, -n, -v, -x, and -p flags have the same meaning as in 'go build' -and 'go install'. See 'go help build'. - The -d flag instructs get to stop after downloading the packages; that is, it instructs get not to install the packages. @@ -233,6 +239,9 @@ The -u flag instructs get to use the network to update the named packages and their dependencies. By default, get uses the network to check out missing packages but does not use it to look for updates to existing packages. +Get also accepts all the flags in the 'go build' and 'go install' commands, +to control the installation. See 'go help build'. + When checking out or updating a package, get looks for a branch or tag that matches the locally installed version of Go. The most important rule is that if the local installation is running version "go1", get @@ -266,7 +275,7 @@ List packages 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. @@ -276,10 +285,10 @@ The default output shows the package import path: code.google.com/p/goauth2/oauth code.google.com/p/sqlite -The -f flag specifies an alternate format for the list, -using the syntax of package template. The default output -is equivalent to -f '{{.ImportPath}}'. The struct -being passed to the template is: +The -f flag specifies an alternate format for the list, using the +syntax of package template. The default output is equivalent to -f +'{{.ImportPath}}'. One extra template function is available, "join", +which calls strings.Join. The struct being passed to the template is: type Package struct { Dir string // directory containing package sources @@ -293,12 +302,15 @@ being passed to the template is: 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 + 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 @@ -333,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 (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'. @@ -368,6 +383,7 @@ followed by detailed output for each failed package. '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. @@ -445,7 +461,7 @@ On Unix, the value is a colon-separated string. On Windows, the value is a semicolon-separated string. On Plan 9, the value is a list. -GOPATH must be set to build and install packages outside the +GOPATH must be set to get, build and install packages outside the standard Go tree. Each directory listed in GOPATH must have a prescribed structure: @@ -491,7 +507,7 @@ Here's an example directory layout: bar.a (installed package object) Go searches each directory listed in GOPATH to find source code, -but new packages are always downloaded into the first directory +but new packages are always downloaded into the first directory in the list. @@ -509,13 +525,13 @@ denotes the package in that directory. Otherwise, the import path P denotes the package found in the directory DIR/src/P for some DIR listed in the GOPATH -environment variable (see 'go help gopath'). +environment variable (see 'go help gopath'). If no import paths are given, the action applies to the package in the current directory. The special import path "all" expands to all package directories -found in all the GOPATH trees. For example, 'go list all' +found in all the GOPATH trees. For example, 'go list all' lists all the packages on the local system. The special import path "std" is like all but expands to just the @@ -623,7 +639,7 @@ The meta tag has the form: <meta name="go-import" content="import-prefix vcs repo-root"> -The import-prefix is the import path correponding to the repository +The import-prefix is the import path corresponding to the repository root. It must be a prefix or an exact match of the package being fetched with "go get". If it's not an exact match, another http request is made at the prefix to verify the <meta> tags match. @@ -663,28 +679,47 @@ Description of testing flags The 'go test' command takes both flags that apply to 'go test' itself and flags that apply to the resulting test binary. -The test binary, called pkg.test, where pkg is the name of the -directory containing the package sources, has its own flags: - - -test.v - Verbose output: log all tests as they are run. +The following flags are recognized by the 'go test' command and +control the execution of any test: - -test.run pattern - Run only those tests and examples matching the regular - expression. - - -test.bench pattern + -bench regexp Run benchmarks matching the regular expression. - By default, no benchmarks run. + By default, no benchmarks run. To run all benchmarks, + use '-bench .' or '-bench=.'. + + -benchmem + Print memory allocation statistics for benchmarks. + + -benchtime t + Run enough iterations of each benchmark to take t, specified + as a time.Duration (for example, -benchtime 1h30s). + The default is 1 second (1s). + + -blockprofile block.out + Write a goroutine blocking profile to the specified file + when all tests are complete. + + -blockprofilerate n + Control the detail provided in goroutine blocking profiles by setting + runtime.BlockProfileRate to n. See 'godoc runtime BlockProfileRate'. + The profiler aims to sample, on average, one blocking event every + n nanoseconds the program spends blocked. By default, + if -test.blockprofile is set without this flag, all blocking events + are recorded, equivalent to -test.blockprofilerate=1. + + -cpu 1,2,4 + Specify a list of GOMAXPROCS values for which the tests or + benchmarks should be executed. The default is the current value + of GOMAXPROCS. - -test.cpuprofile cpu.out + -cpuprofile cpu.out Write a CPU profile to the specified file before exiting. - -test.memprofile mem.out + -memprofile mem.out Write a memory profile to the specified file when all tests are complete. - -test.memprofilerate n + -memprofilerate n Enable more precise (and expensive) memory profiles by setting runtime.MemProfileRate. See 'godoc runtime MemProfileRate'. To profile all memory allocations, use -test.memprofilerate=1 @@ -692,32 +727,35 @@ directory containing the package sources, has its own flags: garbage collector, provided the test can run in the available memory without garbage collection. - -test.parallel n + -parallel n Allow parallel execution of test functions that call t.Parallel. The value of this flag is the maximum number of tests to run simultaneously; by default, it is set to the value of GOMAXPROCS. - -test.short + -run regexp + Run only those tests and examples matching the regular + expression. + + -short Tell long-running tests to shorten their run time. It is off by default but set during all.bash so that installing the Go tree can run a sanity check but not spend time running exhaustive tests. - -test.timeout t + -timeout t If a test runs longer than t, panic. - -test.benchtime n - Run enough iterations of each benchmark to take n seconds. - The default is 1 second. + -v + Verbose output: log all tests as they are run. - -test.cpu 1,2,4 - Specify a list of GOMAXPROCS values for which the tests or - benchmarks should be executed. The default is the current value - of GOMAXPROCS. +The test binary, called pkg.test where pkg is the name of the +directory containing the package sources, can be invoked directly +after building it with 'go test -c'. When invoking the test binary +directly, each of the standard flag names must be prefixed with 'test.', +as in -test.run=TestMyFunc or -test.v. -For convenience, each of these -test.X flags of the test binary is -also available as the flag -X in 'go test' itself. Flags not listed -here are passed through unaltered. For instance, the command +When running 'go test', flags not listed above are passed through +unaltered. For instance, the command go test -x -v -cpuprofile=prof.out -dir=testdata -update @@ -725,6 +763,11 @@ 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. + +Flags not recognized by 'go test' must be placed after any specified packages. + Description of testing functions @@ -740,8 +783,8 @@ A benchmark function is one named BenchmarkXXX and should have the signature, 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 @@ -769,6 +812,6 @@ See the documentation of the testing package for more information. */ -package documentation +package main // NOTE: cmdDoc is in fmt.go. diff --git a/src/cmd/go/env.go b/src/cmd/go/env.go index d5b034809..6cab37b48 100644 --- a/src/cmd/go/env.go +++ b/src/cmd/go/env.go @@ -6,6 +6,7 @@ package main import ( "fmt" + "os" "runtime" "strings" ) @@ -33,16 +34,22 @@ func mkEnv() []envVar { b.init() env := []envVar{ - {"GOROOT", goroot}, - {"GOBIN", gobin}, {"GOARCH", goarch}, + {"GOBIN", gobin}, {"GOCHAR", archChar}, - {"GOOS", goos}, {"GOEXE", exeSuffix}, {"GOHOSTARCH", runtime.GOARCH}, {"GOHOSTOS", runtime.GOOS}, + {"GOOS", goos}, + {"GOPATH", os.Getenv("GOPATH")}, + {"GOROOT", goroot}, {"GOTOOLDIR", toolDir}, - {"GOGCCFLAGS", strings.Join(b.gccCmd(".")[3:], " ")}, + } + + if goos != "plan9" { + cmd := b.gccCmd(".") + env = append(env, envVar{"CC", cmd[0]}) + env = append(env, envVar{"GOGCCFLAGS", strings.Join(cmd[3:], " ")}) } if buildContext.CgoEnabled { diff --git a/src/cmd/go/fix.go b/src/cmd/go/fix.go index ef02b5739..8736cce3e 100644 --- a/src/cmd/go/fix.go +++ b/src/cmd/go/fix.go @@ -25,6 +25,6 @@ func runFix(cmd *Command, args []string) { // 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 b1aba32f3..9d3c911dd 100644 --- a/src/cmd/go/fmt.go +++ b/src/cmd/go/fmt.go @@ -34,7 +34,7 @@ func runFmt(cmd *Command, args []string) { // 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 fe45697e2..8c08ab261 100644 --- a/src/cmd/go/get.go +++ b/src/cmd/go/get.go @@ -18,15 +18,12 @@ import ( ) var cmdGet = &Command{ - UsageLine: "get [-a] [-d] [-fix] [-n] [-p n] [-u] [-v] [-x] [packages]", + UsageLine: "get [-d] [-fix] [-u] [build flags] [packages]", Short: "download and install packages and dependencies", Long: ` Get downloads and installs the packages named by the import paths, along with their dependencies. -The -a, -n, -v, -x, and -p flags have the same meaning as in 'go build' -and 'go install'. See 'go help build'. - The -d flag instructs get to stop after downloading the packages; that is, it instructs get not to install the packages. @@ -37,6 +34,9 @@ The -u flag instructs get to use the network to update the named packages and their dependencies. By default, get uses the network to check out missing packages but does not use it to look for updates to existing packages. +Get also accepts all the flags in the 'go build' and 'go install' commands, +to control the installation. See 'go help build'. + When checking out or updating a package, get looks for a branch or tag that matches the locally installed version of Go. The most important rule is that if the local installation is running version "go1", get @@ -93,7 +93,7 @@ func runGet(cmd *Command, args []string) { runInstall(cmd, args) } -// downloadPath prepares the list of paths to pass to download. +// downloadPaths prepares the list of paths to pass to download. // It expands ... patterns that can be expanded. If there is no match // for a particular pattern, downloadPaths leaves it in the result list, // in the hope that we can figure out the repository from the @@ -105,7 +105,7 @@ func downloadPaths(args []string) []string { if strings.Contains(a, "...") { var expand []string // Use matchPackagesInFS to avoid printing - // warnings. They will be printed by the + // warnings. They will be printed by the // eventual call to importPaths instead. if build.IsLocalImport(a) { expand = matchPackagesInFS(a) @@ -204,7 +204,7 @@ func download(arg string, stk *importStack) { // 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) @@ -247,16 +247,17 @@ func downloadPackage(p *Package) error { } if p.build.SrcRoot == "" { - // Package not found. Put in first directory of $GOPATH or else $GOROOT. - // Guard against people setting GOPATH=$GOROOT. We have to use - // $GOROOT's directory hierarchy (src/pkg, not just src) in that case. - if list := filepath.SplitList(buildContext.GOPATH); len(list) > 0 && list[0] != goroot { - p.build.SrcRoot = filepath.Join(list[0], "src") - p.build.PkgRoot = filepath.Join(list[0], "pkg") - } else { - p.build.SrcRoot = filepath.Join(goroot, "src", "pkg") - p.build.PkgRoot = filepath.Join(goroot, "pkg") + // Package not found. Put in first directory of $GOPATH. + list := filepath.SplitList(buildContext.GOPATH) + if len(list) == 0 { + return fmt.Errorf("cannot download, $GOPATH not set. For more details see: go help gopath") + } + // Guard against people setting GOPATH=$GOROOT. + if list[0] == goroot { + return fmt.Errorf("cannot download, $GOPATH must not be set to $GOROOT. For more details see: go help gopath") } + p.build.SrcRoot = filepath.Join(list[0], "src") + p.build.PkgRoot = filepath.Join(list[0], "pkg") } root := filepath.Join(p.build.SrcRoot, rootPath) // If we've considered this repository already, don't do it again. diff --git a/src/cmd/go/help.go b/src/cmd/go/help.go index 7539753af..6d2bd7dbb 100644 --- a/src/cmd/go/help.go +++ b/src/cmd/go/help.go @@ -186,7 +186,7 @@ On Unix, the value is a colon-separated string. On Windows, the value is a semicolon-separated string. On Plan 9, the value is a list. -GOPATH must be set to build and install packages outside the +GOPATH must be set to get, build and install packages outside the standard Go tree. Each directory listed in GOPATH must have a prescribed structure: diff --git a/src/cmd/go/http.go b/src/cmd/go/http.go index 6de9a3e1e..107b820f2 100644 --- a/src/cmd/go/http.go +++ b/src/cmd/go/http.go @@ -20,9 +20,13 @@ import ( "net/url" ) +// httpClient is the default HTTP client, but a variable so it can be +// changed by tests, without modifying http.DefaultClient. +var httpClient = http.DefaultClient + // httpGET returns the data from an HTTP GET request for the given URL. func httpGET(url string) ([]byte, error) { - resp, err := http.Get(url) + resp, err := httpClient.Get(url) if err != nil { return nil, err } @@ -37,10 +41,6 @@ func httpGET(url string) ([]byte, error) { return b, nil } -// httpClient is the default HTTP client, but a variable so it can be -// changed by tests, without modifying http.DefaultClient. -var httpClient = http.DefaultClient - // httpsOrHTTP returns the body of either the importPath's // https resource or, if unavailable, the http resource. func httpsOrHTTP(importPath string) (urlStr string, body io.ReadCloser, err error) { diff --git a/src/cmd/go/list.go b/src/cmd/go/list.go index edb59aa79..2d23d077e 100644 --- a/src/cmd/go/list.go +++ b/src/cmd/go/list.go @@ -9,11 +9,12 @@ import ( "encoding/json" "io" "os" + "strings" "text/template" ) 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. @@ -24,10 +25,10 @@ The default output shows the package import path: code.google.com/p/goauth2/oauth code.google.com/p/sqlite -The -f flag specifies an alternate format for the list, -using the syntax of package template. The default output -is equivalent to -f '{{.ImportPath}}'. The struct -being passed to the template is: +The -f flag specifies an alternate format for the list, using the +syntax of package template. The default output is equivalent to -f +'{{.ImportPath}}'. One extra template function is available, "join", +which calls strings.Join. The struct being passed to the template is: type Package struct { Dir string // directory containing package sources @@ -41,12 +42,15 @@ being passed to the template is: 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 + 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 @@ -81,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 (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'. `, } @@ -88,6 +95,7 @@ For more about specifying packages, see 'go help packages'. 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, "") @@ -96,7 +104,7 @@ var listJson = cmdList.Flag.Bool("json", false, "") var nl = []byte{'\n'} func runList(cmd *Command, args []string) { - out := newCountingWriter(os.Stdout) + out := newTrackingWriter(os.Stdout) defer out.w.Flush() var do func(*Package) @@ -111,18 +119,17 @@ func runList(cmd *Command, args []string) { out.Write(nl) } } else { - tmpl, err := template.New("main").Parse(*listFmt) + tmpl, err := template.New("main").Funcs(template.FuncMap{"join": strings.Join}).Parse(*listFmt) if err != nil { fatalf("%s", err) } do = func(p *Package) { - out.Reset() if err := tmpl.Execute(out, p); err != nil { out.Flush() fatalf("%s", err) } - if out.Count() > 0 { - out.w.WriteRune('\n') + if out.NeedNL() { + out.Write([]byte{'\n'}) } } } @@ -137,32 +144,33 @@ func runList(cmd *Command, args []string) { } } -// CountingWriter counts its data, so we can avoid appending a newline -// if there was no actual output. -type CountingWriter struct { - w *bufio.Writer - count int64 +// TrackingWriter tracks the last byte written on every write so +// we can avoid printing a newline if one was already written or +// if there is no output at all. +type TrackingWriter struct { + w *bufio.Writer + last byte } -func newCountingWriter(w io.Writer) *CountingWriter { - return &CountingWriter{ - w: bufio.NewWriter(w), +func newTrackingWriter(w io.Writer) *TrackingWriter { + return &TrackingWriter{ + w: bufio.NewWriter(w), + last: '\n', } } -func (cw *CountingWriter) Write(p []byte) (n int, err error) { - cw.count += int64(len(p)) - return cw.w.Write(p) -} - -func (cw *CountingWriter) Flush() { - cw.w.Flush() +func (t *TrackingWriter) Write(p []byte) (n int, err error) { + n, err = t.w.Write(p) + if n > 0 { + t.last = p[n-1] + } + return } -func (cw *CountingWriter) Reset() { - cw.count = 0 +func (t *TrackingWriter) Flush() { + t.w.Flush() } -func (cw *CountingWriter) Count() int64 { - return cw.count +func (t *TrackingWriter) NeedNL() bool { + return t.last != '\n' } diff --git a/src/cmd/go/main.go b/src/cmd/go/main.go index 20585d1be..bf1dad40f 100644 --- a/src/cmd/go/main.go +++ b/src/cmd/go/main.go @@ -127,6 +127,17 @@ func main() { // which is not what most people want when they do it. if gopath := os.Getenv("GOPATH"); gopath == runtime.GOROOT() { 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) + } + } } for _, cmd := range commands { @@ -189,7 +200,7 @@ var documentationTemplate = `// Copyright 2011 The Go Authors. All rights reser {{end}}*/ -package documentation +package main // NOTE: cmdDoc is in fmt.go. ` @@ -368,6 +379,25 @@ func runOut(dir string, cmdargs ...interface{}) []byte { return out } +// envForDir returns a copy of the environment +// suitable for running in the given directory. +// The environment is the current process's environment +// but with an updated $PWD, so that an os.Getwd in the +// child will be faster. +func envForDir(dir string) []string { + env := os.Environ() + for i, kv := range env { + if strings.HasPrefix(kv, "PWD=") { + env[i] = "PWD=" + dir + return env + } + } + // Internally we only use rooted paths, so dir is rooted. + // Even if dir is not rooted, no harm done. + env = append(env, "PWD="+dir) + return env +} + // matchPattern(pattern)(name) reports whether // name matches pattern. Pattern is a limited glob // pattern in which '...' means 'any string' and there @@ -423,19 +453,20 @@ func matchPackages(pattern string) []string { return filepath.SkipDir } - _, err = build.ImportDir(path, 0) - if err != nil { - return nil - } - // We use, e.g., cmd/gofmt as the pseudo import path for gofmt. name = "cmd/" + name - if !have[name] { - have[name] = true - if match(name) { - pkgs = append(pkgs, name) - } + if have[name] { + return nil } + have[name] = true + if !match(name) { + return nil + } + _, err = buildContext.ImportDir(path, 0) + if err != nil { + return nil + } + pkgs = append(pkgs, name) return nil }) @@ -463,14 +494,14 @@ func matchPackages(pattern string) []string { return nil } have[name] = true - - _, err = build.ImportDir(path, 0) - if err != nil && strings.Contains(err.Error(), "no Go source files") { + if !match(name) { return nil } - if match(name) { - pkgs = append(pkgs, name) + _, err = buildContext.ImportDir(path, 0) + if err != nil && strings.Contains(err.Error(), "no Go source files") { + return nil } + pkgs = append(pkgs, name) return nil }) } @@ -559,3 +590,60 @@ func stringList(args ...interface{}) []string { } return x } + +// toFold returns a string with the property that +// strings.EqualFold(s, t) iff toFold(s) == toFold(t) +// This lets us test a large set of strings for fold-equivalent +// duplicates without making a quadratic number of calls +// to EqualFold. Note that strings.ToUpper and strings.ToLower +// have the desired property in some corner cases. +func toFold(s string) string { + // Fast path: all ASCII, no upper case. + // Most paths look like this already. + for i := 0; i < len(s); i++ { + c := s[i] + if c >= utf8.RuneSelf || 'A' <= c && c <= 'Z' { + goto Slow + } + } + return s + +Slow: + var buf bytes.Buffer + for _, r := range s { + // SimpleFold(x) cycles to the next equivalent rune > x + // or wraps around to smaller values. Iterate until it wraps, + // and we've found the minimum value. + for { + r0 := r + r = unicode.SimpleFold(r0) + if r <= r0 { + break + } + } + // Exception to allow fast path above: A-Z => a-z + if 'A' <= r && r <= 'Z' { + r += 'a' - 'A' + } + buf.WriteRune(r) + } + return buf.String() +} + +// foldDup reports a pair of strings from the list that are +// equal according to strings.EqualFold. +// It returns "", "" if there are no such strings. +func foldDup(list []string) (string, string) { + clash := map[string]string{} + for _, s := range list { + fold := toFold(s) + if t := clash[fold]; t != "" { + if s > t { + s, t = t, s + } + return s, t + } + clash[fold] = s + } + return "", "" +} diff --git a/src/cmd/go/pkg.go b/src/cmd/go/pkg.go index 30bbfad55..7fc61fd86 100644 --- a/src/cmd/go/pkg.go +++ b/src/cmd/go/pkg.go @@ -36,12 +36,15 @@ type Package struct { 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 + 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 @@ -69,12 +72,14 @@ type Package struct { 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) { @@ -90,10 +95,13 @@ func (p *Package) copyBuild(pp *build.Package) { 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 p.SysoFiles = pp.SysoFiles + p.SwigFiles = pp.SwigFiles + p.SwigCXXFiles = pp.SwigCXXFiles p.CgoCFLAGS = pp.CgoCFLAGS p.CgoLDFLAGS = pp.CgoLDFLAGS p.CgoPkgConfig = pp.CgoPkgConfig @@ -117,6 +125,9 @@ func (p *PackageError) Error() string { // is the most important thing. return p.Pos + ": " + p.Err } + if len(p.ImportStack) == 0 { + return p.Err + } return "package " + strings.Join(p.ImportStack, "\n\timports ") + ": " + p.Err } @@ -246,7 +257,7 @@ func reusePackage(p *Package, stk *importStack) *Package { if p.Error == nil { p.Error = &PackageError{ ImportStack: stk.copy(), - Err: "import loop", + Err: "import cycle not allowed", } } p.Incomplete = true @@ -260,13 +271,11 @@ func reusePackage(p *Package, stk *importStack) *Package { // isGoTool is the list of directories for Go programs that are installed in // $GOROOT/pkg/tool. var isGoTool = map[string]bool{ - "cmd/api": true, - "cmd/cgo": true, - "cmd/fix": true, - "cmd/vet": true, - "cmd/yacc": true, - "exp/gotype": true, - "exp/ebnflint": true, + "cmd/api": true, + "cmd/cgo": true, + "cmd/fix": true, + "cmd/vet": true, + "cmd/yacc": true, } // expandScanner expands a scanner.List error into all the errors in the list. @@ -316,11 +325,13 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package // Install cross-compiled binaries to subdirectories of bin. elem = full } - p.target = filepath.Join(p.build.BinDir, elem) - if p.Goroot && isGoTool[p.ImportPath] { + 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 { @@ -340,6 +351,11 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package // Everything depends on runtime, except runtime and unsafe. if !p.Standard || (p.ImportPath != "runtime" && p.ImportPath != "unsafe") { importPaths = append(importPaths, "runtime") + // When race detection enabled everything depends on runtime/race. + // Exclude runtime/cgo and cmd/cgo to avoid circular dependencies. + if buildRace && (!p.Standard || (p.ImportPath != "runtime/race" && p.ImportPath != "runtime/cgo" && p.ImportPath != "cmd/cgo")) { + importPaths = append(importPaths, "runtime/race") + } } // Build list of full paths to all Go files in the package, @@ -350,6 +366,38 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package } 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) + + // Check for case-insensitive collision of input files. + // To avoid problems on case-insensitive files, we reject any package + // where two different input files have equal names under a case-insensitive + // comparison. + f1, f2 := foldDup(stringList( + p.GoFiles, + p.CgoFiles, + p.IgnoredGoFiles, + p.CFiles, + p.HFiles, + p.SFiles, + p.SysoFiles, + p.SwigFiles, + p.SwigCXXFiles, + p.TestGoFiles, + p.XTestGoFiles, + )) + if f1 != "" { + p.Error = &PackageError{ + ImportStack: stk.copy(), + Err: fmt.Sprintf("case-insensitive file name collision: %q and %q", f1, f2), + } + return p + } + // Build list of imported packages and full dependency list. imports := make([]*Package, 0, len(p.Imports)) deps := make(map[string]bool) @@ -403,11 +451,47 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package if p.Standard && (p.ImportPath == "unsafe" || buildContext.Compiler == "gccgo") { p.target = "" } - p.Target = p.target + + // In the absence of errors lower in the dependency tree, + // check for case-insensitive collisions of import paths. + if len(p.DepsErrors) == 0 { + dep1, dep2 := foldDup(p.Deps) + if dep1 != "" { + p.Error = &PackageError{ + ImportStack: stk.copy(), + Err: fmt.Sprintf("case-insensitive import collision: %q and %q", dep1, dep2), + } + return p + } + } + return p } +// usesSwig returns whether the package needs to run SWIG. +func (p *Package) usesSwig() bool { + return len(p.SwigFiles) > 0 || len(p.SwigCXXFiles) > 0 +} + +// swigSoname returns the name of the shared library we create for a +// SWIG input file. +func (p *Package) swigSoname(file string) string { + return strings.Replace(p.ImportPath, "/", "-", -1) + "-" + strings.Replace(file, ".", "-", -1) + ".so" +} + +// swigDir returns the name of the shared SWIG directory for a +// package. +func (p *Package) swigDir(ctxt *build.Context) string { + dir := p.build.PkgRoot + if ctxt.Compiler == "gccgo" { + dir = filepath.Join(dir, "gccgo_"+ctxt.GOOS+"_"+ctxt.GOARCH) + } else { + dir = filepath.Join(dir, ctxt.GOOS+"_"+ctxt.GOARCH) + } + return filepath.Join(dir, "swig") +} + // packageList returns the list of packages in the dag rooted at roots // as visited in a depth-first post-order traversal. func packageList(roots []*Package) []*Package { @@ -459,7 +543,7 @@ func isStale(p *Package, topRoot map[string]bool) bool { // distributions of Go packages, although such binaries are // only useful with the specific version of the toolchain that // created them. - if len(p.gofiles) == 0 { + if len(p.gofiles) == 0 && !p.usesSwig() { return false } @@ -491,14 +575,19 @@ func isStale(p *Package, topRoot map[string]bool) bool { // As a courtesy to developers installing new versions of the compiler // frequently, define that packages are stale if they are // older than the compiler, and commands if they are older than - // the linker. This heuristic will not work if the binaries are back-dated, - // as some binary distributions may do, but it does handle a very - // common case. See issue 3036. - if olderThan(buildToolchain.compiler()) { - return true - } - if p.build.IsCommand() && olderThan(buildToolchain.linker()) { - return true + // the linker. This heuristic will not work if the binaries are + // back-dated, as some binary distributions may do, but it does handle + // a very common case. + // See issue 3036. + // Assume code in $GOROOT is up to date, since it may not be writeable. + // See issue 4106. + if p.Root != goroot { + if olderThan(buildToolchain.compiler()) { + return true + } + if p.build.IsCommand() && olderThan(buildToolchain.linker()) { + return true + } } // Have installed copy, probably built using current compilers, @@ -522,6 +611,21 @@ func isStale(p *Package, topRoot map[string]bool) bool { } } + for _, src := range stringList(p.SwigFiles, p.SwigCXXFiles) { + if olderThan(filepath.Join(p.Dir, src)) { + return true + } + soname := p.swigSoname(src) + fi, err := os.Stat(soname) + if err != nil { + return true + } + fiSrc, err := os.Stat(src) + if err != nil || fiSrc.ModTime().After(fi.ModTime()) { + return true + } + } + return false } @@ -546,13 +650,24 @@ func loadPackage(arg string, stk *importStack) *Package { 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() - bp, err := build.ImportDir(filepath.Join(gorootSrc, arg), 0) + + 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 bp.BinDir = gorootBin @@ -580,7 +695,7 @@ func loadPackage(arg string, stk *importStack) *Package { // referring to io/ioutil rather than a hypothetical import of // "./ioutil". if build.IsLocalImport(arg) { - bp, _ := build.ImportDir(filepath.Join(cwd, arg), build.FindOnly) + bp, _ := buildContext.ImportDir(filepath.Join(cwd, arg), build.FindOnly) if bp.ImportPath != "" && bp.ImportPath != "." { arg = bp.ImportPath } @@ -621,10 +736,14 @@ func packagesAndErrors(args []string) []*Package { args = importPaths(args) var pkgs []*Package var stk importStack + var set = make(map[string]bool) + for _, arg := range args { - pkgs = append(pkgs, loadPackage(arg, &stk)) + if !set[arg] { + pkgs = append(pkgs, loadPackage(arg, &stk)) + set[arg] = true + } } - computeStale(pkgs...) return pkgs diff --git a/src/cmd/go/run.go b/src/cmd/go/run.go index 6043b7e20..b50569363 100644 --- a/src/cmd/go/run.go +++ b/src/cmd/go/run.go @@ -34,6 +34,7 @@ func printStderr(args ...interface{}) (int, error) { } func runRun(cmd *Command, args []string) { + raceInit() var b builder b.init() b.print = printStderr @@ -45,6 +46,13 @@ func runRun(cmd *Command, args []string) { 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) @@ -57,6 +65,21 @@ func runRun(cmd *Command, args []string) { 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 if len(p.CgoFiles) > 0 { + src = p.CgoFiles[0] + } else { + // this case could only happen if the provided source uses cgo + // while cgo is disabled. + hint := "" + if !buildContext.CgoEnabled { + hint = " (cgo is disabled)" + } + fatalf("go run: no suitable source files%s", hint) + } + 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) @@ -83,6 +106,7 @@ func runStdin(cmdargs ...interface{}) { cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr + startSigHandlers() if err := cmd.Run(); err != nil { errorf("%v", err) } diff --git a/src/cmd/go/signal.go b/src/cmd/go/signal.go new file mode 100644 index 000000000..e8ba0d365 --- /dev/null +++ b/src/cmd/go/signal.go @@ -0,0 +1,31 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "os" + "os/signal" + "sync" +) + +// interrupted is closed, if go process is interrupted. +var interrupted = make(chan struct{}) + +// processSignals setups signal handler. +func processSignals() { + sig := make(chan os.Signal) + signal.Notify(sig, signalsToIgnore...) + go func() { + <-sig + close(interrupted) + }() +} + +var onceProcessSignals sync.Once + +// startSigHandlers start signal handlers. +func startSigHandlers() { + onceProcessSignals.Do(processSignals) +} diff --git a/src/cmd/go/signal_notunix.go b/src/cmd/go/signal_notunix.go new file mode 100644 index 000000000..ef13c1919 --- /dev/null +++ b/src/cmd/go/signal_notunix.go @@ -0,0 +1,13 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build plan9 windows + +package main + +import ( + "os" +) + +var signalsToIgnore = []os.Signal{os.Interrupt} diff --git a/src/cmd/go/signal_unix.go b/src/cmd/go/signal_unix.go new file mode 100644 index 000000000..489a73b83 --- /dev/null +++ b/src/cmd/go/signal_unix.go @@ -0,0 +1,14 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin freebsd linux netbsd openbsd + +package main + +import ( + "os" + "syscall" +) + +var signalsToIgnore = []os.Signal{os.Interrupt, syscall.SIGQUIT} diff --git a/src/cmd/go/test.bash b/src/cmd/go/test.bash index fe186d4bb..460061a11 100755 --- a/src/cmd/go/test.bash +++ b/src/cmd/go/test.bash @@ -76,6 +76,12 @@ if ! ./testgo test ./testdata/testimport; then ok=false fi +# Test installation with relative imports. +if ! ./testgo test -i ./testdata/testimport; then + echo "go test -i ./testdata/testimport failed" + ok=false +fi + # Test tests with relative imports in packages synthesized # from Go files named on the command line. if ! ./testgo test ./testdata/testimport/*.go; then @@ -119,6 +125,208 @@ elif ! test -x testdata/bin1/helloworld; then ok=false fi +# Reject relative paths in GOPATH. +if GOPATH=. ./testgo build testdata/src/go-cmd-test/helloworld.go; then + echo 'GOPATH="." go build should have failed, did not' + ok=false +fi + +if GOPATH=:$(pwd)/testdata:. ./testgo build go-cmd-test; then + echo 'GOPATH=":$(pwd)/testdata:." go build should have failed, did not' + ok=false +fi + +# issue 4104 +if [ $(./testgo test fmt fmt fmt fmt fmt | wc -l) -ne 1 ] ; then + echo 'go test fmt fmt fmt fmt fmt tested the same package multiple times' + ok=false +fi + +# ensure that output of 'go list' is consistent between runs +./testgo list std > test_std.list +if ! ./testgo list std | cmp -s test_std.list - ; then + echo "go list std ordering is inconsistent" + ok=false +fi +rm -f test_std.list + +# issue 4096. Validate the output of unsucessful go install foo/quxx +if [ $(./testgo install 'foo/quxx' 2>&1 | grep -c 'cannot find package "foo/quxx" in any of') -ne 1 ] ; then + echo 'go install foo/quxx expected error: .*cannot find package "foo/quxx" in any of' + ok=false +fi +# test GOROOT search failure is reported +if [ $(./testgo install 'foo/quxx' 2>&1 | egrep -c 'foo/quxx \(from \$GOROOT\)$') -ne 1 ] ; then + echo 'go install foo/quxx expected error: .*foo/quxx (from $GOROOT)' + ok=false +fi +# test multiple GOPATH entries are reported separately +if [ $(GOPATH=$(pwd)/testdata/a:$(pwd)/testdata/b ./testgo install 'foo/quxx' 2>&1 | egrep -c 'testdata/./src/foo/quxx') -ne 2 ] ; then + echo 'go install foo/quxx expected error: .*testdata/a/src/foo/quxx (from $GOPATH)\n.*testdata/b/src/foo/quxx' + ok=false +fi +# test (from $GOPATH) annotation is reported for the first GOPATH entry +if [ $(GOPATH=$(pwd)/testdata/a:$(pwd)/testdata/b ./testgo install 'foo/quxx' 2>&1 | egrep -c 'testdata/a/src/foo/quxx \(from \$GOPATH\)$') -ne 1 ] ; then + echo 'go install foo/quxx expected error: .*testdata/a/src/foo/quxx (from $GOPATH)' + ok=false +fi +# but not on the second +if [ $(GOPATH=$(pwd)/testdata/a:$(pwd)/testdata/b ./testgo install 'foo/quxx' 2>&1 | egrep -c 'testdata/b/src/foo/quxx$') -ne 1 ] ; then + echo 'go install foo/quxx expected error: .*testdata/b/src/foo/quxx' + ok=false +fi +# test missing GOPATH is reported +if [ $(GOPATH= ./testgo install 'foo/quxx' 2>&1 | egrep -c '\(\$GOPATH not set\)$') -ne 1 ] ; then + echo 'go install foo/quxx expected error: ($GOPATH not set)' + ok=false +fi + +# issue 4186. go get cannot be used to download packages to $GOROOT +# Test that without GOPATH set, go get should fail +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' + ok=false +fi +rm -rf $d +# Test that with GOPATH=$GOROOT, go get should fail +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' + ok=false +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 + +# issue 4773. case-insensitive collisions +d=$(mktemp -d -t testgo) +export GOPATH=$d +mkdir -p $d/src/example/a $d/src/example/b +cat >$d/src/example/a/a.go <<EOF +package p +import ( + _ "math/rand" + _ "math/Rand" +) +EOF +if ./testgo list example/a 2>$d/out; then + echo go list example/a should have failed, did not. + ok=false +elif ! grep "case-insensitive import collision" $d/out >/dev/null; then + echo go list example/a did not report import collision. + ok=false +fi +cat >$d/src/example/b/file.go <<EOF +package b +EOF +cat >$d/src/example/b/FILE.go <<EOF +package b +EOF +if [ $(ls $d/src/example/b | wc -l) = 2 ]; then + # case-sensitive file system, let directory read find both files + args="example/b" +else + # case-insensitive file system, list files explicitly on command line. + args="$d/src/example/b/file.go $d/src/example/b/FILE.go" +fi +if ./testgo list $args 2>$d/out; then + echo go list example/b should have failed, did not. + ok=false +elif ! grep "case-insensitive file name collision" $d/out >/dev/null; then + echo go list example/b did not report file name collision. + ok=false +fi +unset GOPATH +rm -rf $d + +# Only succeeds if source order is preserved. +./testgo test testdata/example[12]_test.go + +# clean up +rm -rf testdata/bin testdata/bin1 +rm -f testgo + if $ok; then echo PASS else diff --git a/src/cmd/go/test.go b/src/cmd/go/test.go index 870ab190f..3132ab210 100644 --- a/src/cmd/go/test.go +++ b/src/cmd/go/test.go @@ -48,6 +48,7 @@ followed by detailed output for each failed package. '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. @@ -80,28 +81,47 @@ var helpTestflag = &Command{ The 'go test' command takes both flags that apply to 'go test' itself and flags that apply to the resulting test binary. -The test binary, called pkg.test, where pkg is the name of the -directory containing the package sources, has its own flags: +The following flags are recognized by the 'go test' command and +control the execution of any test: - -test.v - Verbose output: log all tests as they are run. - - -test.run pattern - Run only those tests and examples matching the regular - expression. - - -test.bench pattern + -bench regexp Run benchmarks matching the regular expression. - By default, no benchmarks run. + By default, no benchmarks run. To run all benchmarks, + use '-bench .' or '-bench=.'. + + -benchmem + Print memory allocation statistics for benchmarks. + + -benchtime t + Run enough iterations of each benchmark to take t, specified + as a time.Duration (for example, -benchtime 1h30s). + The default is 1 second (1s). + + -blockprofile block.out + Write a goroutine blocking profile to the specified file + when all tests are complete. + + -blockprofilerate n + Control the detail provided in goroutine blocking profiles by setting + runtime.BlockProfileRate to n. See 'godoc runtime BlockProfileRate'. + The profiler aims to sample, on average, one blocking event every + n nanoseconds the program spends blocked. By default, + if -test.blockprofile is set without this flag, all blocking events + are recorded, equivalent to -test.blockprofilerate=1. + + -cpu 1,2,4 + Specify a list of GOMAXPROCS values for which the tests or + benchmarks should be executed. The default is the current value + of GOMAXPROCS. - -test.cpuprofile cpu.out + -cpuprofile cpu.out Write a CPU profile to the specified file before exiting. - -test.memprofile mem.out + -memprofile mem.out Write a memory profile to the specified file when all tests are complete. - -test.memprofilerate n + -memprofilerate n Enable more precise (and expensive) memory profiles by setting runtime.MemProfileRate. See 'godoc runtime MemProfileRate'. To profile all memory allocations, use -test.memprofilerate=1 @@ -109,38 +129,46 @@ directory containing the package sources, has its own flags: garbage collector, provided the test can run in the available memory without garbage collection. - -test.parallel n + -parallel n Allow parallel execution of test functions that call t.Parallel. The value of this flag is the maximum number of tests to run simultaneously; by default, it is set to the value of GOMAXPROCS. - -test.short + -run regexp + Run only those tests and examples matching the regular + expression. + + -short Tell long-running tests to shorten their run time. It is off by default but set during all.bash so that installing the Go tree can run a sanity check but not spend time running exhaustive tests. - -test.timeout t + -timeout t If a test runs longer than t, panic. - -test.benchtime n - Run enough iterations of each benchmark to take n seconds. - The default is 1 second. + -v + Verbose output: log all tests as they are run. - -test.cpu 1,2,4 - Specify a list of GOMAXPROCS values for which the tests or - benchmarks should be executed. The default is the current value - of GOMAXPROCS. +The test binary, called pkg.test where pkg is the name of the +directory containing the package sources, can be invoked directly +after building it with 'go test -c'. When invoking the test binary +directly, each of the standard flag names must be prefixed with 'test.', +as in -test.run=TestMyFunc or -test.v. -For convenience, each of these -test.X flags of the test binary is -also available as the flag -X in 'go test' itself. Flags not listed -here are passed through unaltered. For instance, the command +When running 'go test', flags not listed above are passed through +unaltered. For instance, the command go test -x -v -cpuprofile=prof.out -dir=testdata -update 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. + +Flags not recognized by 'go test' must be placed after any specified packages. `, } @@ -160,8 +188,8 @@ A benchmark function is one named BenchmarkXXX and should have the signature, 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 @@ -191,6 +219,7 @@ See the documentation of the testing package for more information. 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 @@ -207,6 +236,7 @@ func runTest(cmd *Command, args []string) { var pkgArgs []string pkgArgs, testArgs = testFlags(args) + raceInit() pkgs := packagesForBuild(pkgArgs) if len(pkgs) == 0 { fatalf("no packages to test") @@ -215,12 +245,15 @@ func runTest(cmd *Command, args []string) { 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 } @@ -276,7 +309,9 @@ func runTest(cmd *Command, args []string) { all := []string{} for path := range deps { - all = append(all, path) + if !build.IsLocalImport(path) { + all = append(all, path) + } } sort.Strings(all) @@ -361,7 +396,11 @@ func runTest(cmd *Command, args []string) { if args != "" { args = " " + args } - fmt.Fprintf(os.Stderr, "installing these packages with 'go test -i%s' will speed future tests.\n\n", args) + extraOpts := "" + if buildRace { + extraOpts = "-race " + } + fmt.Fprintf(os.Stderr, "installing these packages with 'go test %s-i%s' will speed future tests.\n\n", extraOpts, args) } b.do(root) @@ -405,7 +444,14 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action, // 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 @@ -532,14 +578,17 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action, 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 @@ -586,6 +635,7 @@ func (b *builder) runTest(a *action) error { cmd := exec.Command(args[0], args[1:]...) cmd.Dir = a.p.Dir + cmd.Env = envForDir(cmd.Dir) var buf bytes.Buffer if testStreamOutput { cmd.Stdout = os.Stdout @@ -595,14 +645,34 @@ func (b *builder) runTest(a *action) error { cmd.Stderr = &buf } + // If there are any local SWIG dependencies, we want to load + // the shared library from the build directory. + if a.p.usesSwig() { + env := cmd.Env + found := false + prefix := "LD_LIBRARY_PATH=" + for i, v := range env { + if strings.HasPrefix(v, prefix) { + env[i] = v + ":." + found = true + break + } + } + if !found { + env = append(env, "LD_LIBRARY_PATH=.") + } + cmd.Env = env + } + t0 := time.Now() err := cmd.Start() // This is a last-ditch deadline to detect and // stop wedged test binaries, to keep the builders // running. - tick := time.NewTimer(testKillTimeout) if err == nil { + tick := time.NewTimer(testKillTimeout) + startSigHandlers() done := make(chan error) go func() { done <- cmd.Wait() @@ -613,13 +683,12 @@ func (b *builder) runTest(a *action) error { 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() } out := buf.Bytes() - t1 := time.Now() - t := fmt.Sprintf("%.3fs", t1.Sub(t0).Seconds()) + t := fmt.Sprintf("%.3fs", time.Since(t0).Seconds()) if err == nil { if testShowPass { a.testOutput.Write(out) @@ -750,8 +819,10 @@ func (t *testFuncs) load(filename, pkg string, seen *bool) error { *seen = true } } - for _, e := range doc.Examples(f) { - if e.Output == "" { + ex := doc.Examples(f) + sort.Sort(byOrder(ex)) + for _, e := range ex { + if e.Output == "" && !e.EmptyOutput { // Don't run examples with no output. continue } @@ -761,6 +832,12 @@ func (t *testFuncs) load(filename, pkg string, seen *bool) error { return nil } +type byOrder []*doc.Example + +func (x byOrder) Len() int { return len(x) } +func (x byOrder) Swap(i, j int) { x[i], x[j] = x[j], x[i] } +func (x byOrder) Less(i, j int) bool { return x[i].Order < x[j].Order } + var testmainTmpl = template.Must(template.New("main").Parse(` package main diff --git a/src/cmd/go/testdata/example1_test.go b/src/cmd/go/testdata/example1_test.go new file mode 100644 index 000000000..ec7092e97 --- /dev/null +++ b/src/cmd/go/testdata/example1_test.go @@ -0,0 +1,23 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Make sure that go test runs Example_Z before Example_A, preserving source order. + +package p + +import "fmt" + +var n int + +func Example_Z() { + n++ + fmt.Println(n) + // Output: 1 +} + +func Example_A() { + n++ + fmt.Println(n) + // Output: 2 +} diff --git a/src/cmd/go/testdata/example2_test.go b/src/cmd/go/testdata/example2_test.go new file mode 100644 index 000000000..1e0e80b80 --- /dev/null +++ b/src/cmd/go/testdata/example2_test.go @@ -0,0 +1,21 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Make sure that go test runs Example_Y before Example_B, preserving source order. + +package p + +import "fmt" + +func Example_Y() { + n++ + fmt.Println(n) + // Output: 3 +} + +func Example_B() { + n++ + fmt.Println(n) + // Output: 4 +} diff --git a/src/cmd/go/testflag.go b/src/cmd/go/testflag.go index ecf5bf456..b2ca66b09 100644 --- a/src/cmd/go/testflag.go +++ b/src/cmd/go/testflag.go @@ -25,11 +25,14 @@ var usageMessage = `Usage of go test: // These flags can be passed with or without a "test." prefix: -v or -test.v. -bench="": passes -test.bench to test - -benchtime=1: passes -test.benchtime to test + -benchmem=false: print memory allocation statistics for benchmarks + -benchtime=1s: passes -test.benchtime to test -cpu="": passes -test.cpu to test -cpuprofile="": passes -test.cpuprofile to test -memprofile="": passes -test.memprofile to test -memprofilerate=0: passes -test.memprofilerate to test + -blockprofile="": pases -test.blockprofile to test + -blockprofilerate=0: passes -test.blockprofilerate to test -parallel=0: passes -test.parallel to test -run="": passes -test.run to test -short=false: passes -test.short to test @@ -71,14 +74,18 @@ var testFlagDefn = []*testFlagSpec{ {name: "gccgoflags"}, {name: "tags"}, {name: "compiler"}, + {name: "race", boolVar: &buildRace}, // passed to 6.out, adding a "test." prefix to the name if necessary: -v becomes -test.v. {name: "bench", passToTest: true}, + {name: "benchmem", boolVar: new(bool), passToTest: true}, {name: "benchtime", passToTest: true}, {name: "cpu", passToTest: true}, {name: "cpuprofile", passToTest: true}, {name: "memprofile", passToTest: true}, {name: "memprofilerate", passToTest: true}, + {name: "blockprofile", passToTest: true}, + {name: "blockprofilerate", passToTest: true}, {name: "parallel", passToTest: true}, {name: "run", passToTest: true}, {name: "short", boolVar: new(bool), passToTest: true}, @@ -117,7 +124,7 @@ func testFlags(args []string) (packageNames, passToTest []string) { f, value, extraWord := testFlag(args, i) if f == nil { // This is a flag we do not know; we must assume - // that any args we see after this might be flag + // that any args we see after this might be flag // arguments, not package names. inPkg = false if packageNames == nil { @@ -127,18 +134,28 @@ func testFlags(args []string) (packageNames, passToTest []string) { passToTest = append(passToTest, args[i]) continue } + var err error switch f.name { // bool flags. - case "a", "c", "i", "n", "x", "v", "work": + case "a", "c", "i", "n", "x", "v", "work", "race": setBoolFlag(f.boolVar, value) 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": @@ -150,6 +167,8 @@ func testFlags(args []string) (packageNames, passToTest []string) { testBench = true case "timeout": testTimeout = value + case "blockprofile", "cpuprofile", "memprofile": + testProfile = true } if extraWord { i++ @@ -176,9 +195,7 @@ func testFlag(args []string, i int) (f *testFlagSpec, value string, extra bool) } name := arg[1:] // If there's already "test.", drop it for now. - if strings.HasPrefix(name, "test.") { - name = name[5:] - } + name = strings.TrimPrefix(name, "test.") equals := strings.Index(name, "=") if equals >= 0 { value = name[equals+1:] diff --git a/src/cmd/go/tool.go b/src/cmd/go/tool.go index cb463a2e7..299b94cb3 100644 --- a/src/cmd/go/tool.go +++ b/src/cmd/go/tool.go @@ -47,7 +47,7 @@ const toolWindowsExtension = ".exe" func tool(name string) string { p := filepath.Join(toolDir, name) - if toolIsWindows { + if toolIsWindows && name != "pprof" { p += toolWindowsExtension } return p @@ -76,6 +76,16 @@ func runTool(cmd *Command, args []string) { setExitStatus(3) return } + if toolIsWindows && toolName == "pprof" { + args = append([]string{"perl", toolPath}, args[1:]...) + var err error + toolPath, err = exec.LookPath("perl") + if err != nil { + fmt.Fprintf(os.Stderr, "go tool: perl not found\n") + setExitStatus(3) + return + } + } if toolN { fmt.Printf("%s %s\n", toolPath, strings.Join(args[1:], " ")) @@ -90,7 +100,14 @@ func runTool(cmd *Command, args []string) { } 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/vcs.go b/src/cmd/go/vcs.go index 1c121672f..b99579441 100644 --- a/src/cmd/go/vcs.go +++ b/src/cmd/go/vcs.go @@ -81,7 +81,7 @@ var vcsHg = &vcsCmd{ tagSyncCmd: "update -r {tag}", tagSyncDefault: "update default", - scheme: []string{"https", "http"}, + scheme: []string{"https", "http", "ssh"}, pingCmd: "identify {scheme}://{repo}", } @@ -180,8 +180,17 @@ func (v *vcsCmd) run1(dir string, cmdline string, keyval []string, verbose bool) args[i] = expand(m, arg) } + _, err := exec.LookPath(v.cmd) + if err != nil { + fmt.Fprintf(os.Stderr, + "go: missing %s command. See http://golang.org/s/gogetcmd\n", + v.name) + return nil, err + } + cmd := exec.Command(v.cmd, args...) cmd.Dir = dir + cmd.Env = envForDir(cmd.Dir) if buildX { fmt.Printf("cd %s\n", dir) fmt.Printf("%s %s\n", v.cmd, strings.Join(args, " ")) @@ -189,7 +198,7 @@ func (v *vcsCmd) run1(dir string, cmdline string, keyval []string, verbose bool) var buf bytes.Buffer cmd.Stdout = &buf cmd.Stderr = &buf - err := cmd.Run() + err = cmd.Run() out := buf.Bytes() if err != nil { if verbose || buildV { diff --git a/src/cmd/go/version.go b/src/cmd/go/version.go index 09e2f1633..a41f4a736 100644 --- a/src/cmd/go/version.go +++ b/src/cmd/go/version.go @@ -21,5 +21,5 @@ func runVersion(cmd *Command, args []string) { cmd.Usage() } - fmt.Printf("go version %s\n", runtime.Version()) + fmt.Printf("go version %s %s/%s\n", runtime.Version(), runtime.GOOS, runtime.GOARCH) } |