summaryrefslogtreecommitdiff
path: root/src/cmd/go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/go')
-rw-r--r--src/cmd/go/Makefile25
-rw-r--r--src/cmd/go/build.go425
-rw-r--r--src/cmd/go/clean.go197
-rw-r--r--src/cmd/go/doc.go90
-rw-r--r--src/cmd/go/fix.go10
-rw-r--r--src/cmd/go/get.go12
-rw-r--r--src/cmd/go/list.go12
-rw-r--r--src/cmd/go/main.go36
-rw-r--r--src/cmd/go/pkg.go113
-rw-r--r--src/cmd/go/run.go27
-rw-r--r--src/cmd/go/test.go231
-rw-r--r--src/cmd/go/testflag.go9
-rw-r--r--src/cmd/go/tool.go125
-rw-r--r--src/cmd/go/vcs.go103
-rw-r--r--src/cmd/go/vet.go10
15 files changed, 1116 insertions, 309 deletions
diff --git a/src/cmd/go/Makefile b/src/cmd/go/Makefile
deleted file mode 100644
index 295a14498..000000000
--- a/src/cmd/go/Makefile
+++ /dev/null
@@ -1,25 +0,0 @@
-# Copyright 2009 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.
-
-include ../../Make.inc
-
-TARG=go
-GOFILES=\
- build.go\
- fix.go\
- get.go\
- fmt.go\
- help.go\
- http.go\
- list.go\
- main.go\
- pkg.go\
- run.go\
- test.go\
- testflag.go\
- version.go\
- vet.go\
- vcs.go\
-
-include ../../Make.cmd
diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go
index cbe36f52e..caffa1f05 100644
--- a/src/cmd/go/build.go
+++ b/src/cmd/go/build.go
@@ -14,6 +14,7 @@ import (
"io/ioutil"
"os"
"os/exec"
+ "path"
"path/filepath"
"regexp"
"runtime"
@@ -22,7 +23,7 @@ import (
)
var cmdBuild = &Command{
- UsageLine: "build [-a] [-n] [-o output] [-p n] [-v] [-x] [importpath... | gofiles...]",
+ UsageLine: "build [-a] [-n] [-o output] [-p n] [-v] [-x] [-work] [importpath... | gofiles...]",
Short: "compile packages and dependencies",
Long: `
Build compiles the packages named by the import paths,
@@ -47,6 +48,9 @@ It is an error to use -o when the command line specifies multiple packages.
The -p flag specifies the number of builds that can be run in parallel.
The default is the number of CPUs available.
+The -work flag causes build to print the name of the temporary work
+directory and not delete it when exiting.
+
For more about import paths, see 'go help importpath'.
See also: go install, go get, go clean.
@@ -69,6 +73,7 @@ var buildP = runtime.NumCPU() // -p flag
var buildV bool // -v flag
var buildX bool // -x flag
var buildO = cmdBuild.Flag.String("o", "", "output file")
+var buildWork bool // -work flag
var buildContext = build.DefaultContext
@@ -79,6 +84,7 @@ func addBuildFlags(cmd *Command) {
cmd.Flag.IntVar(&buildP, "p", buildP, "")
cmd.Flag.BoolVar(&buildV, "v", false, "")
cmd.Flag.BoolVar(&buildX, "x", false, "")
+ cmd.Flag.BoolVar(&buildWork, "work", false, "")
// TODO(rsc): This -t flag is used by buildscript.sh but
// not documented. Should be documented but the
@@ -113,7 +119,10 @@ func runBuild(cmd *Command, args []string) {
}
if len(pkgs) == 1 && pkgs[0].Name == "main" && *buildO == "" {
- *buildO = "a.out"
+ _, *buildO = path.Split(pkgs[0].ImportPath)
+ if b.goos == "windows" {
+ *buildO += ".exe"
+ }
}
if *buildO != "" {
@@ -136,7 +145,7 @@ func runBuild(cmd *Command, args []string) {
}
var cmdInstall = &Command{
- UsageLine: "install [-a] [-n] [-p n] [-v] [-x] [importpath...]",
+ UsageLine: "install [-a] [-n] [-p n] [-v] [-x] [-work] [importpath...]",
Short: "compile and install packages and dependencies",
Long: `
Install compiles and installs the packages named by the import paths,
@@ -150,6 +159,9 @@ The -x flag prints the commands.
The -p flag specifies the number of builds that can be run in parallel.
The default is the number of CPUs available.
+The -work flag causes build to print the name of the temporary work
+directory and not delete it when exiting.
+
For more about import paths, see 'go help importpath'.
See also: go build, go get, go clean.
@@ -174,14 +186,13 @@ func runInstall(cmd *Command, args []string) {
type builder struct {
work string // the temporary work directory (ends in filepath.Separator)
arch string // e.g., "6"
- goroot string // the $GOROOT
goarch string // the $GOARCH
goos string // the $GOOS
- gobin string // the $GOBIN
exe string // the executable suffix - "" or ".exe"
gcflags []string // additional flags for Go compiler
actionCache map[cacheKey]*action // a cache of already-constructed actions
mkdirCache map[string]bool // a cache of created directories
+ print func(args ...interface{}) (int, error)
output sync.Mutex
scriptDir string // current directory in printed script
@@ -231,14 +242,18 @@ const (
modeInstall
)
+var (
+ gobin = build.Path[0].BinDir()
+ goroot = build.Path[0].Path
+)
+
func (b *builder) init() {
var err error
+ b.print = fmt.Print
b.actionCache = make(map[cacheKey]*action)
b.mkdirCache = make(map[string]bool)
b.goarch = buildContext.GOARCH
b.goos = buildContext.GOOS
- b.goroot = build.Path[0].Path
- b.gobin = build.Path[0].BinDir()
if b.goos == "windows" {
b.exe = ".exe"
}
@@ -256,10 +271,12 @@ func (b *builder) init() {
if err != nil {
fatalf("%s", err)
}
- if buildX {
+ if buildX || buildWork {
fmt.Printf("WORK=%s\n", b.work)
}
- atexit(func() { os.RemoveAll(b.work) })
+ if !buildWork {
+ atexit(func() { os.RemoveAll(b.work) })
+ }
}
}
@@ -293,10 +310,21 @@ func goFilesPackage(gofiles []string, target string) *Package {
ctxt.ReadDir = func(string) ([]os.FileInfo, error) { return dir, nil }
pwd, _ := os.Getwd()
var stk importStack
- pkg := scanPackage(&ctxt, &build.Tree{Path: "."}, "<command line>", "<command line>", pwd+"/.", &stk)
+ pkg := scanPackage(&ctxt, &build.Tree{Path: "."}, "<command line>", "<command line>", pwd+"/.", &stk, true)
if pkg.Error != nil {
fatalf("%s", pkg.Error)
}
+ printed := map[error]bool{}
+ for _, err := range pkg.DepsErrors {
+ // Since these are errors in dependencies,
+ // the same error might show up multiple times,
+ // once in each package that depends on it.
+ // Only print each once.
+ if !printed[err] {
+ printed[err] = true
+ errorf("%s", err)
+ }
+ }
if target != "" {
pkg.target = target
} else if pkg.Name == "main" {
@@ -305,6 +333,7 @@ func goFilesPackage(gofiles []string, target string) *Package {
pkg.target = pkg.Name + ".a"
}
pkg.ImportPath = "_/" + pkg.target
+ exitIfErrors()
return pkg
}
@@ -328,14 +357,21 @@ func (b *builder) action(mode buildMode, depMode buildMode, p *Package) *action
a.deps = append(a.deps, b.action(depMode, depMode, p1))
}
- if len(p.CgoFiles) > 0 {
- var stk importStack
- p1 := loadPackage("cmd/cgo", &stk)
- if p1.Error != nil {
- fatalf("load cmd/cgo: %v", p1.Error)
+ // If we are not doing a cross-build, then record the binary we'll
+ // generate for cgo as a dependency of the build of any package
+ // 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 b.goos == runtime.GOOS && b.goarch == runtime.GOARCH {
+ if len(p.CgoFiles) > 0 || p.Standard && p.ImportPath == "runtime/cgo" {
+ var stk importStack
+ p1 := loadPackage("cmd/cgo", &stk)
+ if p1.Error != nil {
+ fatalf("load cmd/cgo: %v", p1.Error)
+ }
+ a.cgo = b.action(depMode, depMode, p1)
+ a.deps = append(a.deps, a.cgo)
}
- a.cgo = b.action(depMode, depMode, p1)
- a.deps = append(a.deps, a.cgo)
}
if p.Standard {
@@ -344,6 +380,12 @@ func (b *builder) action(mode buildMode, depMode buildMode, p *Package) *action
// Fake packages - nothing to build.
return a
}
+ // gccgo standard library is "fake" too.
+ if _, ok := buildToolchain.(gccgoToolchain); ok {
+ // the target name is needed for cgo.
+ a.target = p.target
+ return a
+ }
}
if !p.Stale && !buildA && p.target != "" {
@@ -354,7 +396,7 @@ func (b *builder) action(mode buildMode, depMode buildMode, p *Package) *action
}
a.objdir = filepath.Join(b.work, filepath.FromSlash(a.p.ImportPath+"/_obj")) + string(filepath.Separator)
- a.objpkg = filepath.Join(b.work, filepath.FromSlash(a.p.ImportPath+".a"))
+ a.objpkg = buildToolchain.pkgpath(b.work, a.p)
a.link = p.Name == "main"
switch mode {
@@ -367,8 +409,6 @@ func (b *builder) action(mode buildMode, depMode buildMode, p *Package) *action
a.target = a.objpkg
if a.link {
// An executable file.
- // Have to use something other than .a for the suffix.
- // It is easier on Windows if we use .exe, so use .exe everywhere.
// (This is the name of a temporary file.)
a.target = a.objdir + "a.out" + b.exe
}
@@ -445,7 +485,7 @@ func (b *builder) do(root *action) {
if err != nil {
if err == errPrintedOutput {
- exitStatus = 2
+ setExitStatus(2)
} else {
errorf("%s", err)
}
@@ -544,7 +584,11 @@ func (b *builder) build(a *action) error {
sfiles = nil
}
- outGo, outObj, err := b.cgo(a.p, a.cgo.target, obj, gccfiles)
+ cgoExe := tool("cgo")
+ if a.cgo != nil {
+ cgoExe = a.cgo.target
+ }
+ outGo, outObj, err := b.cgo(a.p, cgoExe, obj, gccfiles)
if err != nil {
return err
}
@@ -557,17 +601,11 @@ func (b *builder) build(a *action) error {
// Compile Go.
if len(gofiles) > 0 {
- out := "_go_." + b.arch
- gcargs := []string{"-p", a.p.ImportPath}
- if a.p.Standard && a.p.ImportPath == "runtime" {
- // runtime compiles with a special 6g flag to emit
- // additional reflect type data.
- gcargs = append(gcargs, "-+")
- }
- if err := b.gc(a.p, obj+out, gcargs, inc, gofiles); err != nil {
+ if out, err := buildToolchain.gc(b, a.p, obj, inc, gofiles); err != nil {
return err
+ } else {
+ objects = append(objects, out)
}
- objects = append(objects, out)
}
// Copy .h files named for goos or goarch or goos_goarch
@@ -598,7 +636,7 @@ func (b *builder) build(a *action) error {
for _, file := range cfiles {
out := file[:len(file)-len(".c")] + "." + b.arch
- if err := b.cc(a.p, obj, obj+out, file); err != nil {
+ if err := buildToolchain.cc(b, a.p, obj, obj+out, file); err != nil {
return err
}
objects = append(objects, out)
@@ -607,7 +645,7 @@ func (b *builder) build(a *action) error {
// Assemble .s files.
for _, file := range sfiles {
out := file[:len(file)-len(".s")] + "." + b.arch
- if err := b.asm(a.p, obj, obj+out, file); err != nil {
+ if err := buildToolchain.asm(b, a.p, obj, obj+out, file); err != nil {
return err
}
objects = append(objects, out)
@@ -620,7 +658,7 @@ func (b *builder) build(a *action) error {
objects = append(objects, cgoObjects...)
// Pack into archive in obj directory
- if err := b.gopack(a.p, obj, a.objpkg, objects); err != nil {
+ if err := buildToolchain.pack(b, a.p, obj, a.objpkg, objects); err != nil {
return err
}
@@ -630,8 +668,7 @@ func (b *builder) build(a *action) error {
// linker needs the whole dependency tree.
all := actionList(a)
all = all[:len(all)-1] // drop a
- inc := b.includeArgs("-L", all)
- if err := b.ld(a.p, a.target, inc, a.objpkg); err != nil {
+ if err := buildToolchain.ld(b, a.p, a.target, all, a.objpkg, objects); err != nil {
return err
}
}
@@ -655,6 +692,15 @@ func (b *builder) install(a *action) error {
}
}
+ // remove object dir to keep the amount of
+ // garbage down in a large build. On an operating system
+ // with aggressive buffering, cleaning incrementally like
+ // this keeps the intermediate objects from hitting the disk.
+ if !buildWork {
+ defer os.RemoveAll(a1.objdir)
+ defer os.Remove(a1.target)
+ }
+
return b.copyFile(a.target, a1.target, perm)
}
@@ -685,6 +731,9 @@ func (b *builder) includeArgs(flag string, all []*action) []string {
// Finally, look in the installed package directories for each action.
for _, a1 := range all {
if dir := a1.pkgdir; dir == a1.p.t.PkgDir() && !incMap[dir] {
+ if _, ok := buildToolchain.(gccgoToolchain); ok {
+ dir = filepath.Join(filepath.Dir(dir), "gccgo", filepath.Base(dir))
+ }
incMap[dir] = true
inc = append(inc, flag, dir)
}
@@ -693,33 +742,6 @@ func (b *builder) includeArgs(flag string, all []*action) []string {
return inc
}
-// removeByRenaming removes file name by moving it to a tmp
-// directory and deleting the target if possible.
-func removeByRenaming(name string) error {
- f, err := ioutil.TempFile("", "")
- if err != nil {
- return err
- }
- tmpname := f.Name()
- f.Close()
- err = os.Remove(tmpname)
- if err != nil {
- return err
- }
- err = os.Rename(name, tmpname)
- if err != nil {
- // assume name file does not exists,
- // otherwise later code will fail.
- return nil
- }
- err = os.Remove(tmpname)
- if err != nil {
- // TODO(brainman): file is locked and can't be deleted.
- // We need to come up with a better way of doing it.
- }
- return nil
-}
-
// copyFile is like 'cp src dst'.
func (b *builder) copyFile(dst, src string, perm os.FileMode) error {
if buildN || buildX {
@@ -734,23 +756,42 @@ func (b *builder) copyFile(dst, src string, perm os.FileMode) error {
return err
}
defer sf.Close()
+
+ // Be careful about removing/overwriting dst.
+ // Do not remove/overwrite if dst exists and is a directory
+ // or a non-object file.
+ if fi, err := os.Stat(dst); err == nil {
+ if fi.IsDir() {
+ return fmt.Errorf("build output %q already exists and is a directory", dst)
+ }
+ if !isObject(dst) {
+ return fmt.Errorf("build output %q already exists and is not an object file", dst)
+ }
+ }
+
+ // On Windows, remove lingering ~ file from last attempt.
+ if toolIsWindows {
+ if _, err := os.Stat(dst + "~"); err == nil {
+ os.Remove(dst + "~")
+ }
+ }
+
os.Remove(dst)
df, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm)
- if err != nil {
- if runtime.GOOS != "windows" {
- return err
- }
- // Windows does not allow to replace binary file
- // while it is executing. We will cheat.
- err = removeByRenaming(dst)
- if err != nil {
- return err
+ if err != nil && toolIsWindows {
+ // Windows does not allow deletion of a binary file
+ // while it is executing. Try to move it out of the way.
+ // If the remove fails, which is likely, we'll try again the
+ // next time we do an install of this binary.
+ if err := os.Rename(dst, dst+"~"); err == nil {
+ os.Remove(dst + "~")
}
df, err = os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm)
- if err != nil {
- return err
- }
}
+ if err != nil {
+ return err
+ }
+
_, err = io.Copy(df, sf)
df.Close()
if err != nil {
@@ -760,13 +801,39 @@ func (b *builder) copyFile(dst, src string, perm os.FileMode) error {
return nil
}
+var objectMagic = [][]byte{
+ {'!', '<', 'a', 'r', 'c', 'h', '>', '\n'}, // Package archive
+ {'\x7F', 'E', 'L', 'F'}, // ELF
+ {0xFE, 0xED, 0xFA, 0xCE}, // Mach-O big-endian 32-bit
+ {0xFE, 0xED, 0xFA, 0xCF}, // Mach-O big-endian 64-bit
+ {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
+}
+
+func isObject(s string) bool {
+ f, err := os.Open(s)
+ if err != nil {
+ return false
+ }
+ defer f.Close()
+ buf := make([]byte, 64)
+ io.ReadFull(f, buf)
+ for _, magic := range objectMagic {
+ if bytes.HasPrefix(buf, magic) {
+ return true
+ }
+ }
+ return false
+}
+
// fmtcmd formats a command in the manner of fmt.Sprintf but also:
//
// If dir is non-empty and the script is not in dir right now,
// fmtcmd inserts "cd dir\n" before the command.
//
// fmtcmd replaces the value of b.work with $WORK.
-// fmtcmd replaces the value of b.goroot with $GOROOT.
+// fmtcmd replaces the value of goroot with $GOROOT.
// fmtcmd replaces the value of b.gobin with $GOBIN.
//
// fmtcmd replaces the name of the current directory with dot (.)
@@ -781,9 +848,11 @@ func (b *builder) fmtcmd(dir string, format string, args ...interface{}) string
cmd = "cd " + dir + "\n" + cmd
}
}
- cmd = strings.Replace(cmd, b.work, "$WORK", -1)
- cmd = strings.Replace(cmd, b.gobin, "$GOBIN", -1)
- cmd = strings.Replace(cmd, b.goroot, "$GOROOT", -1)
+ if b.work != "" {
+ cmd = strings.Replace(cmd, b.work, "$WORK", -1)
+ }
+ cmd = strings.Replace(cmd, gobin, "$GOBIN", -1)
+ cmd = strings.Replace(cmd, goroot, "$GOROOT", -1)
return cmd
}
@@ -792,7 +861,7 @@ func (b *builder) fmtcmd(dir string, format string, args ...interface{}) string
func (b *builder) showcmd(dir string, format string, args ...interface{}) {
b.output.Lock()
defer b.output.Unlock()
- fmt.Println(b.fmtcmd(dir, format, args...))
+ b.print(b.fmtcmd(dir, format, args...) + "\n")
}
// showOutput prints "# desc" followed by the given output.
@@ -829,7 +898,7 @@ func (b *builder) showOutput(dir, desc, out string) {
b.output.Lock()
defer b.output.Unlock()
- fmt.Print(prefix, suffix)
+ b.print(prefix, suffix)
}
// relPaths returns a copy of paths with absolute paths
@@ -935,49 +1004,153 @@ func mkAbs(dir, f string) string {
return filepath.Join(dir, f)
}
-// gc runs the Go compiler in a specific directory on a set of files
-// to generate the named output file.
-func (b *builder) gc(p *Package, ofile string, gcargs, importArgs []string, gofiles []string) error {
- args := stringList(b.arch+"g", "-o", ofile, b.gcflags, gcargs, importArgs)
+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.
+ gc(b *builder, p *Package, obj string, importArgs []string, gofiles []string) (ofile string, 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
+ // asm runs the assembler in a specific directory on a specific file
+ // to generate the named output file.
+ asm(b *builder, p *Package, obj, ofile, sfile string) error
+ // pkgpath creates the appropriate destination path for a package file.
+ pkgpath(basedir string, p *Package) string
+ // pack runs the archive packer in a specific directory to create
+ // an archive from a set of object files.
+ // typically it is run in the object directory.
+ pack(b *builder, p *Package, objDir, afile string, ofiles []string) error
+ // ld runs the linker to create a package starting at mainpkg.
+ ld(b *builder, p *Package, out string, allactions []*action, mainpkg string, ofiles []string) error
+}
+
+type goToolchain struct{}
+type gccgoToolchain struct{}
+
+var buildToolchain toolchain
+
+func init() {
+ if os.Getenv("GC") == "gccgo" {
+ buildToolchain = gccgoToolchain{}
+ } else {
+ buildToolchain = goToolchain{}
+ }
+}
+
+// The Go toolchain.
+
+func (goToolchain) gc(b *builder, p *Package, obj string, importArgs []string, gofiles []string) (ofile string, err error) {
+ out := "_go_." + b.arch
+ ofile = obj + out
+ gcargs := []string{"-p", p.ImportPath}
+ if p.Standard && p.ImportPath == "runtime" {
+ // runtime compiles with a special 6g flag to emit
+ // additional reflect type data.
+ gcargs = append(gcargs, "-+")
+ }
+
+ args := stringList(tool(b.arch+"g"), "-o", ofile, b.gcflags, gcargs, importArgs)
for _, f := range gofiles {
args = append(args, mkAbs(p.Dir, f))
}
- return b.run(p.Dir, p.ImportPath, args)
+ return ofile, b.run(p.Dir, p.ImportPath, args)
}
-// asm runs the assembler in a specific directory on a specific file
-// to generate the named output file.
-func (b *builder) asm(p *Package, obj, ofile, sfile string) error {
+func (goToolchain) asm(b *builder, p *Package, obj, ofile, sfile string) error {
sfile = mkAbs(p.Dir, sfile)
- return b.run(p.Dir, p.ImportPath, b.arch+"a", "-I", obj, "-o", ofile, "-DGOOS_"+b.goos, "-DGOARCH_"+b.goarch, sfile)
+ return b.run(p.Dir, p.ImportPath, tool(b.arch+"a"), "-I", obj, "-o", ofile, "-DGOOS_"+b.goos, "-DGOARCH_"+b.goarch, sfile)
+}
+
+func (goToolchain) pkgpath(basedir string, p *Package) string {
+ return filepath.Join(basedir, filepath.FromSlash(p.ImportPath+".a"))
}
-// gopack runs the assembler in a specific directory to create
-// an archive from a set of object files.
-// typically it is run in the object directory.
-func (b *builder) gopack(p *Package, objDir, afile string, ofiles []string) error {
+func (goToolchain) pack(b *builder, p *Package, objDir, afile string, ofiles []string) error {
var absOfiles []string
for _, f := range ofiles {
absOfiles = append(absOfiles, mkAbs(objDir, f))
}
- return b.run(p.Dir, p.ImportPath, "gopack", "grc", mkAbs(objDir, afile), absOfiles)
+ return b.run(p.Dir, p.ImportPath, tool("pack"), "grc", mkAbs(objDir, afile), absOfiles)
}
-// ld runs the linker to create a package starting at mainpkg.
-func (b *builder) ld(p *Package, out string, importArgs []string, mainpkg string) error {
- return b.run(p.Dir, p.ImportPath, b.arch+"l", "-o", out, importArgs, mainpkg)
+func (goToolchain) ld(b *builder, p *Package, out string, allactions []*action, mainpkg string, ofiles []string) error {
+ importArgs := b.includeArgs("-L", allactions)
+ return b.run(p.Dir, p.ImportPath, tool(b.arch+"l"), "-o", out, importArgs, mainpkg)
}
-// cc runs the gc-toolchain C compiler in a directory on a C file
-// to produce an output file.
-func (b *builder) cc(p *Package, objdir, ofile, cfile string) error {
- inc := filepath.Join(b.goroot, "pkg", fmt.Sprintf("%s_%s", b.goos, b.goarch))
+func (goToolchain) cc(b *builder, p *Package, objdir, ofile, cfile string) error {
+ inc := filepath.Join(goroot, "pkg", fmt.Sprintf("%s_%s", b.goos, b.goarch))
cfile = mkAbs(p.Dir, cfile)
- return b.run(p.Dir, p.ImportPath, b.arch+"c", "-FVw",
+ return b.run(p.Dir, p.ImportPath, tool(b.arch+"c"), "-FVw",
"-I", objdir, "-I", inc, "-o", ofile,
"-DGOOS_"+b.goos, "-DGOARCH_"+b.goarch, cfile)
}
+// The Gccgo toolchain.
+
+func (gccgoToolchain) gc(b *builder, p *Package, obj string, importArgs []string, gofiles []string) (ofile string, err error) {
+ out := p.Name + ".o"
+ ofile = obj + out
+ gcargs := []string{"-g"}
+ if p.Name != "main" {
+ if p.fake {
+ gcargs = append(gcargs, "-fgo-prefix=fake_"+p.ImportPath)
+ } else {
+ gcargs = append(gcargs, "-fgo-prefix=go_"+p.ImportPath)
+ }
+ }
+ args := stringList("gccgo", importArgs, "-c", b.gcflags, gcargs, "-o", ofile)
+ for _, f := range gofiles {
+ args = append(args, mkAbs(p.Dir, f))
+ }
+ return ofile, b.run(p.Dir, p.ImportPath, args)
+}
+
+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_"+b.goos, "-DGOARCH_"+b.goarch, sfile)
+}
+
+func (gccgoToolchain) pkgpath(basedir string, p *Package) string {
+ afile := filepath.Join(basedir, filepath.FromSlash(p.ImportPath+".a"))
+ // prepend "lib" to the basename
+ return filepath.Join(filepath.Dir(afile), "lib"+filepath.Base(afile))
+}
+
+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))
+ }
+ return b.run(p.Dir, p.ImportPath, "ar", "cru", mkAbs(objDir, afile), absOfiles)
+}
+
+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 := []string{}
+ ldflags := []string{}
+ seen := map[*Package]bool{}
+ for _, a := range allactions {
+ if a.p != nil && !seen[a.p] {
+ seen[a.p] = true
+ if !a.p.Standard {
+ afiles = append(afiles, a.target)
+ }
+ ldflags = append(ldflags, a.p.CgoLDFLAGS...)
+ }
+ }
+ return b.run(p.Dir, p.ImportPath, "gccgo", "-o", out, ofiles, "-Wl,-(", afiles, ldflags, "-Wl,-)")
+}
+
+func (gccgoToolchain) cc(b *builder, p *Package, objdir, ofile, cfile string) error {
+ inc := filepath.Join(goroot, "pkg", fmt.Sprintf("%s_%s", b.goos, b.goarch))
+ cfile = mkAbs(p.Dir, cfile)
+ return b.run(p.Dir, p.ImportPath, "gcc", "-Wall", "-g",
+ "-I", objdir, "-I", inc, "-o", ofile,
+ "-DGOOS_"+b.goos, "-DGOARCH_"+b.goarch, "-c", cfile)
+}
+
// gcc runs the gcc C compiler to create an object from a single C file.
func (b *builder) gcc(p *Package, out string, flags []string, cfile string) error {
cfile = mkAbs(p.Dir, cfile)
@@ -1018,34 +1191,44 @@ func (b *builder) gccCmd(objdir string) []string {
return a
}
+func envList(key string) []string {
+ return strings.Fields(os.Getenv(key))
+}
+
var cgoRe = regexp.MustCompile(`[/\\:]`)
func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo, outObj []string, err error) {
- if b.goos != runtime.GOOS {
+ if b.goos != toolGOOS {
return nil, nil, errors.New("cannot use cgo when compiling for a different operating system")
}
- outObj = append(outObj, "") // for importObj, at end of function
+ cgoCFLAGS := stringList(envList("CGO_CFLAGS"), p.info.CgoCFLAGS)
+ cgoLDFLAGS := stringList(envList("CGO_LDFLAGS"), p.info.CgoLDFLAGS)
- cgoCFLAGS := stringList(p.info.CgoCFLAGS)
- cgoLDFLAGS := stringList(p.info.CgoLDFLAGS)
if pkgs := p.info.CgoPkgConfig; len(pkgs) > 0 {
out, err := b.runOut(p.Dir, p.ImportPath, "pkg-config", "--cflags", pkgs)
if err != nil {
- return nil, nil, err
+ b.showOutput(p.Dir, "pkg-config --cflags "+strings.Join(pkgs, " "), string(out))
+ b.print(err.Error() + "\n")
+ return nil, nil, errPrintedOutput
}
if len(out) > 0 {
cgoCFLAGS = append(cgoCFLAGS, strings.Fields(string(out))...)
}
out, err = b.runOut(p.Dir, p.ImportPath, "pkg-config", "--libs", pkgs)
if err != nil {
- return nil, nil, err
+ b.showOutput(p.Dir, "pkg-config --libs "+strings.Join(pkgs, " "), string(out))
+ b.print(err.Error() + "\n")
+ return nil, nil, errPrintedOutput
}
if len(out) > 0 {
cgoLDFLAGS = append(cgoLDFLAGS, strings.Fields(string(out))...)
}
}
+ // Allows including _cgo_export.h from .[ch] files in the package.
+ cgoCFLAGS = append(cgoCFLAGS, "-I", obj)
+
// cgo
// TODO: CGOPKGPATH, CGO_FLAGS?
gofiles := []string{obj + "_cgo_gotypes.go"}
@@ -1056,19 +1239,24 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo,
cfiles = append(cfiles, f+"cgo2.c")
}
defunC := obj + "_cgo_defun.c"
+
+ cgoflags := []string{}
// TODO: make cgo not depend on $GOARCH?
- var runtimeFlag []string
+
if p.Standard && p.ImportPath == "runtime/cgo" {
- runtimeFlag = []string{"-import_runtime_cgo=false"}
+ cgoflags = append(cgoflags, "-import_runtime_cgo=false")
}
- if err := b.run(p.Dir, p.ImportPath, cgoExe, "-objdir", obj, runtimeFlag, "--", p.CgoFiles); err != nil {
+ if _, ok := buildToolchain.(gccgoToolchain); ok {
+ cgoflags = append(cgoflags, "-gccgo")
+ }
+ if err := b.run(p.Dir, p.ImportPath, cgoExe, "-objdir", obj, cgoflags, "--", cgoCFLAGS, p.CgoFiles); err != nil {
return nil, nil, err
}
outGo = append(outGo, gofiles...)
// cc _cgo_defun.c
defunObj := obj + "_cgo_defun." + b.arch
- if err := b.cc(p, obj, defunObj, defunC); err != nil {
+ if err := buildToolchain.cc(b, p, obj, defunObj, defunC); err != nil {
return nil, nil, err
}
outObj = append(outObj, defunObj)
@@ -1098,6 +1286,11 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo,
return nil, nil, err
}
+ 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 {
@@ -1106,14 +1299,14 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo,
// cc _cgo_import.ARCH
importObj := obj + "_cgo_import." + b.arch
- if err := b.cc(p, obj, importObj, importC); err != nil {
+ if err := buildToolchain.cc(b, p, obj, importObj, importC); 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. We left room above. http://golang.org/issue/2601
- outObj[0] = importObj
+ // Put it first. http://golang.org/issue/2601
+ outObj = append([]string{importObj}, outObj...)
return outGo, outObj, nil
}
diff --git a/src/cmd/go/clean.go b/src/cmd/go/clean.go
new file mode 100644
index 000000000..1ea12b962
--- /dev/null
+++ b/src/cmd/go/clean.go
@@ -0,0 +1,197 @@
+// 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 (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strings"
+)
+
+var cmdClean = &Command{
+ UsageLine: "clean [-i] [-r] [-n] [-x] [importpath...]",
+ Short: "remove object files",
+ Long: `
+Clean removes object files from package source directories.
+The go command builds most objects in a temporary directory,
+so go clean is mainly concerned with object files left by other
+tools or by manual invocations of go build.
+
+Specifically, clean removes the following files from each of the
+source directories corresponding to the import paths:
+
+ _obj/ old object directory, left from Makefiles
+ _test/ old test directory, left from Makefiles
+ _testmain.go old gotest file, left from Makefiles
+ test.out old test log, left from Makefiles
+ build.out old test log, left from Makefiles
+ *.[568ao] object files, left from Makefiles
+
+ DIR(.exe) from go build
+ DIR.test(.exe) from go test -c
+ MAINFILE(.exe) from go build MAINFILE.go
+
+In the list, DIR represents the final path element of the
+directory, and MAINFILE is the base name of any Go source
+file in the directory that is not included when building
+the package.
+
+The -i flag causes clean to remove the corresponding installed
+archive or binary (what 'go install' would create).
+
+The -n flag causes clean to print the remove commands it would execute,
+but not run them.
+
+The -r flag causes clean to be applied recursively to all the
+dependencies of the packages named by the import paths.
+
+The -x flag causes clean to print remove commands as it executes them.
+ `,
+}
+
+var cleanI bool // clean -i flag
+var cleanN bool // clean -n flag
+var cleanR bool // clean -r flag
+var cleanX bool // clean -x flag
+
+func init() {
+ // break init cycle
+ cmdClean.Run = runClean
+
+ cmdClean.Flag.BoolVar(&cleanI, "i", false, "")
+ cmdClean.Flag.BoolVar(&cleanN, "n", false, "")
+ cmdClean.Flag.BoolVar(&cleanR, "r", false, "")
+ cmdClean.Flag.BoolVar(&cleanX, "x", false, "")
+}
+
+func runClean(cmd *Command, args []string) {
+ for _, pkg := range packagesAndErrors(args) {
+ clean(pkg)
+ }
+}
+
+var cleaned = map[*Package]bool{}
+
+// TODO: These are dregs left by Makefile-based builds.
+// Eventually, can stop deleting these.
+var cleanDir = map[string]bool{
+ "_test": true,
+ "_obj": true,
+}
+
+var cleanFile = map[string]bool{
+ "_testmain.go": true,
+ "test.out": true,
+ "build.out": true,
+ "a.out": true,
+}
+
+var cleanExt = map[string]bool{
+ ".5": true,
+ ".6": true,
+ ".8": true,
+ ".a": true,
+ ".o": true,
+}
+
+func clean(p *Package) {
+ if cleaned[p] {
+ return
+ }
+ if p.Dir == "" {
+ errorf("can't load package: %v", p.Error)
+ return
+ }
+ dirs, err := ioutil.ReadDir(p.Dir)
+ if err != nil {
+ errorf("%v", err)
+ return
+ }
+
+ var b builder
+ b.print = fmt.Print
+
+ packageFile := map[string]bool{}
+ if p.Name != "main" {
+ // Record which files are not in package main.
+ // The others are.
+ keep := func(list []string) {
+ for _, f := range list {
+ packageFile[f] = true
+ }
+ }
+ keep(p.GoFiles)
+ keep(p.CgoFiles)
+ keep(p.TestGoFiles)
+ keep(p.XTestGoFiles)
+ }
+
+ _, elem := filepath.Split(p.Dir)
+ allRemove := []string{
+ elem,
+ elem + ".exe",
+ elem + ".test",
+ elem + ".test.exe",
+ }
+ for _, dir := range dirs {
+ name := dir.Name()
+ if packageFile[name] {
+ continue
+ }
+ if !dir.IsDir() && strings.HasSuffix(name, ".go") {
+ base := name[:len(name)-len(".go")]
+ allRemove = append(allRemove, base, base+".exe")
+ }
+ }
+ if cleanN || cleanX {
+ b.showcmd(p.Dir, "rm -f %s", strings.Join(allRemove, " "))
+ }
+
+ toRemove := map[string]bool{}
+ for _, name := range allRemove {
+ toRemove[name] = true
+ }
+ for _, dir := range dirs {
+ name := dir.Name()
+ if dir.IsDir() {
+ // TODO: Remove once Makefiles are forgotten.
+ if cleanDir[name] {
+ if cleanN || cleanX {
+ b.showcmd(p.Dir, "rm -r %s", name)
+ if cleanN {
+ continue
+ }
+ }
+ os.RemoveAll(filepath.Join(p.Dir, name))
+ }
+ continue
+ }
+
+ if cleanN {
+ continue
+ }
+
+ if cleanFile[name] || cleanExt[filepath.Ext(name)] || toRemove[name] {
+ os.Remove(filepath.Join(p.Dir, name))
+ }
+ }
+
+ if cleanI && p.target != "" {
+ if cleanN || cleanX {
+ b.showcmd("", "rm -f %s", p.target)
+ }
+ if !cleanN {
+ os.Remove(p.target)
+ }
+ }
+
+ if cleanR {
+ for _, p1 := range p.imports {
+ clean(p1)
+ }
+ }
+}
diff --git a/src/cmd/go/doc.go b/src/cmd/go/doc.go
index 27be32bf3..e2df7beb1 100644
--- a/src/cmd/go/doc.go
+++ b/src/cmd/go/doc.go
@@ -10,16 +10,18 @@ Usage: go command [arguments]
The commands are:
build compile packages and dependencies
+ clean remove object files
doc run godoc on package sources
- fix run gofix on packages
+ fix run go tool fix on packages
fmt run gofmt on package sources
get download and install packages and dependencies
install compile and install packages and dependencies
list list packages
run compile and run Go program
test test packages
+ tool run specified go tool
version print Go version
- vet run govet on packages
+ vet run go tool vet on packages
Use "go help [command]" for more information about a command.
@@ -67,6 +69,48 @@ For more about import paths, see 'go help importpath'.
See also: go install, go get, go clean.
+Remove object files
+
+Usage:
+
+ go clean [-i] [-r] [-n] [-x] [importpath...]
+
+Clean removes object files from package source directories.
+The go command builds most objects in a temporary directory,
+so go clean is mainly concerned with object files left by other
+tools or by manual invocations of go build.
+
+Specifically, clean removes the following files from each of the
+source directories corresponding to the import paths:
+
+ _obj/ old object directory, left from Makefiles
+ _test/ old test directory, left from Makefiles
+ _testmain.go old gotest file, left from Makefiles
+ test.out old test log, left from Makefiles
+ build.out old test log, left from Makefiles
+ *.[568ao] object files, left from Makefiles
+
+ DIR(.exe) from go build
+ DIR.test(.exe) from go test -c
+ MAINFILE(.exe) from go build MAINFILE.go
+
+In the list, DIR represents the final path element of the
+directory, and MAINFILE is the base name of any Go source
+file in the directory that is not included when building
+the package.
+
+The -i flag causes clean to remove the corresponding installed
+archive or binary (what 'go install' would create).
+
+The -n flag causes clean to print the remove commands it would execute,
+but not run them.
+
+The -r flag causes clean to be applied recursively to all the
+dependencies of the packages named by the import paths.
+
+The -x flag causes clean to print remove commands as it executes them.
+
+
Run godoc on package sources
Usage:
@@ -84,18 +128,18 @@ To run godoc with specific options, run godoc itself.
See also: go fix, go fmt, go vet.
-Run gofix on packages
+Run go tool fix on packages
Usage:
go fix [importpath...]
-Fix runs the gofix command on the packages named by the import paths.
+Fix runs the Go fix command on the packages named by the import paths.
-For more about gofix, see 'godoc gofix'.
+For more about fix, see 'godoc fix'.
For more about import paths, see 'go help importpath'.
-To run gofix with specific options, run gofix itself.
+To run fix with specific options, run 'go tool fix'.
See also: go fmt, go vet.
@@ -132,7 +176,7 @@ and 'go install'. See 'go help install'.
The -d flag instructs get to stop after downloading the packages; that is,
it instructs get not to install the packages.
-The -fix flag instructs get to run gofix on the downloaded packages
+The -fix flag instructs get to run the fix tool on the downloaded packages
before resolving dependencies or building the code.
The -u flag instructs get to use the network to update the named packages
@@ -252,7 +296,7 @@ Test packages
Usage:
- go test [-c] [-file a.go -file b.go ...] [-p n] [-x] [importpath...] [flags for test binary]
+ go test [-c] [-file a.go -file b.go ...] [-i] [-p n] [-x] [importpath...] [flags for test binary]
'Go test' automates testing the packages named by the import paths.
It prints a summary of the test results in the format:
@@ -285,6 +329,18 @@ See 'go help importpath' for more about import paths.
See also: go build, go vet.
+Run specified go tool
+
+Usage:
+
+ go tool command [args...]
+
+Tool runs the go tool command identified by the arguments.
+With no arguments it prints the list of known tools.
+
+For more about each tool command, see 'go tool command -h'.
+
+
Print Go version
Usage:
@@ -294,18 +350,18 @@ Usage:
Version prints the Go version, as reported by runtime.Version.
-Run govet on packages
+Run go tool vet on packages
Usage:
go vet [importpath...]
-Vet runs the govet command on the packages named by the import paths.
+Vet runs the Go vet command on the packages named by the import paths.
-For more about govet, see 'godoc govet'.
+For more about vet, see 'godoc vet'.
For more about import paths, see 'go help importpath'.
-To run govet with specific options, run govet itself.
+To run the vet tool with specific options, run 'go tool vet'.
See also: go fmt, go fix.
@@ -487,19 +543,23 @@ and flags that apply to the resulting test binary.
The flags handled by 'go test' are:
- -c Compile the test binary to test.out but do not run it.
+ -c Compile the test binary to pkg.test but do not run it.
-file a.go
Use only the tests in the source file a.go.
Multiple -file flags may be provided.
+ -i
+ Install packages that are dependencies of the test.
+
-p n
Compile and test up to n packages in parallel.
The default value is the number of CPUs available.
-x Print each subcommand go test executes.
-The resulting test binary, called test.out, has its own flags:
+The resulting 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.
@@ -557,7 +617,7 @@ here are passed through unaltered. For instance, the command
will compile the test binary using x_test.go and then run it as
- test.out -test.v -test.cpuprofile=prof.out -dir=testdata -update
+ pkg.test -test.v -test.cpuprofile=prof.out -dir=testdata -update
Description of testing functions
diff --git a/src/cmd/go/fix.go b/src/cmd/go/fix.go
index bae9f5c98..6a0ad0774 100644
--- a/src/cmd/go/fix.go
+++ b/src/cmd/go/fix.go
@@ -7,14 +7,14 @@ package main
var cmdFix = &Command{
Run: runFix,
UsageLine: "fix [importpath...]",
- Short: "run gofix on packages",
+ Short: "run go tool fix on packages",
Long: `
-Fix runs the gofix command on the packages named by the import paths.
+Fix runs the Go fix command on the packages named by the import paths.
-For more about gofix, see 'godoc gofix'.
+For more about fix, see 'godoc fix'.
For more about import paths, see 'go help importpath'.
-To run gofix with specific options, run gofix itself.
+To run fix with specific options, run 'go tool fix'.
See also: go fmt, go vet.
`,
@@ -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("gofix", relPaths(pkg.gofiles)))
+ run(stringList(tool("fix"), relPaths(pkg.gofiles)))
}
}
diff --git a/src/cmd/go/get.go b/src/cmd/go/get.go
index cd57d3025..e66810cb3 100644
--- a/src/cmd/go/get.go
+++ b/src/cmd/go/get.go
@@ -29,7 +29,7 @@ and 'go install'. See 'go help install'.
The -d flag instructs get to stop after downloading the packages; that is,
it instructs get not to install the packages.
-The -fix flag instructs get to run gofix on the downloaded packages
+The -fix flag instructs get to run the fix tool on the downloaded packages
before resolving dependencies or building the code.
The -u flag instructs get to use the network to update the named packages
@@ -119,7 +119,7 @@ func download(arg string, stk *importStack) {
stk.push(p.ImportPath)
defer stk.pop()
if err := downloadPackage(p); err != nil {
- errorf("%s", &PackageError{stk.copy(), err.Error()})
+ errorf("%s", &PackageError{ImportStack: stk.copy(), Err: err.Error()})
return
}
@@ -132,7 +132,7 @@ func download(arg string, stk *importStack) {
}
if *getFix {
- run(stringList("gofix", relPaths(p.gofiles)))
+ run(stringList(tool("fix"), relPaths(p.gofiles)))
// The imports might have changed, so reload again.
p = reloadPackage(arg, stk)
@@ -215,11 +215,7 @@ func downloadPackage(p *Package) error {
if i := strings.Index(vers, " "); i >= 0 {
vers = vers[:i]
}
- tag := selectTag(vers, tags)
- if tag == "" {
- tag = vcs.tagDefault
- }
- if err := vcs.tagSync(root, tag); err != nil {
+ if err := vcs.tagSync(root, selectTag(vers, tags)); err != nil {
return err
}
diff --git a/src/cmd/go/list.go b/src/cmd/go/list.go
index af211f98d..30baaa7b2 100644
--- a/src/cmd/go/list.go
+++ b/src/cmd/go/list.go
@@ -5,6 +5,7 @@
package main
import (
+ "bufio"
"encoding/json"
"os"
"text/template"
@@ -81,15 +82,19 @@ var listJson = cmdList.Flag.Bool("json", false, "")
var nl = []byte{'\n'}
func runList(cmd *Command, args []string) {
+ out := bufio.NewWriter(os.Stdout)
+ defer out.Flush()
+
var do func(*Package)
if *listJson {
do = func(p *Package) {
b, err := json.MarshalIndent(p, "", "\t")
if err != nil {
+ out.Flush()
fatalf("%s", err)
}
- os.Stdout.Write(b)
- os.Stdout.Write(nl)
+ out.Write(b)
+ out.Write(nl)
}
} else {
tmpl, err := template.New("main").Parse(*listFmt + "\n")
@@ -97,7 +102,8 @@ func runList(cmd *Command, args []string) {
fatalf("%s", err)
}
do = func(p *Package) {
- if err := tmpl.Execute(os.Stdout, p); err != nil {
+ if err := tmpl.Execute(out, p); err != nil {
+ out.Flush()
fatalf("%s", err)
}
}
diff --git a/src/cmd/go/main.go b/src/cmd/go/main.go
index ca3b1188a..b07d720e8 100644
--- a/src/cmd/go/main.go
+++ b/src/cmd/go/main.go
@@ -17,6 +17,7 @@ import (
"path/filepath"
"regexp"
"strings"
+ "sync"
"text/template"
"unicode"
"unicode/utf8"
@@ -63,10 +64,17 @@ func (c *Command) Usage() {
os.Exit(2)
}
+// Runnable reports whether the command can be run; otherwise
+// it is a documentation pseudo-command such as importpath.
+func (c *Command) Runnable() bool {
+ return c.Run != nil
+}
+
// Commands lists the available commands and help topics.
// The order here is the order in which they are printed by 'go help'.
var commands = []*Command{
cmdBuild,
+ cmdClean,
cmdDoc,
cmdFix,
cmdFmt,
@@ -75,6 +83,7 @@ var commands = []*Command{
cmdList,
cmdRun,
cmdTest,
+ cmdTool,
cmdVersion,
cmdVet,
@@ -86,6 +95,15 @@ var commands = []*Command{
}
var exitStatus = 0
+var exitMu sync.Mutex
+
+func setExitStatus(n int) {
+ exitMu.Lock()
+ if exitStatus < n {
+ exitStatus = n
+ }
+ exitMu.Unlock()
+}
func main() {
flag.Usage = usage
@@ -126,20 +144,20 @@ var usageTemplate = `Go is a tool for managing Go source code.
Usage: go command [arguments]
The commands are:
-{{range .}}{{if .Run}}
+{{range .}}{{if .Runnable}}
{{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}}
Use "go help [command]" for more information about a command.
Additional help topics:
-{{range .}}{{if not .Run}}
+{{range .}}{{if not .Runnable}}
{{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}}
Use "go help [topic]" for more information about that topic.
`
-var helpTemplate = `{{if .Run}}usage: go {{.UsageLine}}
+var helpTemplate = `{{if .Runnable}}usage: go {{.UsageLine}}
{{end}}{{.Long | trim}}
`
@@ -151,7 +169,7 @@ var documentationTemplate = `// Copyright 2011 The Go Authors. All rights reser
/*
{{range .}}{{if .Short}}{{.Short | capitalize}}
-{{end}}{{if .Run}}Usage:
+{{end}}{{if .Runnable}}Usage:
go {{.UsageLine}}
@@ -266,7 +284,7 @@ func fatalf(format string, args ...interface{}) {
func errorf(format string, args ...interface{}) {
log.Printf(format, args...)
- exitStatus = 1
+ setExitStatus(1)
}
var logf = log.Printf
@@ -335,7 +353,7 @@ func allPackages(pattern string) []string {
goroot := build.Path[0].Path
cmd := filepath.Join(goroot, "src/cmd") + string(filepath.Separator)
filepath.Walk(cmd, func(path string, fi os.FileInfo, err error) error {
- if err != nil || !fi.IsDir() {
+ if err != nil || !fi.IsDir() || path == cmd {
return nil
}
name := path[len(cmd):]
@@ -366,7 +384,7 @@ func allPackages(pattern string) []string {
}
src := t.SrcDir() + string(filepath.Separator)
filepath.Walk(src, func(path string, fi os.FileInfo, err error) error {
- if err != nil || !fi.IsDir() {
+ if err != nil || !fi.IsDir() || path == src {
return nil
}
@@ -386,7 +404,7 @@ func allPackages(pattern string) []string {
have[name] = true
_, err = build.ScanDir(path)
- if err != nil {
+ if err != nil && strings.Contains(err.Error(), "no Go source files") {
return nil
}
@@ -433,7 +451,7 @@ func allPackagesInFS(pattern string) []string {
var pkgs []string
filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error {
- if err != nil || !fi.IsDir() {
+ if err != nil || !fi.IsDir() || path == dir {
return nil
}
diff --git a/src/cmd/go/pkg.go b/src/cmd/go/pkg.go
index 09fa67127..718b9fea0 100644
--- a/src/cmd/go/pkg.go
+++ b/src/cmd/go/pkg.go
@@ -5,7 +5,11 @@
package main
import (
+ "bytes"
+ "fmt"
"go/build"
+ "go/doc"
+ "go/scanner"
"os"
"path/filepath"
"sort"
@@ -22,6 +26,7 @@ type Package struct {
Name string `json:",omitempty"` // package name
Doc string `json:",omitempty"` // package documentation string
Dir string `json:",omitempty"` // directory containing package sources
+ Target string `json:",omitempty"` // install path
Version string `json:",omitempty"` // version of installed package (TODO)
Standard bool `json:",omitempty"` // is this package part of the standard Go library?
Stale bool `json:",omitempty"` // would 'go install' do anything for this package?
@@ -58,10 +63,14 @@ type Package struct {
// A PackageError describes an error loading information about a package.
type PackageError struct {
ImportStack []string // shortest path from package named on command line to this one
+ Pos string // position of error
Err string // the error itself
}
func (p *PackageError) Error() string {
+ if p.Pos != "" {
+ return strings.Join(p.ImportStack, "\n\timports ") + ": " + p.Pos + ": " + p.Err
+ }
return strings.Join(p.ImportStack, "\n\timports ") + ": " + p.Err
}
@@ -128,6 +137,7 @@ func loadPackage(arg string, stk *importStack) *Package {
}
// Find basic information about package path.
+ isCmd := false
t, importPath, err := build.FindTree(arg)
dir := ""
// Maybe it is a standard command.
@@ -139,6 +149,7 @@ func loadPackage(arg string, stk *importStack) *Package {
importPath = arg
dir = p
err = nil
+ isCmd = true
}
}
// Maybe it is a path to a standard command.
@@ -151,6 +162,7 @@ func loadPackage(arg string, stk *importStack) *Package {
importPath = filepath.FromSlash(arg[len(cmd):])
dir = arg
err = nil
+ isCmd = true
}
}
if err != nil {
@@ -171,12 +183,23 @@ func loadPackage(arg string, stk *importStack) *Package {
}
// Maybe we know the package by its directory.
- if p := packageCache[dir]; p != nil {
+ p := packageCache[dir]
+ if p != nil {
packageCache[importPath] = p
- return reusePackage(p, stk)
+ p = reusePackage(p, stk)
+ } else {
+ p = scanPackage(&buildContext, t, arg, importPath, dir, stk, false)
}
- return scanPackage(&buildContext, t, arg, importPath, dir, stk)
+ // If we loaded the files from the Go root's cmd/ tree,
+ // it must be a command (package main).
+ if isCmd && p.Error == nil && p.Name != "main" {
+ p.Error = &PackageError{
+ ImportStack: stk.copy(),
+ Err: fmt.Sprintf("expected package main in %q; found package %s", dir, p.Name),
+ }
+ }
+ return p
}
func reusePackage(p *Package, stk *importStack) *Package {
@@ -198,33 +221,19 @@ func reusePackage(p *Package, stk *importStack) *Package {
return p
}
-// firstSentence returns the first sentence of the document text.
-// The sentence ends after the first period followed by a space.
-// The returned sentence will have no \n \r or \t characters and
-// will use only single spaces between words.
-func firstSentence(text string) string {
- var b []byte
- space := true
-Loop:
- for i := 0; i < len(text); i++ {
- switch c := text[i]; c {
- case ' ', '\t', '\r', '\n':
- if !space {
- space = true
- if len(b) > 0 && b[len(b)-1] == '.' {
- break Loop
- }
- b = append(b, ' ')
- }
- default:
- space = false
- b = append(b, c)
- }
- }
- return string(b)
+// isGoTool is the list of directories for Go programs that are installed in
+// $GOROOT/bin/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,
}
-func scanPackage(ctxt *build.Context, t *build.Tree, arg, importPath, dir string, stk *importStack) *Package {
+func scanPackage(ctxt *build.Context, t *build.Tree, arg, importPath, dir string, stk *importStack, useAllFiles bool) *Package {
// Read the files in the directory to learn the structure
// of the package.
p := &Package{
@@ -236,19 +245,35 @@ func scanPackage(ctxt *build.Context, t *build.Tree, arg, importPath, dir string
packageCache[dir] = p
packageCache[importPath] = p
+ ctxt.UseAllFiles = useAllFiles
info, err := ctxt.ScanDir(dir)
+ useAllFiles = false // flag does not apply to dependencies
if err != nil {
p.Error = &PackageError{
ImportStack: stk.copy(),
Err: err.Error(),
}
+ // Look for parser errors.
+ if err, ok := err.(scanner.ErrorList); ok {
+ // Prepare error with \n before each message.
+ // When printed in something like context: %v
+ // this will put the leading file positions each on
+ // its own line. It will also show all the errors
+ // instead of just the first, as err.Error does.
+ var buf bytes.Buffer
+ for _, e := range err {
+ buf.WriteString("\n")
+ buf.WriteString(e.Error())
+ }
+ p.Error.Err = buf.String()
+ }
p.Incomplete = true
return p
}
p.info = info
p.Name = info.Package
- p.Doc = firstSentence(info.PackageComment.Text())
+ p.Doc = doc.Synopsis(info.PackageComment.Text())
p.Imports = info.Imports
p.GoFiles = info.GoFiles
p.TestGoFiles = info.TestGoFiles
@@ -262,12 +287,26 @@ func scanPackage(ctxt *build.Context, t *build.Tree, arg, importPath, dir string
if info.Package == "main" {
_, elem := filepath.Split(importPath)
- p.target = filepath.Join(t.BinDir(), elem)
+ full := ctxt.GOOS + "_" + ctxt.GOARCH + "/" + elem
+ if t.Goroot && isGoTool[p.ImportPath] {
+ p.target = filepath.Join(t.Path, "pkg/tool", full)
+ } else {
+ if ctxt.GOOS != toolGOOS || ctxt.GOARCH != toolGOARCH {
+ // Install cross-compiled binaries to subdirectories of bin.
+ elem = full
+ }
+ p.target = filepath.Join(t.BinDir(), elem)
+ }
if ctxt.GOOS == "windows" {
p.target += ".exe"
}
} else {
- p.target = filepath.Join(t.PkgDir(), filepath.FromSlash(importPath)+".a")
+ dir := t.PkgDir()
+ // For gccgo, rewrite p.target with the expected library name.
+ if _, ok := buildToolchain.(gccgoToolchain); ok {
+ dir = filepath.Join(filepath.Dir(dir), "gccgo", filepath.Base(dir))
+ }
+ p.target = buildToolchain.pkgpath(dir, p)
}
var built time.Time
@@ -334,6 +373,12 @@ Stale:
}
deps[path] = true
p1 := loadPackage(path, stk)
+ if p1.Error != nil {
+ if info.ImportPos != nil && len(info.ImportPos[path]) > 0 {
+ pos := info.ImportPos[path][0]
+ p1.Error.Pos = pos.String()
+ }
+ }
imports = append(imports, p1)
for _, dep := range p1.Deps {
deps[dep] = true
@@ -379,6 +424,8 @@ Stale:
p.target = ""
}
+ p.Target = p.target
+
return p
}
@@ -397,7 +444,7 @@ func packages(args []string) []*Package {
for _, arg := range args {
pkg := loadPackage(arg, &stk)
if pkg.Error != nil {
- errorf("%s", pkg.Error)
+ errorf("can't load package: %s", pkg.Error)
continue
}
pkgs = append(pkgs, pkg)
@@ -427,7 +474,7 @@ func packagesForBuild(args []string) []*Package {
printed := map[*PackageError]bool{}
for _, pkg := range pkgs {
if pkg.Error != nil {
- errorf("%s", pkg.Error)
+ errorf("can't load package: %s", pkg.Error)
}
for _, err := range pkg.DepsErrors {
// Since these are errors in dependencies,
diff --git a/src/cmd/go/run.go b/src/cmd/go/run.go
index 714cd4051..f317620e7 100644
--- a/src/cmd/go/run.go
+++ b/src/cmd/go/run.go
@@ -4,7 +4,12 @@
package main
-import "strings"
+import (
+ "fmt"
+ "os"
+ "os/exec"
+ "strings"
+)
var cmdRun = &Command{
UsageLine: "run [-a] [-n] [-x] gofiles... [arguments...]",
@@ -28,9 +33,14 @@ func init() {
cmdRun.Flag.BoolVar(&buildX, "x", false, "")
}
+func printStderr(args ...interface{}) (int, error) {
+ return fmt.Fprint(os.Stderr, args...)
+}
+
func runRun(cmd *Command, args []string) {
var b builder
b.init()
+ b.print = printStderr
i := 0
for i < len(args) && strings.HasSuffix(args[i], ".go") {
i++
@@ -52,6 +62,19 @@ func (b *builder) runProgram(a *action) error {
return nil
}
}
- run(a.deps[0].target, a.args)
+
+ runStdin(a.deps[0].target, a.args)
return nil
}
+
+// runStdin is like run, but connects Stdin.
+func runStdin(cmdargs ...interface{}) {
+ cmdline := stringList(cmdargs...)
+ cmd := exec.Command(cmdline[0], cmdline[1:]...)
+ cmd.Stdin = os.Stdin
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ if err := cmd.Run(); err != nil {
+ errorf("%v", err)
+ }
+}
diff --git a/src/cmd/go/test.go b/src/cmd/go/test.go
index 6cd49fe5a..57cdc7696 100644
--- a/src/cmd/go/test.go
+++ b/src/cmd/go/test.go
@@ -9,11 +9,15 @@ import (
"fmt"
"go/ast"
"go/build"
+ "go/doc"
"go/parser"
"go/token"
"os"
"os/exec"
+ "path"
"path/filepath"
+ "runtime"
+ "sort"
"strings"
"text/template"
"time"
@@ -28,7 +32,7 @@ func init() {
var cmdTest = &Command{
CustomFlags: true,
- UsageLine: "test [-c] [-file a.go -file b.go ...] [-p n] [-x] [importpath...] [flags for test binary]",
+ UsageLine: "test [-c] [-i] [-p n] [-x] [importpath...] [flags for test binary]",
Short: "test packages",
Long: `
'Go test' automates testing the packages named by the import paths.
@@ -47,15 +51,26 @@ benchmark functions, and example functions. See 'go help testfunc' for more.
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.
-If file names are given (with flag -file=test.go, one per extra test source file),
-only those test files are added to the package. (The non-test files are always
-compiled.)
The package is built in a temporary directory so it does not interfere with the
non-test installation.
-See 'go help testflag' for details about flags handled by 'go test'
-and the test binary.
+The flags handled by 'go test' itself are:
+
+ -c Compile the test binary to pkg.test but do not run it.
+
+ -i
+ Install packages that are dependencies of the test.
+ Do not run the test.
+
+ -p n
+ Compile and test up to n packages in parallel.
+ The default value is the number of CPUs available.
+
+ -x Print each subcommand go test executes.
+
+The test binary also accepts flags that control execution of the test; these
+flags are also accessible by 'go test'. See 'go help testflag' for details.
See 'go help importpath' for more about import paths.
@@ -70,21 +85,8 @@ 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 flags handled by 'go test' are:
-
- -c Compile the test binary to test.out but do not run it.
-
- -file a.go
- Use only the tests in the source file a.go.
- Multiple -file flags may be provided.
-
- -p n
- Compile and test up to n packages in parallel.
- The default value is the number of CPUs available.
-
- -x Print each subcommand go test executes.
-
-The resulting test binary, called test.out, has its own flags:
+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.
@@ -138,11 +140,11 @@ 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
- go test -x -v -cpuprofile=prof.out -dir=testdata -update -file x_test.go
+ go test -x -v -cpuprofile=prof.out -dir=testdata -update
-will compile the test binary using x_test.go and then run it as
+will compile the test binary and then run it as
- test.out -test.v -test.cpuprofile=prof.out -dir=testdata -update
+ pkg.test -test.v -test.cpuprofile=prof.out -dir=testdata -update
`,
}
@@ -175,35 +177,39 @@ where xxx is a suffix not beginning with an upper case letter.
Here is an example of an example:
- // The output of this example function.
func ExamplePrintln() {
Println("The output of this example function.")
+ // Output: The output of this example function.
}
+The entire test file is presented as the example when it contains a single
+example function, at least one other function, type, variable, or constant
+declaration, and no test or benchmark functions.
+
See the documentation of the testing package for more information.
`,
}
var (
- testC bool // -c flag
- testP int // -p flag
- testX bool // -x flag
- testV bool // -v flag
- testFiles []string // -file flag(s) TODO: not respected
- testArgs []string
- testShowPass bool // whether to display passing output
- testBench bool
+ testC bool // -c flag
+ testI bool // -i flag
+ testP int // -p flag
+ testX bool // -x flag
+ testV bool // -v flag
+ testFiles []string // -file flag(s) TODO: not respected
+ testTimeout string // -timeout flag
+ testArgs []string
+ testBench bool
+ testStreamOutput bool // show output as it is generated
+ testShowPass bool // show passing output
+
+ testKillTimeout = 10 * time.Minute
)
func runTest(cmd *Command, args []string) {
var pkgArgs []string
pkgArgs, testArgs = testFlags(args)
- // show test PASS output when no packages
- // are listed (implicitly current directory: "go test")
- // or when the -v flag has been given.
- testShowPass = len(pkgArgs) == 0 || testV
-
pkgs := packagesForBuild(pkgArgs)
if len(pkgs) == 0 {
fatalf("no packages to test")
@@ -213,6 +219,29 @@ func runTest(cmd *Command, args []string) {
fatalf("cannot use -c 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 {
+ testKillTimeout = dt + 1*time.Minute
+ }
+
+ // show passing test output (after buffering) with -v flag.
+ // must buffer because tests are running in parallel, and
+ // otherwise the output will get mixed.
+ testShowPass = testV
+
+ // stream test output (no buffering) when no package has
+ // been given on the command line (implicit current directory)
+ // or when benchmarking.
+ // Also stream if we're showing output anyway with a
+ // single package under test. In that case, streaming the
+ // output produces the same result as not streaming,
+ // just more immediately.
+ testStreamOutput = len(pkgArgs) == 0 || testBench ||
+ (len(pkgs) <= 1 && testShowPass)
+
buildX = testX
if testP > 0 {
buildP = testP
@@ -221,6 +250,49 @@ func runTest(cmd *Command, args []string) {
var b builder
b.init()
+ if testI {
+ buildV = testV
+
+ deps := map[string]bool{
+ // Dependencies for testmain.
+ "testing": true,
+ "regexp": true,
+ }
+ for _, p := range pkgs {
+ // Dependencies for each test.
+ for _, path := range p.info.Imports {
+ deps[path] = true
+ }
+ for _, path := range p.info.TestImports {
+ deps[path] = true
+ }
+ }
+
+ // translate C to runtime/cgo
+ if deps["C"] {
+ delete(deps, "C")
+ deps["runtime/cgo"] = true
+ if buildContext.GOOS == runtime.GOOS && buildContext.GOARCH == runtime.GOARCH {
+ deps["cmd/cgo"] = true
+ }
+ }
+ // Ignore pseudo-packages.
+ delete(deps, "unsafe")
+
+ all := []string{}
+ for path := range deps {
+ all = append(all, path)
+ }
+ sort.Strings(all)
+
+ a := &action{}
+ for _, p := range packagesForBuild(all) {
+ a.deps = append(a.deps, b.action(modeInstall, modeInstall, p))
+ }
+ b.do(a)
+ return
+ }
+
var builds, runs, prints []*action
// Prepare build + run + print actions for all packages being tested.
@@ -279,7 +351,11 @@ func runTest(cmd *Command, args []string) {
}
}
if warned {
- fmt.Fprintf(os.Stderr, "installing these packages with 'go install' will speed future tests.\n\n")
+ args := strings.Join(pkgArgs, " ")
+ if args != "" {
+ args = " " + args
+ }
+ fmt.Fprintf(os.Stderr, "installing these packages with 'go test -i%s' will speed future tests.\n\n", args)
}
b.do(root)
@@ -296,7 +372,7 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,
// Build Package structs describing:
// ptest - package + test files
// pxtest - package of external test files
- // pmain - test.out binary
+ // pmain - pkg.test binary
var ptest, pxtest, pmain *Package
// go/build does not distinguish the dependencies used
@@ -315,6 +391,11 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,
}
stk.pop()
+ // Use last element of import path, not package name.
+ // They differ when package name is "main".
+ _, elem := path.Split(p.ImportPath)
+ testBinary := elem + ".test"
+
// The ptest package needs to be importable under the
// same import path that p has, but we cannot put it in
// the usual place in the temporary tree, because then
@@ -330,8 +411,7 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,
// We write the external test package archive to
// $WORK/unicode/utf8/_test/unicode/utf8_test.a.
testDir := filepath.Join(b.work, filepath.FromSlash(p.ImportPath+"/_test"))
- ptestObj := filepath.Join(testDir, filepath.FromSlash(p.ImportPath+".a"))
- pxtestObj := filepath.Join(testDir, filepath.FromSlash(p.ImportPath+"_test.a"))
+ ptestObj := buildToolchain.pkgpath(testDir, p)
// Create the directory for the .a files.
ptestDir, _ := filepath.Split(ptestObj)
@@ -380,11 +460,11 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,
pxtest.imports = append(pxtest.imports, ptest)
a := b.action(modeBuild, modeBuild, pxtest)
a.objdir = testDir + string(filepath.Separator)
- a.objpkg = pxtestObj
- a.target = pxtestObj
+ a.objpkg = buildToolchain.pkgpath(testDir, pxtest)
+ a.target = a.objpkg
}
- // Action for building test.out.
+ // Action for building pkg.test.
pmain = &Package{
Name: "main",
Dir: testDir,
@@ -413,7 +493,7 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,
a := b.action(modeBuild, modeBuild, pmain)
a.objdir = testDir + string(filepath.Separator)
a.objpkg = filepath.Join(testDir, "main.a")
- a.target = filepath.Join(testDir, "test.out") + b.exe
+ a.target = filepath.Join(testDir, testBinary) + b.exe
pmainAction := a
if testC {
@@ -422,7 +502,7 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,
f: (*builder).install,
deps: []*action{pmainAction},
p: pmain,
- target: "test.out" + b.exe,
+ target: testBinary + b.exe,
}
printAction = &action{p: p, deps: []*action{runAction}} // nop
} else {
@@ -433,9 +513,14 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,
p: p,
ignoreFail: true,
}
+ cleanAction := &action{
+ f: (*builder).cleanTest,
+ deps: []*action{runAction},
+ p: p,
+ }
printAction = &action{
f: (*builder).printTest,
- deps: []*action{runAction},
+ deps: []*action{cleanAction},
p: p,
}
}
@@ -459,15 +544,20 @@ func (b *builder) runTest(a *action) error {
// We were unable to build the binary.
a.failed = false
fmt.Fprintf(a.testOutput, "FAIL\t%s [build failed]\n", a.p.ImportPath)
- exitStatus = 1
+ setExitStatus(1)
return nil
}
cmd := exec.Command(args[0], args[1:]...)
cmd.Dir = a.p.Dir
var buf bytes.Buffer
- cmd.Stdout = &buf
- cmd.Stderr = &buf
+ if testStreamOutput {
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ } else {
+ cmd.Stdout = &buf
+ cmd.Stderr = &buf
+ }
t0 := time.Now()
err := cmd.Start()
@@ -475,9 +565,7 @@ func (b *builder) runTest(a *action) error {
// This is a last-ditch deadline to detect and
// stop wedged test binaries, to keep the builders
// running.
- const deadline = 10 * time.Minute
-
- tick := time.NewTimer(deadline)
+ tick := time.NewTimer(testKillTimeout)
if err == nil {
done := make(chan error)
go func() {
@@ -497,27 +585,37 @@ func (b *builder) runTest(a *action) error {
t1 := time.Now()
t := fmt.Sprintf("%.3fs", t1.Sub(t0).Seconds())
if err == nil {
- fmt.Fprintf(a.testOutput, "ok \t%s\t%s\n", a.p.ImportPath, t)
if testShowPass {
a.testOutput.Write(out)
}
+ fmt.Fprintf(a.testOutput, "ok \t%s\t%s\n", a.p.ImportPath, t)
return nil
}
- fmt.Fprintf(a.testOutput, "FAIL\t%s\t%s\n", a.p.ImportPath, t)
- exitStatus = 1
+ setExitStatus(1)
if len(out) > 0 {
a.testOutput.Write(out)
// assume printing the test binary's exit status is superfluous
} else {
fmt.Fprintf(a.testOutput, "%s\n", err)
}
+ fmt.Fprintf(a.testOutput, "FAIL\t%s\t%s\n", a.p.ImportPath, t)
+
+ return nil
+}
+
+// cleanTest is the action for cleaning up after a test.
+func (b *builder) cleanTest(a *action) error {
+ run := a.deps[0]
+ testDir := filepath.Join(b.work, filepath.FromSlash(run.p.ImportPath+"/_test"))
+ os.RemoveAll(testDir)
return nil
}
// printTest is the action for printing a test result.
func (b *builder) printTest(a *action) error {
- run := a.deps[0]
+ clean := a.deps[0]
+ run := clean.deps[0]
os.Stdout.Write(run.testOutput.Bytes())
run.testOutput = nil
return nil
@@ -613,17 +711,16 @@ func (t *testFuncs) load(filename, pkg string, seen *bool) error {
case isTest(name, "Benchmark"):
t.Benchmarks = append(t.Benchmarks, testFunc{pkg, name, ""})
*seen = true
- case isTest(name, "Example"):
- output := n.Doc.Text()
- if output == "" {
- // Don't run examples with no output.
- continue
- }
- t.Examples = append(t.Examples, testFunc{pkg, name, output})
- *seen = true
}
}
-
+ for _, e := range doc.Examples(f) {
+ if e.Output == "" {
+ // Don't run examples with no output.
+ continue
+ }
+ t.Examples = append(t.Examples, testFunc{pkg, "Example" + e.Name, e.Output})
+ *seen = true
+ }
return nil
}
diff --git a/src/cmd/go/testflag.go b/src/cmd/go/testflag.go
index a3cacd657..7c9b7f16d 100644
--- a/src/cmd/go/testflag.go
+++ b/src/cmd/go/testflag.go
@@ -40,7 +40,7 @@ var usageMessage = `Usage of go test:
// usage prints a usage message and exits.
func testUsage() {
fmt.Fprint(os.Stderr, usageMessage)
- exitStatus = 2
+ setExitStatus(2)
exit()
}
@@ -58,6 +58,7 @@ var testFlagDefn = []*testFlagSpec{
// local.
{name: "c", isBool: true},
{name: "file", multiOK: true},
+ {name: "i", isBool: true},
{name: "p"},
{name: "x", isBool: true},
@@ -79,7 +80,7 @@ var testFlagDefn = []*testFlagSpec{
// to have "test" before them, and reading the command line for the 6.out.
// Unfortunately for us, we need to do our own flag processing because go test
// grabs some flags but otherwise its command line is just a holding place for
-// test.out's arguments.
+// pkg.test's arguments.
// We allow known flags both before and after the package name list,
// to allow both
// go test fmt -custom-flag-for-fmt-test
@@ -119,6 +120,8 @@ func testFlags(args []string) (packageNames, passToTest []string) {
switch f.name {
case "c":
setBoolFlag(&testC, value)
+ case "i":
+ setBoolFlag(&testI, value)
case "p":
setIntFlag(&testP, value)
case "x":
@@ -130,6 +133,8 @@ func testFlags(args []string) (packageNames, passToTest []string) {
case "bench":
// record that we saw the flag; don't care about the value
testBench = true
+ case "timeout":
+ testTimeout = value
}
if extraWord {
i++
diff --git a/src/cmd/go/tool.go b/src/cmd/go/tool.go
new file mode 100644
index 000000000..9776d3359
--- /dev/null
+++ b/src/cmd/go/tool.go
@@ -0,0 +1,125 @@
+// Copyright 2011 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 (
+ "fmt"
+ "go/build"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "runtime"
+ "sort"
+ "strings"
+)
+
+var cmdTool = &Command{
+ Run: runTool,
+ UsageLine: "tool [-n] command [args...]",
+ Short: "run specified go tool",
+ Long: `
+Tool runs the go tool command identified by the arguments.
+With no arguments it prints the list of known tools.
+
+The -n flag causes tool to print the command that would be
+executed but not execute it.
+
+For more about each tool command, see 'go tool command -h'.
+`,
+}
+
+var (
+ toolGOOS = runtime.GOOS
+ toolGOARCH = runtime.GOARCH
+ toolIsWindows = toolGOOS == "windows"
+ toolDir = build.ToolDir
+
+ toolN bool
+)
+
+func init() {
+ cmdTool.Flag.BoolVar(&toolN, "n", false, "")
+}
+
+const toolWindowsExtension = ".exe"
+
+func tool(name string) string {
+ p := filepath.Join(toolDir, name)
+ if toolIsWindows {
+ p += toolWindowsExtension
+ }
+ return p
+}
+
+func runTool(cmd *Command, args []string) {
+ if len(args) == 0 {
+ listTools()
+ return
+ }
+ toolName := args[0]
+ // The tool name must be lower-case letters and numbers.
+ for _, c := range toolName {
+ switch {
+ case 'a' <= c && c <= 'z', '0' <= c && c <= '9':
+ default:
+ fmt.Fprintf(os.Stderr, "go tool: bad tool name %q\n", toolName)
+ setExitStatus(2)
+ return
+ }
+ }
+ toolPath := tool(toolName)
+ // Give a nice message if there is no tool with that name.
+ if _, err := os.Stat(toolPath); err != nil {
+ fmt.Fprintf(os.Stderr, "go tool: no such tool %q\n", toolName)
+ setExitStatus(3)
+ return
+ }
+
+ if toolN {
+ fmt.Printf("%s %s\n", toolPath, strings.Join(args[1:], " "))
+ return
+ }
+ toolCmd := &exec.Cmd{
+ Path: toolPath,
+ Args: args,
+ Stdin: os.Stdin,
+ Stdout: os.Stdout,
+ Stderr: os.Stderr,
+ }
+ err := toolCmd.Run()
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "go tool %s: %s\n", toolName, err)
+ setExitStatus(1)
+ return
+ }
+}
+
+// listTools prints a list of the available tools in the tools directory.
+func listTools() {
+ f, err := os.Open(toolDir)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "go tool: no tool directory: %s\n", err)
+ setExitStatus(2)
+ return
+ }
+ defer f.Close()
+ names, err := f.Readdirnames(-1)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "go tool: can't read directory: %s\n", err)
+ setExitStatus(2)
+ return
+ }
+
+ sort.Strings(names)
+ for _, name := range names {
+ // Unify presentation by going to lower case.
+ name = strings.ToLower(name)
+ // If it's windows, don't show the .exe suffix.
+ if toolIsWindows && strings.HasSuffix(name, toolWindowsExtension) {
+ name = name[:len(name)-len(toolWindowsExtension)]
+ }
+ fmt.Println(name)
+ }
+}
diff --git a/src/cmd/go/vcs.go b/src/cmd/go/vcs.go
index da35048d5..2a7bdd034 100644
--- a/src/cmd/go/vcs.go
+++ b/src/cmd/go/vcs.go
@@ -23,9 +23,13 @@ type vcsCmd struct {
createCmd string // command to download a fresh copy of a repository
downloadCmd string // command to download updates into an existing repository
- tagCmd []tagCmd // commands to list tags
- tagDefault string // default tag to use
- tagSyncCmd string // command to sync to specific tag
+ tagCmd []tagCmd // commands to list tags
+ tagLookupCmd []tagCmd // commands to lookup tags before running tagSyncCmd
+ tagSyncCmd string // command to sync to specific tag
+ tagSyncDefault string // command to sync to default tag
+
+ scheme []string
+ pingCmd string
}
// A tagCmd describes a command to list available tags
@@ -71,8 +75,11 @@ var vcsHg = &vcsCmd{
{"tags", `^(\S+)`},
{"branches", `^(\S+)`},
},
- tagDefault: "default",
- tagSyncCmd: "update -r {tag}",
+ tagSyncCmd: "update -r {tag}",
+ tagSyncDefault: "update default",
+
+ scheme: []string{"https", "http"},
+ pingCmd: "identify {scheme}://{repo}",
}
// vcsGit describes how to use Git.
@@ -83,9 +90,19 @@ var vcsGit = &vcsCmd{
createCmd: "clone {repo} {dir}",
downloadCmd: "fetch",
- tagCmd: []tagCmd{{"tag", `^(\S+)$`}},
- tagDefault: "master",
- tagSyncCmd: "checkout {tag}",
+ tagCmd: []tagCmd{
+ // tags/xxx matches a git tag named xxx
+ // origin/xxx matches a git branch named xxx on the default remote repository
+ {"show-ref", `(?:tags|origin)/(\S+)$`},
+ },
+ tagLookupCmd: []tagCmd{
+ {"show-ref tags/{tag} origin/{tag}", `((?:tags|origin)/\S+)$`},
+ },
+ tagSyncCmd: "checkout {tag}",
+ tagSyncDefault: "checkout origin/master",
+
+ scheme: []string{"git", "https", "http"},
+ pingCmd: "ls-remote {scheme}://{repo}",
}
// vcsBzr describes how to use Bazaar.
@@ -99,9 +116,12 @@ var vcsBzr = &vcsCmd{
// Replace by --overwrite-tags after http://pad.lv/681792 goes in.
downloadCmd: "pull --overwrite",
- tagCmd: []tagCmd{{"tags", `^(\S+)`}},
- tagDefault: "revno:-1",
- tagSyncCmd: "update -r {tag}",
+ tagCmd: []tagCmd{{"tags", `^(\S+)`}},
+ tagSyncCmd: "update -r {tag}",
+ tagSyncDefault: "update -r revno:-1",
+
+ scheme: []string{"https", "http", "bzr"},
+ pingCmd: "info {scheme}://{repo}",
}
// vcsSvn describes how to use Subversion.
@@ -114,6 +134,9 @@ var vcsSvn = &vcsCmd{
// There is no tag command in subversion.
// The branch information is all in the path names.
+
+ scheme: []string{"https", "http", "svn"},
+ pingCmd: "info {scheme}://{repo}",
}
func (v *vcsCmd) String() string {
@@ -128,17 +151,23 @@ func (v *vcsCmd) String() string {
// command's combined stdout+stderr to standard error.
// Otherwise run discards the command's output.
func (v *vcsCmd) run(dir string, cmd string, keyval ...string) error {
- _, err := v.run1(dir, false, cmd, keyval)
+ _, err := v.run1(dir, cmd, keyval, true)
+ return err
+}
+
+// runVerboseOnly is like run but only generates error output to standard error in verbose mode.
+func (v *vcsCmd) runVerboseOnly(dir string, cmd string, keyval ...string) error {
+ _, err := v.run1(dir, cmd, keyval, false)
return err
}
// runOutput is like run but returns the output of the command.
func (v *vcsCmd) runOutput(dir string, cmd string, keyval ...string) ([]byte, error) {
- return v.run1(dir, true, cmd, keyval)
+ return v.run1(dir, cmd, keyval, true)
}
// run1 is the generalized implementation of run and runOutput.
-func (v *vcsCmd) run1(dir string, output bool, cmdline string, keyval []string) ([]byte, error) {
+func (v *vcsCmd) run1(dir string, cmdline string, keyval []string, verbose bool) ([]byte, error) {
m := make(map[string]string)
for i := 0; i < len(keyval); i += 2 {
m[keyval[i]] = keyval[i+1]
@@ -157,16 +186,23 @@ func (v *vcsCmd) run1(dir string, output bool, cmdline string, keyval []string)
var buf bytes.Buffer
cmd.Stdout = &buf
cmd.Stderr = &buf
- out := buf.Bytes()
err := cmd.Run()
+ out := buf.Bytes()
if err != nil {
- fmt.Fprintf(os.Stderr, "# cd %s; %s %s\n", dir, v.cmd, strings.Join(args, " "))
- os.Stderr.Write(out)
+ if verbose || buildV {
+ fmt.Fprintf(os.Stderr, "# cd %s; %s %s\n", dir, v.cmd, strings.Join(args, " "))
+ os.Stderr.Write(out)
+ }
return nil, err
}
return out, nil
}
+// ping pings to determine scheme to use.
+func (v *vcsCmd) ping(scheme, repo string) error {
+ return v.runVerboseOnly(".", v.pingCmd, "scheme", scheme, "repo", repo)
+}
+
// create creates a new copy of repo in dir.
// The parent of dir must exist; dir must not.
func (v *vcsCmd) create(dir, repo string) error {
@@ -187,7 +223,9 @@ func (v *vcsCmd) tags(dir string) ([]string, error) {
return nil, err
}
re := regexp.MustCompile(`(?m-s)` + tc.pattern)
- tags = append(tags, re.FindAllString(string(out), -1)...)
+ for _, m := range re.FindAllStringSubmatch(string(out), -1) {
+ tags = append(tags, m[1])
+ }
}
return tags, nil
}
@@ -198,6 +236,23 @@ func (v *vcsCmd) tagSync(dir, tag string) error {
if v.tagSyncCmd == "" {
return nil
}
+ if tag != "" {
+ for _, tc := range v.tagLookupCmd {
+ out, err := v.runOutput(dir, tc.cmd, "tag", tag)
+ if err != nil {
+ return err
+ }
+ re := regexp.MustCompile(`(?m-s)` + tc.pattern)
+ m := re.FindStringSubmatch(string(out))
+ if len(m) > 1 {
+ tag = m[1]
+ break
+ }
+ }
+ }
+ if tag == "" && v.tagSyncDefault != "" {
+ return v.run(dir, v.tagSyncDefault)
+ }
return v.run(dir, v.tagSyncCmd, "tag", tag)
}
@@ -209,6 +264,7 @@ type vcsPath struct {
repo string // repository to use (expand with match of re)
vcs string // version control system to use (expand with match of re)
check func(match map[string]string) error // additional checks
+ ping bool // ping for scheme to use to download repo
regexp *regexp.Regexp // cached compiled form of re
}
@@ -256,6 +312,14 @@ func vcsForImportPath(importPath string) (vcs *vcsCmd, repo, root string, err er
if vcs == nil {
return nil, "", "", fmt.Errorf("unknown version control system %q", match["vcs"])
}
+ if srv.ping {
+ for _, scheme := range vcs.scheme {
+ if vcs.ping(scheme, match["repo"]) == nil {
+ match["repo"] = scheme + "://" + match["repo"]
+ break
+ }
+ }
+ }
return vcs, match["repo"], match["root"], nil
}
return nil, "", "", fmt.Errorf("unrecognized import path %q", importPath)
@@ -313,7 +377,8 @@ var vcsPaths = []*vcsPath{
// General syntax for any server.
{
- re: `^(?P<root>(?P<repo>([a-z0-9.\-]+\.)+[a-z0-9.\-]+(:[0-9]+)?/[A-Za-z0-9_.\-/]*?)\.(?P<vcs>bzr|git|hg|svn))(/[A-Za-z0-9_.\-]+)*$`,
+ re: `^(?P<root>(?P<repo>([a-z0-9.\-]+\.)+[a-z0-9.\-]+(:[0-9]+)?/[A-Za-z0-9_.\-/]*?)\.(?P<vcs>bzr|git|hg|svn))(/[A-Za-z0-9_.\-]+)*$`,
+ ping: true,
},
}
diff --git a/src/cmd/go/vet.go b/src/cmd/go/vet.go
index 52c320032..51dcec2be 100644
--- a/src/cmd/go/vet.go
+++ b/src/cmd/go/vet.go
@@ -7,14 +7,14 @@ package main
var cmdVet = &Command{
Run: runVet,
UsageLine: "vet [importpath...]",
- Short: "run govet on packages",
+ Short: "run go tool vet on packages",
Long: `
-Vet runs the govet command on the packages named by the import paths.
+Vet runs the Go vet command on the packages named by the import paths.
-For more about govet, see 'godoc govet'.
+For more about vet, see 'godoc vet'.
For more about import paths, see 'go help importpath'.
-To run govet with specific options, run govet itself.
+To run the vet tool with specific options, run 'go tool vet'.
See also: go fmt, go fix.
`,
@@ -25,6 +25,6 @@ func runVet(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("govet", relPaths(pkg.gofiles))
+ run(tool("vet"), relPaths(pkg.gofiles))
}
}