diff options
author | Ondřej Surý <ondrej@sury.org> | 2012-01-30 15:38:19 +0100 |
---|---|---|
committer | Ondřej Surý <ondrej@sury.org> | 2012-01-30 15:38:19 +0100 |
commit | 4cecda6c347bd6902b960c6a35a967add7070b0d (patch) | |
tree | a462e224ff41ec9f3eb1a0b6e815806f9e8804ad /src/cmd/go | |
parent | 6c7ca6e4d4e26e4c8cbe0d183966011b3b088a0a (diff) | |
download | golang-4cecda6c347bd6902b960c6a35a967add7070b0d.tar.gz |
Imported Upstream version 2012.01.27upstream-weekly/2012.01.27
Diffstat (limited to 'src/cmd/go')
-rw-r--r-- | src/cmd/go/Makefile | 25 | ||||
-rw-r--r-- | src/cmd/go/bootstrap.go | 17 | ||||
-rw-r--r-- | src/cmd/go/build.go | 1142 | ||||
-rw-r--r-- | src/cmd/go/doc.go | 601 | ||||
-rw-r--r-- | src/cmd/go/fix.go | 30 | ||||
-rw-r--r-- | src/cmd/go/fmt.go | 54 | ||||
-rw-r--r-- | src/cmd/go/get.go | 269 | ||||
-rw-r--r-- | src/cmd/go/help.go | 185 | ||||
-rw-r--r-- | src/cmd/go/http.go | 35 | ||||
-rw-r--r-- | src/cmd/go/list.go | 114 | ||||
-rw-r--r-- | src/cmd/go/main.go | 485 | ||||
-rwxr-xr-x | src/cmd/go/mkdoc.sh | 8 | ||||
-rw-r--r-- | src/cmd/go/pkg.go | 445 | ||||
-rw-r--r-- | src/cmd/go/run.go | 57 | ||||
-rwxr-xr-x | src/cmd/go/script | 23 | ||||
-rw-r--r-- | src/cmd/go/script.txt | 352 | ||||
-rw-r--r-- | src/cmd/go/test.go | 681 | ||||
-rw-r--r-- | src/cmd/go/testflag.go | 215 | ||||
-rw-r--r-- | src/cmd/go/vcs.go | 425 | ||||
-rw-r--r-- | src/cmd/go/version.go | 25 | ||||
-rw-r--r-- | src/cmd/go/vet.go | 30 |
21 files changed, 5218 insertions, 0 deletions
diff --git a/src/cmd/go/Makefile b/src/cmd/go/Makefile new file mode 100644 index 000000000..295a14498 --- /dev/null +++ b/src/cmd/go/Makefile @@ -0,0 +1,25 @@ +# 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/bootstrap.go b/src/cmd/go/bootstrap.go new file mode 100644 index 000000000..bc9a3dbbc --- /dev/null +++ b/src/cmd/go/bootstrap.go @@ -0,0 +1,17 @@ +// 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 cmd_go_bootstrap + +// This code is compiled only into the bootstrap 'go' binary. +// These stubs avoid importing packages with large dependency +// trees, like the use of "net/http" in vcs.go. + +package main + +import "errors" + +func httpGET(url string) ([]byte, error) { + return nil, errors.New("no http in bootstrap go command") +} diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go new file mode 100644 index 000000000..cbe36f52e --- /dev/null +++ b/src/cmd/go/build.go @@ -0,0 +1,1142 @@ +// 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 ( + "bytes" + "container/heap" + "errors" + "fmt" + "go/build" + "io" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "regexp" + "runtime" + "strings" + "sync" +) + +var cmdBuild = &Command{ + UsageLine: "build [-a] [-n] [-o output] [-p n] [-v] [-x] [importpath... | gofiles...]", + Short: "compile packages and dependencies", + Long: ` +Build compiles the packages named by the import paths, +along with their dependencies, but it does not install the results. + +If the arguments are a list of .go files, build treats them as a list +of source files specifying a single package. + +When the command line specifies a single main package, +build writes the resulting executable to output (default a.out). +Otherwise build compiles the packages but discards the results, +serving only as a check that the packages can be built. + +The -a flag forces rebuilding of packages that are already up-to-date. +The -n flag prints the commands but does not run them. +The -v flag prints the names of packages as they are compiled. +The -x flag prints the commands. + +The -o flag specifies the output file name. +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. + +For more about import paths, see 'go help importpath'. + +See also: go install, go get, go clean. + `, +} + +func init() { + // break init cycle + cmdBuild.Run = runBuild + cmdInstall.Run = runInstall + + addBuildFlags(cmdBuild) + addBuildFlags(cmdInstall) +} + +// Flags set by multiple commands. +var buildA bool // -a flag +var buildN bool // -n flag +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 buildContext = build.DefaultContext + +// addBuildFlags adds the flags common to the build and install commands. +func addBuildFlags(cmd *Command) { + cmd.Flag.BoolVar(&buildA, "a", false, "") + cmd.Flag.BoolVar(&buildN, "n", false, "") + cmd.Flag.IntVar(&buildP, "p", buildP, "") + cmd.Flag.BoolVar(&buildV, "v", false, "") + cmd.Flag.BoolVar(&buildX, "x", false, "") + + // TODO(rsc): This -t flag is used by buildscript.sh but + // not documented. Should be documented but the + // usage lines are getting too long. Probably need to say + // that these flags are applicable to every command and + // document them in one help message instead of on every + // command's help message. + cmd.Flag.Var((*stringsFlag)(&buildContext.BuildTags), "t", "") +} + +type stringsFlag []string + +func (v *stringsFlag) Set(s string) error { + *v = append(*v, s) + return nil +} + +func (v *stringsFlag) String() string { + return "<stringsFlag>" +} + +func runBuild(cmd *Command, args []string) { + var b builder + b.init() + + var pkgs []*Package + if len(args) > 0 && strings.HasSuffix(args[0], ".go") { + pkg := goFilesPackage(args, "") + pkgs = append(pkgs, pkg) + } else { + pkgs = packagesForBuild(args) + } + + if len(pkgs) == 1 && pkgs[0].Name == "main" && *buildO == "" { + *buildO = "a.out" + } + + if *buildO != "" { + if len(pkgs) > 1 { + fatalf("go build: cannot use -o with multiple packages") + } + p := pkgs[0] + p.target = "" // must build - not up to date + a := b.action(modeInstall, modeBuild, p) + a.target = *buildO + b.do(a) + return + } + + a := &action{} + for _, p := range packages(args) { + a.deps = append(a.deps, b.action(modeBuild, modeBuild, p)) + } + b.do(a) +} + +var cmdInstall = &Command{ + UsageLine: "install [-a] [-n] [-p n] [-v] [-x] [importpath...]", + Short: "compile and install packages and dependencies", + Long: ` +Install compiles and installs the packages named by the import paths, +along with their dependencies. + +The -a flag forces reinstallation of packages that are already up-to-date. +The -n flag prints the commands but does not run them. +The -v flag prints the names of packages as they are compiled. +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. + +For more about import paths, see 'go help importpath'. + +See also: go build, go get, go clean. + `, +} + +func runInstall(cmd *Command, args []string) { + pkgs := packagesForBuild(args) + + var b builder + b.init() + a := &action{} + for _, p := range pkgs { + a.deps = append(a.deps, b.action(modeInstall, modeInstall, p)) + } + b.do(a) +} + +// A builder holds global state about a build. +// It does not hold per-package state, because eventually we will +// build packages in parallel, and the builder will be shared. +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 + + output sync.Mutex + scriptDir string // current directory in printed script + + exec sync.Mutex + readySema chan bool + ready actionQueue +} + +// An action represents a single action in the action graph. +type action struct { + p *Package // the package this action works on + deps []*action // actions that must happen before this one + triggers []*action // inverse of deps + cgo *action // action for cgo binary if needed + args []string // additional args for runProgram + testOutput *bytes.Buffer // test output buffer + + f func(*builder, *action) error // the action itself (nil = no-op) + ignoreFail bool // whether to run f even if dependencies fail + + // Generated files, directories. + link bool // target is executable, not just package + pkgdir string // the -I or -L argument to use when importing this package + objdir string // directory for intermediate objects + objpkg string // the intermediate package .a file created during the action + target string // goal of the action: the created package or executable + + // Execution state. + pending int // number of deps yet to complete + priority int // relative execution priority + failed bool // whether the action failed +} + +// cacheKey is the key for the action cache. +type cacheKey struct { + mode buildMode + p *Package +} + +// buildMode specifies the build mode: +// are we just building things or also installing the results? +type buildMode int + +const ( + modeBuild buildMode = iota + modeInstall +) + +func (b *builder) init() { + var err error + 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" + } + b.gcflags = strings.Fields(os.Getenv("GCFLAGS")) + + b.arch, err = build.ArchChar(b.goarch) + if err != nil { + fatalf("%s", err) + } + + if buildN { + b.work = "$WORK" + } else { + b.work, err = ioutil.TempDir("", "go-build") + if err != nil { + fatalf("%s", err) + } + if buildX { + fmt.Printf("WORK=%s\n", b.work) + } + atexit(func() { os.RemoveAll(b.work) }) + } +} + +// goFilesPackage creates a package for building a collection of Go files +// (typically named on the command line). If target is given, the package +// target is target. Otherwise, the target is named p.a for +// package p or named after the first Go file for package main. +func goFilesPackage(gofiles []string, target string) *Package { + // TODO: Remove this restriction. + for _, f := range gofiles { + if !strings.HasSuffix(f, ".go") || strings.Contains(f, "/") || strings.Contains(f, string(filepath.Separator)) { + fatalf("named files must be in current directory and .go files") + } + } + + // Synthesize fake "directory" that only shows those two files, + // to make it look like this is a standard package or + // command directory. + var dir []os.FileInfo + for _, file := range gofiles { + fi, err := os.Stat(file) + if err != nil { + fatalf("%s", err) + } + if fi.IsDir() { + fatalf("%s is a directory, should be a Go file", file) + } + dir = append(dir, fi) + } + ctxt := buildContext + 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) + if pkg.Error != nil { + fatalf("%s", pkg.Error) + } + if target != "" { + pkg.target = target + } else if pkg.Name == "main" { + pkg.target = gofiles[0][:len(gofiles[0])-len(".go")] + } else { + pkg.target = pkg.Name + ".a" + } + pkg.ImportPath = "_/" + pkg.target + return pkg +} + +// action returns the action for applying the given operation (mode) to the package. +// depMode is the action to use when building dependencies. +func (b *builder) action(mode buildMode, depMode buildMode, p *Package) *action { + key := cacheKey{mode, p} + a := b.actionCache[key] + if a != nil { + return a + } + + a = &action{p: p, pkgdir: p.t.PkgDir()} + if p.pkgdir != "" { // overrides p.t + a.pkgdir = p.pkgdir + } + + b.actionCache[key] = a + + for _, p1 := range p.imports { + 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) + } + a.cgo = b.action(depMode, depMode, p1) + a.deps = append(a.deps, a.cgo) + } + + if p.Standard { + switch p.ImportPath { + case "builtin", "unsafe": + // Fake packages - nothing to build. + return a + } + } + + if !p.Stale && !buildA && p.target != "" { + // p.Stale==false implies that p.target is up-to-date. + // Record target name for use by actions depending on this one. + a.target = p.target + return a + } + + 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.link = p.Name == "main" + + switch mode { + case modeInstall: + a.f = (*builder).install + a.deps = []*action{b.action(modeBuild, depMode, p)} + a.target = a.p.target + case modeBuild: + a.f = (*builder).build + 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 + } + } + + return a +} + +// actionList returns the list of actions in the dag rooted at root +// as visited in a depth-first post-order traversal. +func actionList(root *action) []*action { + seen := map[*action]bool{} + all := []*action{} + var walk func(*action) + walk = func(a *action) { + if seen[a] { + return + } + seen[a] = true + for _, a1 := range a.deps { + walk(a1) + } + all = append(all, a) + } + walk(root) + return all +} + +// do runs the action graph rooted at root. +func (b *builder) do(root *action) { + // Build list of all actions, assigning depth-first post-order priority. + // The original implementation here was a true queue + // (using a channel) but it had the effect of getting + // distracted by low-level leaf actions to the detriment + // of completing higher-level actions. The order of + // work does not matter much to overall execution time, + // but when running "go test std" it is nice to see each test + // results as soon as possible. The priorities assigned + // ensure that, all else being equal, the execution prefers + // to do what it would have done first in a simple depth-first + // dependency order traversal. + all := actionList(root) + for i, a := range all { + a.priority = i + } + + b.readySema = make(chan bool, len(all)) + done := make(chan bool) + + // Initialize per-action execution state. + for _, a := range all { + for _, a1 := range a.deps { + a1.triggers = append(a1.triggers, a) + } + a.pending = len(a.deps) + if a.pending == 0 { + b.ready.push(a) + b.readySema <- true + } + } + + // Handle runs a single action and takes care of triggering + // any actions that are runnable as a result. + handle := func(a *action) { + var err error + if a.f != nil && (!a.failed || a.ignoreFail) { + err = a.f(b, a) + } + + // The actions run in parallel but all the updates to the + // shared work state are serialized through b.exec. + b.exec.Lock() + defer b.exec.Unlock() + + if err != nil { + if err == errPrintedOutput { + exitStatus = 2 + } else { + errorf("%s", err) + } + a.failed = true + } + + for _, a0 := range a.triggers { + if a.failed { + a0.failed = true + } + if a0.pending--; a0.pending == 0 { + b.ready.push(a0) + b.readySema <- true + } + } + + if a == root { + close(b.readySema) + done <- true + } + } + + // 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 + // deterministic and because there is no real work anyway. + par := buildP + if buildN { + par = 1 + } + for i := 0; i < par; i++ { + 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) + } + }() + } + + <-done +} + +// build is the action for building a single package or command. +func (b *builder) build(a *action) error { + if buildN { + // In -n mode, print a banner between packages. + // The banner is five lines so that when changes to + // different sections of the bootstrap script have to + // be merged, the banners give patch something + // to use to find its context. + fmt.Printf("\n#\n# %s\n#\n\n", a.p.ImportPath) + } + + if buildV { + fmt.Fprintf(os.Stderr, "%s\n", a.p.ImportPath) + } + + // Make build directory. + obj := a.objdir + if err := b.mkdir(obj); err != nil { + return err + } + + var gofiles, cfiles, sfiles, objects, cgoObjects []string + gofiles = append(gofiles, a.p.GoFiles...) + cfiles = append(cfiles, a.p.CFiles...) + sfiles = append(sfiles, a.p.SFiles...) + + // Run cgo. + if len(a.p.CgoFiles) > 0 { + // In a package using cgo, cgo compiles the C and assembly files with gcc. + // There is one exception: runtime/cgo's job is to bridge the + // cgo and non-cgo worlds, so it necessarily has files in both. + // In that case gcc only gets the gcc_* files. + var gccfiles []string + if a.p.Standard && a.p.ImportPath == "runtime/cgo" { + filter := func(files, nongcc, gcc []string) ([]string, []string) { + for _, f := range files { + if strings.HasPrefix(f, "gcc_") { + gcc = append(gcc, f) + } else { + nongcc = append(nongcc, f) + } + } + return nongcc, gcc + } + cfiles, gccfiles = filter(cfiles, cfiles[:0], gccfiles) + sfiles, gccfiles = filter(sfiles, sfiles[:0], gccfiles) + } else { + gccfiles = append(cfiles, sfiles...) + cfiles = nil + sfiles = nil + } + + outGo, outObj, err := b.cgo(a.p, a.cgo.target, 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 { + 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 { + return err + } + objects = append(objects, out) + } + + // Copy .h files named for goos or goarch or goos_goarch + // to names using GOOS and GOARCH. + // For example, defs_linux_amd64.h becomes defs_GOOS_GOARCH.h. + _goos_goarch := "_" + b.goos + "_" + b.goarch + ".h" + _goos := "_" + b.goos + ".h" + _goarch := "_" + b.goarch + ".h" + for _, file := range a.p.HFiles { + switch { + case strings.HasSuffix(file, _goos_goarch): + targ := file[:len(file)-len(_goos_goarch)] + "_GOOS_GOARCH.h" + if err := b.copyFile(obj+targ, filepath.Join(a.p.Dir, file), 0666); err != nil { + return err + } + case strings.HasSuffix(file, _goarch): + targ := file[:len(file)-len(_goarch)] + "_GOARCH.h" + if err := b.copyFile(obj+targ, filepath.Join(a.p.Dir, file), 0666); err != nil { + return err + } + case strings.HasSuffix(file, _goos): + targ := file[:len(file)-len(_goos)] + "_GOOS.h" + if err := b.copyFile(obj+targ, filepath.Join(a.p.Dir, file), 0666); err != nil { + return err + } + } + } + + for _, file := range cfiles { + out := file[:len(file)-len(".c")] + "." + b.arch + if err := b.cc(a.p, obj, obj+out, file); err != nil { + return err + } + objects = append(objects, out) + } + + // 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 { + return err + } + objects = append(objects, out) + } + + // NOTE(rsc): On Windows, it is critically important that the + // gcc-compiled objects (cgoObjects) be listed after the ordinary + // objects in the archive. I do not know why this is. + // http://golang.org/issue/2601 + objects = append(objects, cgoObjects...) + + // Pack into archive in obj directory + if err := b.gopack(a.p, obj, a.objpkg, objects); err != nil { + return err + } + + // Link if needed. + if a.link { + // The compiler only cares about direct imports, but the + // 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 { + return err + } + } + + return nil +} + +// install is the action for installing a single package or executable. +func (b *builder) install(a *action) error { + a1 := a.deps[0] + perm := os.FileMode(0666) + if a1.link { + perm = 0777 + } + + // make target directory + dir, _ := filepath.Split(a.target) + if dir != "" { + if err := b.mkdir(dir); err != nil { + return err + } + } + + return b.copyFile(a.target, a1.target, perm) +} + +// includeArgs returns the -I or -L directory list for access +// to the results of the list of actions. +func (b *builder) includeArgs(flag string, all []*action) []string { + inc := []string{} + incMap := map[string]bool{ + b.work: true, // handled later + build.Path[0].PkgDir(): true, // goroot + "": true, // ignore empty strings + } + + // Look in the temporary space for results of test-specific actions. + // This is the $WORK/my/package/_test directory for the + // package being built, so there are few of these. + for _, a1 := range all { + if dir := a1.pkgdir; dir != a1.p.t.PkgDir() && !incMap[dir] { + incMap[dir] = true + inc = append(inc, flag, dir) + } + } + + // Also look in $WORK for any non-test packages that have + // been built but not installed. + inc = append(inc, flag, b.work) + + // 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] { + incMap[dir] = true + inc = append(inc, flag, dir) + } + } + + 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 { + b.showcmd("", "cp %s %s", src, dst) + if buildN { + return nil + } + } + + sf, err := os.Open(src) + if err != nil { + return err + } + defer sf.Close() + 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 + } + df, err = os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm) + if err != nil { + return err + } + } + _, err = io.Copy(df, sf) + df.Close() + if err != nil { + os.Remove(dst) + return err + } + return nil +} + +// 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 b.gobin with $GOBIN. +// +// fmtcmd replaces the name of the current directory with dot (.) +// but only when it is at the beginning of a space-separated token. +// +func (b *builder) fmtcmd(dir string, format string, args ...interface{}) string { + cmd := fmt.Sprintf(format, args...) + if dir != "" { + cmd = strings.Replace(" "+cmd, " "+dir, " .", -1)[1:] + if b.scriptDir != dir { + b.scriptDir = dir + 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) + return cmd +} + +// showcmd prints the given command to standard output +// for the implementation of -n or -x. +func (b *builder) showcmd(dir string, format string, args ...interface{}) { + b.output.Lock() + defer b.output.Unlock() + fmt.Println(b.fmtcmd(dir, format, args...)) +} + +// showOutput prints "# desc" followed by the given output. +// The output is expected to contain references to 'dir', usually +// the source directory for the package that has failed to build. +// showOutput rewrites mentions of dir with a relative path to dir +// when the relative path is shorter. This is usually more pleasant. +// For example, if fmt doesn't compile and we are in src/pkg/html, +// the output is +// +// $ go build +// # fmt +// ../fmt/print.go:1090: undefined: asdf +// $ +// +// instead of +// +// $ go build +// # fmt +// /usr/gopher/go/src/pkg/fmt/print.go:1090: undefined: asdf +// $ +// +// showOutput also replaces references to the work directory with $WORK. +// +func (b *builder) showOutput(dir, desc, out string) { + prefix := "# " + desc + suffix := "\n" + out + pwd, _ := os.Getwd() + if reldir, err := filepath.Rel(pwd, dir); err == nil && len(reldir) < len(dir) { + suffix = strings.Replace(suffix, " "+dir, " "+reldir, -1) + suffix = strings.Replace(suffix, "\n"+dir, "\n"+reldir, -1) + } + suffix = strings.Replace(suffix, " "+b.work, " $WORK", -1) + + b.output.Lock() + defer b.output.Unlock() + fmt.Print(prefix, suffix) +} + +// relPaths returns a copy of paths with absolute paths +// made relative to the current directory if they would be shorter. +func relPaths(paths []string) []string { + var out []string + pwd, _ := os.Getwd() + for _, p := range paths { + rel, err := filepath.Rel(pwd, p) + if err == nil && len(rel) < len(p) { + p = rel + } + out = append(out, p) + } + return out +} + +// errPrintedOutput is a special error indicating that a command failed +// but that it generated output as well, and that output has already +// been printed, so there's no point showing 'exit status 1' or whatever +// the wait status was. The main executor, builder.do, knows not to +// print this error. +var errPrintedOutput = errors.New("already printed output - no need to show error") + +// run runs the command given by cmdline in the directory dir. +// If the commnd 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)) + if err != nil { + err = errPrintedOutput + } + } + return err +} + +// 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) { + cmdline := stringList(cmdargs...) + if buildN || buildX { + b.showcmd(dir, "%s", strings.Join(cmdline, " ")) + if buildN { + return nil, nil + } + } + + var buf bytes.Buffer + cmd := exec.Command(cmdline[0], cmdline[1:]...) + cmd.Stdout = &buf + cmd.Stderr = &buf + cmd.Dir = dir + // TODO: cmd.Env + err := cmd.Run() + return buf.Bytes(), err +} + +// mkdir makes the named directory. +func (b *builder) mkdir(dir string) error { + b.exec.Lock() + defer b.exec.Unlock() + // We can be a little aggressive about being + // sure directories exist. Skip repeated calls. + if b.mkdirCache[dir] { + return nil + } + b.mkdirCache[dir] = true + + if buildN || buildX { + b.showcmd("", "mkdir -p %s", dir) + if buildN { + return nil + } + } + + if err := os.MkdirAll(dir, 0777); err != nil { + return err + } + return nil +} + +// mkAbs returns an absolute path corresponding to +// evaluating f in the directory dir. +// We always pass absolute paths of source files so that +// the error messages will include the full path to a file +// in need of attention. +func mkAbs(dir, f string) string { + // Leave absolute paths alone. + // Also, during -n mode we use the pseudo-directory $WORK + // instead of creating an actual work directory that won't be used. + // Leave paths beginning with $WORK alone too. + if filepath.IsAbs(f) || strings.HasPrefix(f, "$WORK") { + return f + } + 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) + for _, f := range gofiles { + args = append(args, mkAbs(p.Dir, f)) + } + return 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 { + 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) +} + +// 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 { + 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) +} + +// 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) +} + +// 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)) + cfile = mkAbs(p.Dir, cfile) + return b.run(p.Dir, p.ImportPath, b.arch+"c", "-FVw", + "-I", objdir, "-I", inc, "-o", ofile, + "-DGOOS_"+b.goos, "-DGOARCH_"+b.goarch, 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) + return b.run(p.Dir, p.ImportPath, b.gccCmd(p.Dir), flags, "-o", out, "-c", cfile) +} + +// gccld runs the gcc linker to create an executable from a set of object files +func (b *builder) gccld(p *Package, out string, flags []string, obj []string) error { + return b.run(p.Dir, p.ImportPath, b.gccCmd(p.Dir), "-o", out, obj, flags) +} + +// gccCmd returns a gcc command line prefix +func (b *builder) gccCmd(objdir string) []string { + // TODO: HOST_CC? + a := []string{"gcc", "-I", objdir, "-g", "-O2"} + + // Definitely want -fPIC but on Windows gcc complains + // "-fPIC ignored for target (all code is position independent)" + if b.goos != "windows" { + a = append(a, "-fPIC") + } + switch b.arch { + case "8": + a = append(a, "-m32") + case "6": + a = append(a, "-m64") + } + // gcc-4.5 and beyond require explicit "-pthread" flag + // for multithreading with pthread library. + if buildContext.CgoEnabled { + switch b.goos { + case "windows": + a = append(a, "-mthreads") + default: + a = append(a, "-pthread") + } + } + return a +} + +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 { + 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(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 + } + 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 + } + if len(out) > 0 { + cgoLDFLAGS = append(cgoLDFLAGS, strings.Fields(string(out))...) + } + } + + // cgo + // TODO: CGOPKGPATH, CGO_FLAGS? + gofiles := []string{obj + "_cgo_gotypes.go"} + cfiles := []string{"_cgo_main.c", "_cgo_export.c"} + for _, fn := range p.CgoFiles { + f := cgoRe.ReplaceAllString(fn[:len(fn)-2], "_") + gofiles = append(gofiles, obj+f+"cgo1.go") + cfiles = append(cfiles, f+"cgo2.c") + } + defunC := obj + "_cgo_defun.c" + // TODO: make cgo not depend on $GOARCH? + var runtimeFlag []string + if p.Standard && p.ImportPath == "runtime/cgo" { + runtimeFlag = []string{"-import_runtime_cgo=false"} + } + if err := b.run(p.Dir, p.ImportPath, cgoExe, "-objdir", obj, runtimeFlag, "--", 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 { + return nil, nil, err + } + outObj = append(outObj, defunObj) + + // gcc + var linkobj []string + for _, cfile := range cfiles { + ofile := obj + cfile[:len(cfile)-1] + "o" + if err := b.gcc(p, ofile, cgoCFLAGS, obj+cfile); err != nil { + return nil, nil, err + } + linkobj = append(linkobj, ofile) + if !strings.HasSuffix(ofile, "_cgo_main.o") { + outObj = append(outObj, ofile) + } + } + for _, file := range gccfiles { + ofile := obj + cgoRe.ReplaceAllString(file[:len(file)-1], "_") + "o" + if err := b.gcc(p, ofile, cgoCFLAGS, file); err != nil { + return nil, nil, err + } + linkobj = append(linkobj, ofile) + outObj = append(outObj, ofile) + } + dynobj := obj + "_cgo_.o" + if err := b.gccld(p, dynobj, cgoLDFLAGS, linkobj); err != nil { + return nil, nil, err + } + + // cgo -dynimport + importC := obj + "_cgo_import.c" + if err := b.run(p.Dir, p.ImportPath, cgoExe, "-objdir", obj, "-dynimport", dynobj, "-dynout", importC); err != nil { + return nil, nil, err + } + + // cc _cgo_import.ARCH + importObj := obj + "_cgo_import." + b.arch + if err := b.cc(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 + + return outGo, outObj, nil +} + +// An actionQueue is a priority queue of actions. +type actionQueue []*action + +// Implement heap.Interface +func (q *actionQueue) Len() int { return len(*q) } +func (q *actionQueue) Swap(i, j int) { (*q)[i], (*q)[j] = (*q)[j], (*q)[i] } +func (q *actionQueue) Less(i, j int) bool { return (*q)[i].priority < (*q)[j].priority } +func (q *actionQueue) Push(x interface{}) { *q = append(*q, x.(*action)) } +func (q *actionQueue) Pop() interface{} { + n := len(*q) - 1 + x := (*q)[n] + *q = (*q)[:n] + return x +} + +func (q *actionQueue) push(a *action) { + heap.Push(q, a) +} + +func (q *actionQueue) pop() *action { + return heap.Pop(q).(*action) +} diff --git a/src/cmd/go/doc.go b/src/cmd/go/doc.go new file mode 100644 index 000000000..27be32bf3 --- /dev/null +++ b/src/cmd/go/doc.go @@ -0,0 +1,601 @@ +// 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. + +/* +Go is a tool for managing Go source code. + +Usage: go command [arguments] + +The commands are: + + build compile packages and dependencies + doc run godoc on package sources + fix run gofix 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 + version print Go version + vet run govet on packages + +Use "go help [command]" for more information about a command. + +Additional help topics: + + gopath GOPATH environment variable + importpath description of import paths + remote remote import path syntax + testflag description of testing flags + testfunc description of testing functions + +Use "go help [topic]" for more information about that topic. + + +Compile packages and dependencies + +Usage: + + go build [-a] [-n] [-o output] [-p n] [-v] [-x] [importpath... | gofiles...] + +Build compiles the packages named by the import paths, +along with their dependencies, but it does not install the results. + +If the arguments are a list of .go files, build treats them as a list +of source files specifying a single package. + +When the command line specifies a single main package, +build writes the resulting executable to output (default a.out). +Otherwise build compiles the packages but discards the results, +serving only as a check that the packages can be built. + +The -a flag forces rebuilding of packages that are already up-to-date. +The -n flag prints the commands but does not run them. +The -v flag prints the names of packages as they are compiled. +The -x flag prints the commands. + +The -o flag specifies the output file name. +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. + +For more about import paths, see 'go help importpath'. + +See also: go install, go get, go clean. + + +Run godoc on package sources + +Usage: + + go doc [importpath...] + +Doc runs the godoc command on the packages named by the +import paths. + +For more about godoc, see 'godoc godoc'. +For more about import paths, see 'go help importpath'. + +To run godoc with specific options, run godoc itself. + +See also: go fix, go fmt, go vet. + + +Run gofix on packages + +Usage: + + go fix [importpath...] + +Fix runs the gofix command on the packages named by the import paths. + +For more about gofix, see 'godoc gofix'. +For more about import paths, see 'go help importpath'. + +To run gofix with specific options, run gofix itself. + +See also: go fmt, go vet. + + +Run gofmt on package sources + +Usage: + + go fmt [importpath...] + +Fmt runs the command 'gofmt -l -w' on the packages named +by the import paths. It prints the names of the files that are modified. + +For more about gofmt, see 'godoc gofmt'. +For more about import paths, see 'go help importpath'. + +To run gofmt with specific options, run gofmt itself. + +See also: go doc, go fix, go vet. + + +Download and install packages and dependencies + +Usage: + + go get [-a] [-d] [-fix] [-n] [-p n] [-u] [-v] [-x] [importpath...] + +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 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 +before resolving dependencies or building the code. + +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. + +TODO: Explain versions better. + +For more about import paths, see 'go help importpath'. + +For more about how 'go get' finds source code to +download, see 'go help remote'. + +See also: go build, go install, go clean. + + +Compile and install packages and dependencies + +Usage: + + go install [-a] [-n] [-p n] [-v] [-x] [importpath...] + +Install compiles and installs the packages named by the import paths, +along with their dependencies. + +The -a flag forces reinstallation of packages that are already up-to-date. +The -n flag prints the commands but does not run them. +The -v flag prints the names of packages as they are compiled. +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. + +For more about import paths, see 'go help importpath'. + +See also: go build, go get, go clean. + + +List packages + +Usage: + + go list [-e] [-f format] [-json] [importpath...] + +List lists the packages named by the import paths, one per line. + +The default output shows the package import path: + + code.google.com/p/google-api-go-client/books/v1 + 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: + + type Package struct { + Name string // package name + Doc string // package documentation string + ImportPath string // import path of package in dir + Dir string // directory containing package sources + Version string // version of installed package (TODO) + Stale bool // would 'go install' do anything for this package? + + // Source files + GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, and XTestGoFiles) + TestGoFiles []string // _test.go source files internal to the package they are testing + XTestGoFiles []string // _test.go source files external to the package they are testing + CFiles []string // .c source files + HFiles []string // .h source files + SFiles []string // .s source files + CgoFiles []string // .go sources files that import "C" + + // Dependency information + Imports []string // import paths used by this package + Deps []string // all (recursively) imported dependencies + + // Error information + Incomplete bool // this package or a dependency has an error + Error *PackageError // error loading package + DepsErrors []*PackageError // errors loading dependencies + } + +The -json flag causes the package data to be printed in JSON format +instead of using the template format. + +The -e flag changes the handling of erroneous packages, those that +cannot be found or are malformed. By default, the list command +prints an error to standard error for each erroneous package and +omits the packages from consideration during the usual printing. +With the -e flag, the list command never prints errors to standard +error and instead processes the erroneous packages with the usual +printing. Erroneous packages will have a non-empty ImportPath and +a non-nil Error field; other information may or may not be missing +(zeroed). + +For more about import paths, see 'go help importpath'. + + +Compile and run Go program + +Usage: + + go run [-a] [-n] [-x] gofiles... [arguments...] + +Run compiles and runs the main package comprising the named Go source files. + +The -a flag forces reinstallation of packages that are already up-to-date. +The -n flag prints the commands but does not run them. +The -x flag prints the commands. + +See also: go build. + + +Test packages + +Usage: + + go test [-c] [-file a.go -file b.go ...] [-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: + + ok archive/tar 0.011s + FAIL archive/zip 0.022s + ok compress/gzip 0.033s + ... + +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. + +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. + +See 'go help importpath' for more about import paths. + +See also: go build, go vet. + + +Print Go version + +Usage: + + go version + +Version prints the Go version, as reported by runtime.Version. + + +Run govet on packages + +Usage: + + go vet [importpath...] + +Vet runs the govet command on the packages named by the import paths. + +For more about govet, see 'godoc govet'. +For more about import paths, see 'go help importpath'. + +To run govet with specific options, run govet itself. + +See also: go fmt, go fix. + + +GOPATH environment variable + +The GOPATH environment variable lists places to look for Go code. +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 +standard Go tree. + +Each directory listed in GOPATH must have a prescribed structure: + +The src/ directory holds source code. The path below 'src' +determines the import path or executable name. + +The pkg/ directory holds installed package objects. +As in the Go tree, each target operating system and +architecture pair has its own subdirectory of pkg +(pkg/GOOS_GOARCH). + +If DIR is a directory listed in the GOPATH, a package with +source in DIR/src/foo/bar can be imported as "foo/bar" and +has its compiled form installed to "DIR/pkg/GOOS_GOARCH/foo/bar.a". + +The bin/ directory holds compiled commands. +Each command is named for its source directory, but only +the final element, not the entire path. That is, the +command with source in DIR/src/foo/quux is installed into +DIR/bin/quux, not DIR/bin/foo/quux. The foo/ is stripped +so that you can add DIR/bin to your PATH to get at the +installed commands. + +Here's an example directory layout: + + GOPATH=/home/user/gocode + + /home/user/gocode/ + src/ + foo/ + bar/ (go code in package bar) + x.go + quux/ (go code in package main) + y.go + bin/ + quux (installed command) + pkg/ + linux_amd64/ + foo/ + 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 +in the list. + + +Description of import paths + +Many commands apply to a set of packages named by import paths: + + go action [importpath...] + +An import path that is a rooted path or that begins with +a . or .. element is interpreted as a file system path and +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'). + +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' +lists all the packages on the local system. + +The special import path "std" is like all but expands to just the +packages in the standard Go library. + +An import path is a pattern if it includes one or more "..." wildcards, +each of which can match any string, including the empty string and +strings containing slashes. Such a pattern expands to all package +directories found in the GOPATH trees with names matching the +patterns. For example, encoding/... expands to all packages +in the encoding tree. + +An import path can also name a package to be downloaded from +a remote repository. Run 'go help remote' for details. + +Every package in a program must have a unique import path. +By convention, this is arranged by starting each path with a +unique prefix that belongs to you. For example, paths used +internally at Google all begin with 'google', and paths +denoting remote repositories begin with the path to the code, +such as 'code.google.com/p/project'. + + +Remote import path syntax + +An import path (see 'go help importpath') denotes a package +stored in the local file system. Certain import paths also +describe how to obtain the source code for the package using +a revision control system. + +A few common code hosting sites have special syntax: + + BitBucket (Mercurial) + + import "bitbucket.org/user/project" + import "bitbucket.org/user/project/sub/directory" + + GitHub (Git) + + import "github.com/user/project" + import "github.com/user/project/sub/directory" + + Google Code Project Hosting (Git, Mercurial, Subversion) + + import "code.google.com/p/project" + import "code.google.com/p/project/sub/directory" + + import "code.google.com/p/project.subrepository" + import "code.google.com/p/project.subrepository/sub/directory" + + Launchpad (Bazaar) + + import "launchpad.net/project" + import "launchpad.net/project/series" + import "launchpad.net/project/series/sub/directory" + + import "launchpad.net/~user/project/branch" + import "launchpad.net/~user/project/branch/sub/directory" + +For code hosted on other servers, an import path of the form + + repository.vcs/path + +specifies the given repository, with or without the .vcs suffix, +using the named version control system, and then the path inside +that repository. The supported version control systems are: + + Bazaar .bzr + Git .git + Mercurial .hg + Subversion .svn + +For example, + + import "example.org/user/foo.hg" + +denotes the root directory of the Mercurial repository at +example.org/user/foo or foo.hg, and + + import "example.org/repo.git/foo/bar" + +denotes the foo/bar directory of the Git repository at +example.com/repo or repo.git. + +When a version control system supports multiple protocols, +each is tried in turn when downloading. For example, a Git +download tries git://, then https://, then http://. + +New downloaded packages are written to the first directory +listed in the GOPATH environment variable (see 'go help gopath'). + +The go command attempts to download the version of the +package appropriate for the Go release being used. +Run 'go help install' for more. + + +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 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: + + -test.v + Verbose output: log all tests as they are run. + + -test.run pattern + Run only those tests matching the regular expression. + + -test.bench pattern + Run benchmarks matching the regular expression. + By default, no benchmarks run. + + -test.cpuprofile cpu.out + Write a CPU profile to the specified file before exiting. + + -test.memprofile mem.out + Write a memory profile to the specified file when all tests + are complete. + + -test.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 + and set the environment variable GOGC=off to disable the + garbage collector, provided the test can run in the available + memory without garbage collection. + + -test.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 + 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 + 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. + + -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. + +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 + +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 + + +Description of testing functions + +The 'go test' command expects to find test, benchmark, and example functions +in the "*_test.go" files corresponding to the package under test. + +A test function is one named TestXXX (where XXX is any alphanumeric string +not starting with a lower case letter) and should have the signature, + + func TestXXX(t *testing.T) { ... } + +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. +That output is compared against the function's doc comment. +An example without a doc comment is compiled but not executed. + +Godoc displays the body of ExampleXXX to demonstrate the use +of the function, constant, or variable XXX. An example of a method M with +receiver type T or *T is named ExampleT_M. There may be multiple examples +for a given function, constant, or variable, distinguished by a trailing _xxx, +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.") + } + +See the documentation of the testing package for more information. + + +*/ +package documentation + +// NOTE: cmdDoc is in fmt.go. diff --git a/src/cmd/go/fix.go b/src/cmd/go/fix.go new file mode 100644 index 000000000..bae9f5c98 --- /dev/null +++ b/src/cmd/go/fix.go @@ -0,0 +1,30 @@ +// 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 + +var cmdFix = &Command{ + Run: runFix, + UsageLine: "fix [importpath...]", + Short: "run gofix on packages", + Long: ` +Fix runs the gofix command on the packages named by the import paths. + +For more about gofix, see 'godoc gofix'. +For more about import paths, see 'go help importpath'. + +To run gofix with specific options, run gofix itself. + +See also: go fmt, go vet. + `, +} + +func runFix(cmd *Command, args []string) { + for _, pkg := range packages(args) { + // 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))) + } +} diff --git a/src/cmd/go/fmt.go b/src/cmd/go/fmt.go new file mode 100644 index 000000000..4a47e2ea2 --- /dev/null +++ b/src/cmd/go/fmt.go @@ -0,0 +1,54 @@ +// 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 + +var cmdFmt = &Command{ + Run: runFmt, + UsageLine: "fmt [importpath...]", + Short: "run gofmt on package sources", + Long: ` +Fmt runs the command 'gofmt -l -w' on the packages named +by the import paths. It prints the names of the files that are modified. + +For more about gofmt, see 'godoc gofmt'. +For more about import paths, see 'go help importpath'. + +To run gofmt with specific options, run gofmt itself. + +See also: go doc, go fix, go vet. + `, +} + +func runFmt(cmd *Command, args []string) { + for _, pkg := range packages(args) { + // 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))) + } +} + +var cmdDoc = &Command{ + Run: runDoc, + UsageLine: "doc [importpath...]", + Short: "run godoc on package sources", + Long: ` +Doc runs the godoc command on the packages named by the +import paths. + +For more about godoc, see 'godoc godoc'. +For more about import paths, see 'go help importpath'. + +To run godoc with specific options, run godoc itself. + +See also: go fix, go fmt, go vet. + `, +} + +func runDoc(cmd *Command, args []string) { + for _, pkg := range packages(args) { + run("godoc", pkg.Dir) + } +} diff --git a/src/cmd/go/get.go b/src/cmd/go/get.go new file mode 100644 index 000000000..cd57d3025 --- /dev/null +++ b/src/cmd/go/get.go @@ -0,0 +1,269 @@ +// 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. + +// TODO: Dashboard upload + +package main + +import ( + "fmt" + "go/build" + "os" + "path/filepath" + "runtime" + "strconv" + "strings" +) + +var cmdGet = &Command{ + UsageLine: "get [-a] [-d] [-fix] [-n] [-p n] [-u] [-v] [-x] [importpath...]", + 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 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 +before resolving dependencies or building the code. + +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. + +TODO: Explain versions better. + +For more about import paths, see 'go help importpath'. + +For more about how 'go get' finds source code to +download, see 'go help remote'. + +See also: go build, go install, go clean. + `, +} + +var getD = cmdGet.Flag.Bool("d", false, "") +var getU = cmdGet.Flag.Bool("u", false, "") +var getFix = cmdGet.Flag.Bool("fix", false, "") + +func init() { + addBuildFlags(cmdGet) + cmdGet.Run = runGet // break init loop +} + +func runGet(cmd *Command, args []string) { + // Phase 1. Download/update. + args = importPaths(args) + var stk importStack + for _, arg := range args { + download(arg, &stk) + } + exitIfErrors() + + if *getD { + // download only + return + } + + // Phase 2. Install. + + // Code we downloaded and all code that depends on it + // needs to be evicted from the package cache so that + // the information will be recomputed. Instead of keeping + // track of the reverse dependency information, evict + // everything. + for name := range packageCache { + delete(packageCache, name) + } + + runInstall(cmd, args) +} + +// downloadCache records the import paths we have already +// considered during the download, to avoid duplicate work when +// there is more than one dependency sequence leading to +// a particular package. +var downloadCache = map[string]bool{} + +// downloadRootCache records the version control repository +// root directories we have already considered during the download. +// For example, all the packages in the code.google.com/p/codesearch repo +// share the same root (the directory for that path), and we only need +// to run the hg commands to consider each repository once. +var downloadRootCache = map[string]bool{} + +// download runs the download half of the get command +// for the package named by the argument. +func download(arg string, stk *importStack) { + p := loadPackage(arg, stk) + + // There's nothing to do if this is a package in the standard library. + if p.Standard { + return + } + + // Only process each package once. + if downloadCache[arg] { + return + } + downloadCache[arg] = true + + // Download if the package is missing, or update if we're using -u. + if p.Dir == "" || *getU { + // The actual download. + stk.push(p.ImportPath) + defer stk.pop() + if err := downloadPackage(p); err != nil { + errorf("%s", &PackageError{stk.copy(), err.Error()}) + return + } + + // Reread the package information from the updated files. + p = reloadPackage(arg, stk) + if p.Error != nil { + errorf("%s", p.Error) + return + } + } + + if *getFix { + run(stringList("gofix", relPaths(p.gofiles))) + + // The imports might have changed, so reload again. + p = reloadPackage(arg, stk) + if p.Error != nil { + errorf("%s", p.Error) + return + } + } + + // Process dependencies, now that we know what they are. + for _, dep := range p.deps { + download(dep.ImportPath, stk) + } +} + +// downloadPackage runs the create or download command +// to make the first copy of or update a copy of the given package. +func downloadPackage(p *Package) error { + // Analyze the import path to determine the version control system, + // repository, and the import path for the root of the repository. + vcs, repo, rootPath, err := vcsForImportPath(p.ImportPath) + if err != nil { + return err + } + if p.t == nil { + // Package not found. Put in first directory of $GOPATH or else $GOROOT. + p.t = build.Path[0] // $GOROOT + if len(build.Path) > 1 { + p.t = build.Path[1] // first in $GOPATH + } + p.Dir = filepath.Join(p.t.SrcDir(), p.ImportPath) + } + root := filepath.Join(p.t.SrcDir(), rootPath) + + // If we've considered this repository already, don't do it again. + if downloadRootCache[root] { + return nil + } + downloadRootCache[root] = true + + if buildV { + fmt.Fprintf(os.Stderr, "%s (download)\n", rootPath) + } + + // Check that this is an appropriate place for the repo to be checked out. + // The target directory must either not exist or have a repo checked out already. + meta := filepath.Join(root, "."+vcs.cmd) + st, err := os.Stat(meta) + if err == nil && !st.IsDir() { + return fmt.Errorf("%s exists but is not a directory", meta) + } + if err != nil { + // Metadata directory does not exist. Prepare to checkout new copy. + // Some version control tools require the target directory not to exist. + // We require that too, just to avoid stepping on existing work. + if _, err := os.Stat(root); err == nil { + return fmt.Errorf("%s exists but %s does not - stale checkout?", root, meta) + } + // Some version control tools require the parent of the target to exist. + parent, _ := filepath.Split(root) + if err := os.MkdirAll(parent, 0777); err != nil { + return err + } + if err = vcs.create(root, repo); err != nil { + return err + } + } else { + // Metadata directory does exist; download incremental updates. + if err = vcs.download(root); err != nil { + return err + } + } + + // Select and sync to appropriate version of the repository. + tags, err := vcs.tags(root) + if err != nil { + return err + } + vers := runtime.Version() + 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 { + return err + } + + return nil +} + +// selectTag returns the closest matching tag for a given version. +// Closest means the latest one that is not after the current release. +// Version "release.rN" matches tags of the form "go.rN" (N being a decimal). +// Version "weekly.YYYY-MM-DD" matches tags like "go.weekly.YYYY-MM-DD". +func selectTag(goVersion string, tags []string) (match string) { + const rPrefix = "release.r" + if strings.HasPrefix(goVersion, rPrefix) { + p := "go.r" + v, err := strconv.ParseFloat(goVersion[len(rPrefix):], 64) + if err != nil { + return "" + } + var matchf float64 + for _, t := range tags { + if !strings.HasPrefix(t, p) { + continue + } + tf, err := strconv.ParseFloat(t[len(p):], 64) + if err != nil { + continue + } + if matchf < tf && tf <= v { + match, matchf = t, tf + } + } + } + const wPrefix = "weekly." + if strings.HasPrefix(goVersion, wPrefix) { + p := "go.weekly." + v := goVersion[len(wPrefix):] + for _, t := range tags { + if !strings.HasPrefix(t, p) { + continue + } + if match < t && t[len(p):] <= v { + match = t + } + } + } + return match +} diff --git a/src/cmd/go/help.go b/src/cmd/go/help.go new file mode 100644 index 000000000..33716eff9 --- /dev/null +++ b/src/cmd/go/help.go @@ -0,0 +1,185 @@ +// 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 + +var helpImportpath = &Command{ + UsageLine: "importpath", + Short: "description of import paths", + Long: ` +Many commands apply to a set of packages named by import paths: + + go action [importpath...] + +An import path that is a rooted path or that begins with +a . or .. element is interpreted as a file system path and +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'). + +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' +lists all the packages on the local system. + +The special import path "std" is like all but expands to just the +packages in the standard Go library. + +An import path is a pattern if it includes one or more "..." wildcards, +each of which can match any string, including the empty string and +strings containing slashes. Such a pattern expands to all package +directories found in the GOPATH trees with names matching the +patterns. For example, encoding/... expands to all packages +in the encoding tree. + +An import path can also name a package to be downloaded from +a remote repository. Run 'go help remote' for details. + +Every package in a program must have a unique import path. +By convention, this is arranged by starting each path with a +unique prefix that belongs to you. For example, paths used +internally at Google all begin with 'google', and paths +denoting remote repositories begin with the path to the code, +such as 'code.google.com/p/project'. + `, +} + +var helpRemote = &Command{ + UsageLine: "remote", + Short: "remote import path syntax", + Long: ` + +An import path (see 'go help importpath') denotes a package +stored in the local file system. Certain import paths also +describe how to obtain the source code for the package using +a revision control system. + +A few common code hosting sites have special syntax: + + BitBucket (Mercurial) + + import "bitbucket.org/user/project" + import "bitbucket.org/user/project/sub/directory" + + GitHub (Git) + + import "github.com/user/project" + import "github.com/user/project/sub/directory" + + Google Code Project Hosting (Git, Mercurial, Subversion) + + import "code.google.com/p/project" + import "code.google.com/p/project/sub/directory" + + import "code.google.com/p/project.subrepository" + import "code.google.com/p/project.subrepository/sub/directory" + + Launchpad (Bazaar) + + import "launchpad.net/project" + import "launchpad.net/project/series" + import "launchpad.net/project/series/sub/directory" + + import "launchpad.net/~user/project/branch" + import "launchpad.net/~user/project/branch/sub/directory" + +For code hosted on other servers, an import path of the form + + repository.vcs/path + +specifies the given repository, with or without the .vcs suffix, +using the named version control system, and then the path inside +that repository. The supported version control systems are: + + Bazaar .bzr + Git .git + Mercurial .hg + Subversion .svn + +For example, + + import "example.org/user/foo.hg" + +denotes the root directory of the Mercurial repository at +example.org/user/foo or foo.hg, and + + import "example.org/repo.git/foo/bar" + +denotes the foo/bar directory of the Git repository at +example.com/repo or repo.git. + +When a version control system supports multiple protocols, +each is tried in turn when downloading. For example, a Git +download tries git://, then https://, then http://. + +New downloaded packages are written to the first directory +listed in the GOPATH environment variable (see 'go help gopath'). + +The go command attempts to download the version of the +package appropriate for the Go release being used. +Run 'go help install' for more. + `, +} + +var helpGopath = &Command{ + UsageLine: "gopath", + Short: "GOPATH environment variable", + Long: ` +The GOPATH environment variable lists places to look for Go code. +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 +standard Go tree. + +Each directory listed in GOPATH must have a prescribed structure: + +The src/ directory holds source code. The path below 'src' +determines the import path or executable name. + +The pkg/ directory holds installed package objects. +As in the Go tree, each target operating system and +architecture pair has its own subdirectory of pkg +(pkg/GOOS_GOARCH). + +If DIR is a directory listed in the GOPATH, a package with +source in DIR/src/foo/bar can be imported as "foo/bar" and +has its compiled form installed to "DIR/pkg/GOOS_GOARCH/foo/bar.a". + +The bin/ directory holds compiled commands. +Each command is named for its source directory, but only +the final element, not the entire path. That is, the +command with source in DIR/src/foo/quux is installed into +DIR/bin/quux, not DIR/bin/foo/quux. The foo/ is stripped +so that you can add DIR/bin to your PATH to get at the +installed commands. + +Here's an example directory layout: + + GOPATH=/home/user/gocode + + /home/user/gocode/ + src/ + foo/ + bar/ (go code in package bar) + x.go + quux/ (go code in package main) + y.go + bin/ + quux (installed command) + pkg/ + linux_amd64/ + foo/ + 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 +in the list. + `, +} diff --git a/src/cmd/go/http.go b/src/cmd/go/http.go new file mode 100644 index 000000000..8d9b2a165 --- /dev/null +++ b/src/cmd/go/http.go @@ -0,0 +1,35 @@ +// 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 !cmd_go_bootstrap + +// This code is compiled into the real 'go' binary, but it is not +// compiled into the binary that is built during all.bash, so as +// to avoid needing to build net (and thus use cgo) during the +// bootstrap process. + +package main + +import ( + "fmt" + "io/ioutil" + "net/http" +) + +// httpGET returns the data from an HTTP GET request for the given URL. +func httpGET(url string) ([]byte, error) { + resp, err := http.Get(url) + if err != nil { + return nil, err + } + defer resp.Body.Close() + if resp.StatusCode != 200 { + return nil, fmt.Errorf("%s: %s", url, resp.Status) + } + b, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("%s: %v", url, err) + } + return b, nil +} diff --git a/src/cmd/go/list.go b/src/cmd/go/list.go new file mode 100644 index 000000000..af211f98d --- /dev/null +++ b/src/cmd/go/list.go @@ -0,0 +1,114 @@ +// 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 ( + "encoding/json" + "os" + "text/template" +) + +var cmdList = &Command{ + UsageLine: "list [-e] [-f format] [-json] [importpath...]", + Short: "list packages", + Long: ` +List lists the packages named by the import paths, one per line. + +The default output shows the package import path: + + code.google.com/p/google-api-go-client/books/v1 + 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: + + type Package struct { + Name string // package name + Doc string // package documentation string + ImportPath string // import path of package in dir + Dir string // directory containing package sources + Version string // version of installed package (TODO) + Stale bool // would 'go install' do anything for this package? + + // Source files + GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, and XTestGoFiles) + TestGoFiles []string // _test.go source files internal to the package they are testing + XTestGoFiles []string // _test.go source files external to the package they are testing + CFiles []string // .c source files + HFiles []string // .h source files + SFiles []string // .s source files + CgoFiles []string // .go sources files that import "C" + + // Dependency information + Imports []string // import paths used by this package + Deps []string // all (recursively) imported dependencies + + // Error information + Incomplete bool // this package or a dependency has an error + Error *PackageError // error loading package + DepsErrors []*PackageError // errors loading dependencies + } + +The -json flag causes the package data to be printed in JSON format +instead of using the template format. + +The -e flag changes the handling of erroneous packages, those that +cannot be found or are malformed. By default, the list command +prints an error to standard error for each erroneous package and +omits the packages from consideration during the usual printing. +With the -e flag, the list command never prints errors to standard +error and instead processes the erroneous packages with the usual +printing. Erroneous packages will have a non-empty ImportPath and +a non-nil Error field; other information may or may not be missing +(zeroed). + +For more about import paths, see 'go help importpath'. + `, +} + +func init() { + cmdList.Run = runList // break init cycle +} + +var listE = cmdList.Flag.Bool("e", false, "") +var listFmt = cmdList.Flag.String("f", "{{.ImportPath}}", "") +var listJson = cmdList.Flag.Bool("json", false, "") +var nl = []byte{'\n'} + +func runList(cmd *Command, args []string) { + var do func(*Package) + if *listJson { + do = func(p *Package) { + b, err := json.MarshalIndent(p, "", "\t") + if err != nil { + fatalf("%s", err) + } + os.Stdout.Write(b) + os.Stdout.Write(nl) + } + } else { + tmpl, err := template.New("main").Parse(*listFmt + "\n") + if err != nil { + fatalf("%s", err) + } + do = func(p *Package) { + if err := tmpl.Execute(os.Stdout, p); err != nil { + fatalf("%s", err) + } + } + } + + load := packages + if *listE { + load = packagesAndErrors + } + + for _, pkg := range load(args) { + do(pkg) + } +} diff --git a/src/cmd/go/main.go b/src/cmd/go/main.go new file mode 100644 index 000000000..ca3b1188a --- /dev/null +++ b/src/cmd/go/main.go @@ -0,0 +1,485 @@ +// 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 ( + "bytes" + "flag" + "fmt" + "go/build" + "io" + "log" + "os" + "os/exec" + "path" + "path/filepath" + "regexp" + "strings" + "text/template" + "unicode" + "unicode/utf8" +) + +// A Command is an implementation of a go command +// like go build or go fix. +type Command struct { + // Run runs the command. + // The args are the arguments after the command name. + Run func(cmd *Command, args []string) + + // UsageLine is the one-line usage message. + // The first word in the line is taken to be the command name. + UsageLine string + + // Short is the short description shown in the 'go help' output. + Short string + + // Long is the long message shown in the 'go help <this-command>' output. + Long string + + // Flag is a set of flags specific to this command. + Flag flag.FlagSet + + // CustomFlags indicates that the command will do its own + // flag parsing. + CustomFlags bool +} + +// Name returns the command's name: the first word in the usage line. +func (c *Command) Name() string { + name := c.UsageLine + i := strings.Index(name, " ") + if i >= 0 { + name = name[:i] + } + return name +} + +func (c *Command) Usage() { + fmt.Fprintf(os.Stderr, "usage: %s\n\n", c.UsageLine) + fmt.Fprintf(os.Stderr, "%s\n", strings.TrimSpace(c.Long)) + os.Exit(2) +} + +// 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, + cmdDoc, + cmdFix, + cmdFmt, + cmdGet, + cmdInstall, + cmdList, + cmdRun, + cmdTest, + cmdVersion, + cmdVet, + + helpGopath, + helpImportpath, + helpRemote, + helpTestflag, + helpTestfunc, +} + +var exitStatus = 0 + +func main() { + flag.Usage = usage + flag.Parse() + log.SetFlags(0) + + args := flag.Args() + if len(args) < 1 { + usage() + } + + if args[0] == "help" { + help(args[1:]) + return + } + + for _, cmd := range commands { + if cmd.Name() == args[0] && cmd.Run != nil { + cmd.Flag.Usage = func() { cmd.Usage() } + if cmd.CustomFlags { + args = args[1:] + } else { + cmd.Flag.Parse(args[1:]) + args = cmd.Flag.Args() + } + cmd.Run(cmd, args) + exit() + return + } + } + + fmt.Fprintf(os.Stderr, "Unknown command %#q\n\n", args[0]) + usage() +} + +var usageTemplate = `Go is a tool for managing Go source code. + +Usage: go command [arguments] + +The commands are: +{{range .}}{{if .Run}} + {{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}} + +Use "go help [command]" for more information about a command. + +Additional help topics: +{{range .}}{{if not .Run}} + {{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}} + +Use "go help [topic]" for more information about that topic. + +` + +var helpTemplate = `{{if .Run}}usage: go {{.UsageLine}} + +{{end}}{{.Long | trim}} +` + +var documentationTemplate = `// 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. + +/* +{{range .}}{{if .Short}}{{.Short | capitalize}} + +{{end}}{{if .Run}}Usage: + + go {{.UsageLine}} + +{{end}}{{.Long | trim}} + + +{{end}}*/ +package documentation + +// NOTE: cmdDoc is in fmt.go. +` + +// tmpl executes the given template text on data, writing the result to w. +func tmpl(w io.Writer, text string, data interface{}) { + t := template.New("top") + t.Funcs(template.FuncMap{"trim": strings.TrimSpace, "capitalize": capitalize}) + template.Must(t.Parse(text)) + if err := t.Execute(w, data); err != nil { + panic(err) + } +} + +func capitalize(s string) string { + if s == "" { + return s + } + r, n := utf8.DecodeRuneInString(s) + return string(unicode.ToTitle(r)) + s[n:] +} + +func printUsage(w io.Writer) { + tmpl(w, usageTemplate, commands) +} + +func usage() { + printUsage(os.Stderr) + os.Exit(2) +} + +// help implements the 'help' command. +func help(args []string) { + if len(args) == 0 { + printUsage(os.Stdout) + // not exit 2: succeeded at 'go help'. + return + } + if len(args) != 1 { + fmt.Fprintf(os.Stderr, "usage: go help command\n\nToo many arguments given.\n") + os.Exit(2) // failed at 'go help' + } + + arg := args[0] + + // 'go help documentation' generates doc.go. + if arg == "documentation" { + buf := new(bytes.Buffer) + printUsage(buf) + usage := &Command{Long: buf.String()} + tmpl(os.Stdout, documentationTemplate, append([]*Command{usage}, commands...)) + return + } + + for _, cmd := range commands { + if cmd.Name() == arg { + tmpl(os.Stdout, helpTemplate, cmd) + // not exit 2: succeeded at 'go help cmd'. + return + } + } + + fmt.Fprintf(os.Stderr, "Unknown help topic %#q. Run 'go help'.\n", arg) + os.Exit(2) // failed at 'go help cmd' +} + +// importPaths returns the import paths to use for the given command line. +func importPaths(args []string) []string { + if len(args) == 0 { + return []string{"."} + } + var out []string + for _, a := range args { + if isLocalPath(a) && strings.Contains(a, "...") { + out = append(out, allPackagesInFS(a)...) + continue + } + if a == "all" || a == "std" || strings.Contains(a, "...") { + out = append(out, allPackages(a)...) + continue + } + out = append(out, a) + } + return out +} + +var atexitFuncs []func() + +func atexit(f func()) { + atexitFuncs = append(atexitFuncs, f) +} + +func exit() { + for _, f := range atexitFuncs { + f() + } + os.Exit(exitStatus) +} + +func fatalf(format string, args ...interface{}) { + errorf(format, args...) + exit() +} + +func errorf(format string, args ...interface{}) { + log.Printf(format, args...) + exitStatus = 1 +} + +var logf = log.Printf + +func exitIfErrors() { + if exitStatus != 0 { + exit() + } +} + +func run(cmdargs ...interface{}) { + cmdline := stringList(cmdargs...) + cmd := exec.Command(cmdline[0], cmdline[1:]...) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + errorf("%v", err) + } +} + +func runOut(dir string, cmdargs ...interface{}) []byte { + cmdline := stringList(cmdargs...) + cmd := exec.Command(cmdline[0], cmdline[1:]...) + cmd.Dir = dir + out, err := cmd.CombinedOutput() + if err != nil { + os.Stderr.Write(out) + errorf("%v", err) + out = nil + } + return out +} + +// matchPattern(pattern)(name) reports whether +// name matches pattern. Pattern is a limited glob +// pattern in which '...' means 'any string' and there +// is no other special syntax. +func matchPattern(pattern string) func(name string) bool { + re := regexp.QuoteMeta(pattern) + re = strings.Replace(re, `\.\.\.`, `.*`, -1) + reg := regexp.MustCompile(`^` + re + `$`) + return func(name string) bool { + return reg.MatchString(name) + } +} + +// allPackages returns all the packages that can be found +// under the $GOPATH directories and $GOROOT matching pattern. +// The pattern is either "all" (all packages), "std" (standard packages) +// or a path including "...". +func allPackages(pattern string) []string { + match := func(string) bool { return true } + if pattern != "all" && pattern != "std" { + match = matchPattern(pattern) + } + + have := map[string]bool{ + "builtin": true, // ignore pseudo-package that exists only for documentation + } + if !buildContext.CgoEnabled { + have["runtime/cgo"] = true // ignore during walk + } + var pkgs []string + + // Commands + 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() { + return nil + } + name := path[len(cmd):] + // Commands are all in cmd/, not in subdirectories. + if strings.Contains(name, string(filepath.Separator)) { + return filepath.SkipDir + } + + _, err = build.ScanDir(path) + 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) + } + } + return nil + }) + + for _, t := range build.Path { + if pattern == "std" && !t.Goroot { + continue + } + src := t.SrcDir() + string(filepath.Separator) + filepath.Walk(src, func(path string, fi os.FileInfo, err error) error { + if err != nil || !fi.IsDir() { + return nil + } + + // Avoid .foo, _foo, and testdata directory trees. + _, elem := filepath.Split(path) + if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" { + return filepath.SkipDir + } + + name := filepath.ToSlash(path[len(src):]) + if pattern == "std" && strings.Contains(name, ".") { + return filepath.SkipDir + } + if have[name] { + return nil + } + have[name] = true + + _, err = build.ScanDir(path) + if err != nil { + return nil + } + + if match(name) { + pkgs = append(pkgs, name) + } + + // Avoid go/build test data. + // TODO: Move it into a testdata directory. + if path == filepath.Join(build.Path[0].SrcDir(), "go/build") { + return filepath.SkipDir + } + + return nil + }) + } + + if len(pkgs) == 0 { + fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern) + } + return pkgs +} + +// allPackagesInFS is like allPackages but is passed a pattern +// beginning ./ or ../, meaning it should scan the tree rooted +// at the given directory. There are ... in the pattern too. +func allPackagesInFS(pattern string) []string { + // Find directory to begin the scan. + // Could be smarter but this one optimization + // is enough for now, since ... is usually at the + // end of a path. + i := strings.Index(pattern, "...") + dir, _ := path.Split(pattern[:i]) + + // pattern begins with ./ or ../. + // path.Clean will discard the ./ but not the ../. + // We need to preserve the ./ for pattern matching + // and in the returned import paths. + prefix := "" + if strings.HasPrefix(pattern, "./") { + prefix = "./" + } + match := matchPattern(pattern) + + var pkgs []string + filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error { + if err != nil || !fi.IsDir() { + return nil + } + + // Avoid .foo, _foo, and testdata directory trees. + _, elem := filepath.Split(path) + if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" { + return filepath.SkipDir + } + + name := prefix + filepath.ToSlash(path) + if !match(name) { + return nil + } + if _, err = build.ScanDir(path); err != nil { + return nil + } + pkgs = append(pkgs, name) + return nil + }) + + if len(pkgs) == 0 { + fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern) + } + return pkgs +} + +// stringList's arguments should be a sequence of string or []string values. +// stringList flattens them into a single []string. +func stringList(args ...interface{}) []string { + var x []string + for _, arg := range args { + switch arg := arg.(type) { + case []string: + x = append(x, arg...) + case string: + x = append(x, arg) + default: + panic("stringList: invalid argument") + } + } + return x +} + +// isLocalPath returns true if arg is an import path denoting +// a local file system directory. That is, it returns true if the +// path begins with ./ or ../ . +func isLocalPath(arg string) bool { + return arg == "." || arg == ".." || strings.HasPrefix(arg, "./") || strings.HasPrefix(arg, "../") +} diff --git a/src/cmd/go/mkdoc.sh b/src/cmd/go/mkdoc.sh new file mode 100755 index 000000000..7768baeb6 --- /dev/null +++ b/src/cmd/go/mkdoc.sh @@ -0,0 +1,8 @@ +#!/bin/sh +# 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. + +go help documentation > doc.go +gofmt -w doc.go + diff --git a/src/cmd/go/pkg.go b/src/cmd/go/pkg.go new file mode 100644 index 000000000..09fa67127 --- /dev/null +++ b/src/cmd/go/pkg.go @@ -0,0 +1,445 @@ +// 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 ( + "go/build" + "os" + "path/filepath" + "sort" + "strings" + "time" +) + +// A Package describes a single package found in a directory. +type Package struct { + // Note: These fields are part of the go command's public API. + // See list.go. It is okay to add fields, but not to change or + // remove existing ones. Keep in sync with list.go + ImportPath string // import path of package in dir + Name string `json:",omitempty"` // package name + Doc string `json:",omitempty"` // package documentation string + Dir string `json:",omitempty"` // directory containing package sources + 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? + Incomplete bool `json:",omitempty"` // was there an error loading this package or dependencies? + Error *PackageError `json:",omitempty"` // error loading this package (not dependencies) + + // Source files + GoFiles []string `json:",omitempty"` // .go source files (excluding CgoFiles, TestGoFiles and XTestGoFiles) + TestGoFiles []string `json:",omitempty"` // _test.go source files internal to the package they are testing + XTestGoFiles []string `json:",omitempty"` //_test.go source files external to the package they are testing + CFiles []string `json:",omitempty"` // .c source files + HFiles []string `json:",omitempty"` // .h source files + SFiles []string `json:",omitempty"` // .s source files + CgoFiles []string `json:",omitempty"` // .go sources files that import "C" + CgoCFLAGS []string `json:",omitempty"` // cgo: flags for C compiler + CgoLDFLAGS []string `json:",omitempty"` // cgo: flags for linker + + // Dependency information + Imports []string `json:",omitempty"` // import paths used by this package + Deps []string `json:",omitempty"` // all (recursively) imported dependencies + DepsErrors []*PackageError `json:",omitempty"` // errors loading dependencies + + // Unexported fields are not part of the public API. + t *build.Tree + pkgdir string + info *build.DirInfo + imports []*Package + deps []*Package + gofiles []string // GoFiles+CgoFiles+TestGoFiles+XTestGoFiles files, absolute paths + target string // installed file for this package (may be executable) + fake bool // synthesized package +} + +// 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 + Err string // the error itself +} + +func (p *PackageError) Error() string { + return strings.Join(p.ImportStack, "\n\timports ") + ": " + p.Err +} + +// An importStack is a stack of import paths. +type importStack []string + +func (s *importStack) push(p string) { + *s = append(*s, p) +} + +func (s *importStack) pop() { + *s = (*s)[0 : len(*s)-1] +} + +func (s *importStack) copy() []string { + return append([]string{}, *s...) +} + +// shorterThan returns true if sp is shorter than t. +// We use this to record the shortest import sequence +// that leads to a particular package. +func (sp *importStack) shorterThan(t []string) bool { + s := *sp + if len(s) != len(t) { + return len(s) < len(t) + } + // If they are the same length, settle ties using string ordering. + for i := range s { + if s[i] != t[i] { + return s[i] < t[i] + } + } + return false // they are equal +} + +// packageCache is a lookup cache for loadPackage, +// so that if we look up a package multiple times +// we return the same pointer each time. +var packageCache = map[string]*Package{} + +// reloadPackage is like loadPackage but makes sure +// not to use the package cache. +func reloadPackage(arg string, stk *importStack) *Package { + p := packageCache[arg] + if p != nil { + delete(packageCache, p.Dir) + delete(packageCache, p.ImportPath) + } + return loadPackage(arg, stk) +} + +// loadPackage scans directory named by arg, +// which is either an import path or a file system path +// (if the latter, must be rooted or begin with . or ..), +// and returns a *Package describing the package +// found in that directory. +func loadPackage(arg string, stk *importStack) *Package { + stk.push(arg) + defer stk.pop() + + // Check package cache. + if p := packageCache[arg]; p != nil { + return reusePackage(p, stk) + } + + // Find basic information about package path. + t, importPath, err := build.FindTree(arg) + dir := "" + // Maybe it is a standard command. + if err != nil && strings.HasPrefix(arg, "cmd/") { + goroot := build.Path[0] + p := filepath.Join(goroot.Path, "src", arg) + if st, err1 := os.Stat(p); err1 == nil && st.IsDir() { + t = goroot + importPath = arg + dir = p + err = nil + } + } + // Maybe it is a path to a standard command. + if err != nil && (filepath.IsAbs(arg) || isLocalPath(arg)) { + arg, _ := filepath.Abs(arg) + goroot := build.Path[0] + cmd := filepath.Join(goroot.Path, "src", "cmd") + string(filepath.Separator) + if st, err1 := os.Stat(arg); err1 == nil && st.IsDir() && strings.HasPrefix(arg, cmd) { + t = goroot + importPath = filepath.FromSlash(arg[len(cmd):]) + dir = arg + err = nil + } + } + if err != nil { + p := &Package{ + ImportPath: arg, + Error: &PackageError{ + ImportStack: stk.copy(), + Err: err.Error(), + }, + Incomplete: true, + } + packageCache[arg] = p + return p + } + + if dir == "" { + dir = filepath.Join(t.SrcDir(), filepath.FromSlash(importPath)) + } + + // Maybe we know the package by its directory. + if p := packageCache[dir]; p != nil { + packageCache[importPath] = p + return reusePackage(p, stk) + } + + return scanPackage(&buildContext, t, arg, importPath, dir, stk) +} + +func reusePackage(p *Package, stk *importStack) *Package { + // We use p.imports==nil to detect a package that + // is in the midst of its own loadPackage call + // (all the recursion below happens before p.imports gets set). + if p.imports == nil { + if p.Error == nil { + p.Error = &PackageError{ + ImportStack: stk.copy(), + Err: "import loop", + } + } + p.Incomplete = true + } + if p.Error != nil && stk.shorterThan(p.Error.ImportStack) { + p.Error.ImportStack = stk.copy() + } + 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) +} + +func scanPackage(ctxt *build.Context, t *build.Tree, arg, importPath, dir string, stk *importStack) *Package { + // Read the files in the directory to learn the structure + // of the package. + p := &Package{ + ImportPath: importPath, + Dir: dir, + Standard: t.Goroot && !strings.Contains(importPath, "."), + t: t, + } + packageCache[dir] = p + packageCache[importPath] = p + + info, err := ctxt.ScanDir(dir) + if err != nil { + p.Error = &PackageError{ + ImportStack: stk.copy(), + Err: err.Error(), + } + p.Incomplete = true + return p + } + + p.info = info + p.Name = info.Package + p.Doc = firstSentence(info.PackageComment.Text()) + p.Imports = info.Imports + p.GoFiles = info.GoFiles + p.TestGoFiles = info.TestGoFiles + p.XTestGoFiles = info.XTestGoFiles + p.CFiles = info.CFiles + p.HFiles = info.HFiles + p.SFiles = info.SFiles + p.CgoFiles = info.CgoFiles + p.CgoCFLAGS = info.CgoCFLAGS + p.CgoLDFLAGS = info.CgoLDFLAGS + + if info.Package == "main" { + _, elem := filepath.Split(importPath) + 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") + } + + var built time.Time + if fi, err := os.Stat(p.target); err == nil { + built = fi.ModTime() + } + + // Build list of full paths to all Go files in the package, + // for use by commands like go fmt. + for _, f := range info.GoFiles { + p.gofiles = append(p.gofiles, filepath.Join(dir, f)) + } + for _, f := range info.CgoFiles { + p.gofiles = append(p.gofiles, filepath.Join(dir, f)) + } + for _, f := range info.TestGoFiles { + p.gofiles = append(p.gofiles, filepath.Join(dir, f)) + } + for _, f := range info.XTestGoFiles { + p.gofiles = append(p.gofiles, filepath.Join(dir, f)) + } + + sort.Strings(p.gofiles) + + srcss := [][]string{ + p.GoFiles, + p.CFiles, + p.HFiles, + p.SFiles, + p.CgoFiles, + } +Stale: + for _, srcs := range srcss { + for _, src := range srcs { + if fi, err := os.Stat(filepath.Join(p.Dir, src)); err != nil || fi.ModTime().After(built) { + //println("STALE", p.ImportPath, "needs", src, err) + p.Stale = true + break Stale + } + } + } + + importPaths := p.Imports + // Packages that use cgo import runtime/cgo implicitly, + // except runtime/cgo itself. + if len(info.CgoFiles) > 0 && (!p.Standard || p.ImportPath != "runtime/cgo") { + importPaths = append(importPaths, "runtime/cgo") + } + // Everything depends on runtime, except runtime and unsafe. + if !p.Standard || (p.ImportPath != "runtime" && p.ImportPath != "unsafe") { + importPaths = append(importPaths, "runtime") + } + + // Record package under both import path and full directory name. + packageCache[dir] = p + packageCache[importPath] = p + + // Build list of imported packages and full dependency list. + imports := make([]*Package, 0, len(p.Imports)) + deps := make(map[string]bool) + for _, path := range importPaths { + if path == "C" { + continue + } + deps[path] = true + p1 := loadPackage(path, stk) + imports = append(imports, p1) + for _, dep := range p1.Deps { + deps[dep] = true + } + if p1.Stale { + p.Stale = true + } + if p1.Incomplete { + p.Incomplete = true + } + // p1.target can be empty only if p1 is not a real package, + // such as package unsafe or the temporary packages + // created during go test. + if !p.Stale && p1.target != "" { + if fi, err := os.Stat(p1.target); err != nil || fi.ModTime().After(built) { + //println("STALE", p.ImportPath, "needs", p1.target, err) + //println("BUILT", built.String(), "VS", fi.ModTime().String()) + p.Stale = true + } + } + } + p.imports = imports + + p.Deps = make([]string, 0, len(deps)) + for dep := range deps { + p.Deps = append(p.Deps, dep) + } + sort.Strings(p.Deps) + for _, dep := range p.Deps { + p1 := packageCache[dep] + if p1 == nil { + panic("impossible: missing entry in package cache for " + dep + " imported by " + p.ImportPath) + } + p.deps = append(p.deps, p1) + if p1.Error != nil { + p.DepsErrors = append(p.DepsErrors, p1.Error) + } + } + + // unsafe is a fake package and is never out-of-date. + if p.Standard && p.ImportPath == "unsafe" { + p.Stale = false + p.target = "" + } + + return p +} + +// packages returns the packages named by the +// command line arguments 'args'. If a named package +// cannot be loaded at all (for example, if the directory does not exist), +// then packages prints an error and does not include that +// package in the results. However, if errors occur trying +// to load dependencies of a named package, the named +// package is still returned, with p.Incomplete = true +// and details in p.DepsErrors. +func packages(args []string) []*Package { + args = importPaths(args) + var pkgs []*Package + var stk importStack + for _, arg := range args { + pkg := loadPackage(arg, &stk) + if pkg.Error != nil { + errorf("%s", pkg.Error) + continue + } + pkgs = append(pkgs, pkg) + } + return pkgs +} + +// packagesAndErrors is like 'packages' but returns a +// *Package for every argument, even the ones that +// cannot be loaded at all. +// The packages that fail to load will have p.Error != nil. +func packagesAndErrors(args []string) []*Package { + args = importPaths(args) + var pkgs []*Package + var stk importStack + for _, arg := range args { + pkgs = append(pkgs, loadPackage(arg, &stk)) + } + return pkgs +} + +// packagesForBuild is like 'packages' but fails if any of +// the packages or their dependencies have errors +// (cannot be built). +func packagesForBuild(args []string) []*Package { + pkgs := packagesAndErrors(args) + printed := map[*PackageError]bool{} + for _, pkg := range pkgs { + if pkg.Error != nil { + errorf("%s", pkg.Error) + } + 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) + } + } + } + exitIfErrors() + return pkgs +} diff --git a/src/cmd/go/run.go b/src/cmd/go/run.go new file mode 100644 index 000000000..714cd4051 --- /dev/null +++ b/src/cmd/go/run.go @@ -0,0 +1,57 @@ +// 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 "strings" + +var cmdRun = &Command{ + UsageLine: "run [-a] [-n] [-x] gofiles... [arguments...]", + Short: "compile and run Go program", + Long: ` +Run compiles and runs the main package comprising the named Go source files. + +The -a flag forces reinstallation of packages that are already up-to-date. +The -n flag prints the commands but does not run them. +The -x flag prints the commands. + +See also: go build. + `, +} + +func init() { + cmdRun.Run = runRun // break init loop + + cmdRun.Flag.BoolVar(&buildA, "a", false, "") + cmdRun.Flag.BoolVar(&buildN, "n", false, "") + cmdRun.Flag.BoolVar(&buildX, "x", false, "") +} + +func runRun(cmd *Command, args []string) { + var b builder + b.init() + i := 0 + for i < len(args) && strings.HasSuffix(args[i], ".go") { + i++ + } + files, cmdArgs := args[:i], args[i:] + p := goFilesPackage(files, "") + p.target = "" // must build - not up to date + a1 := b.action(modeBuild, modeBuild, p) + a := &action{f: (*builder).runProgram, args: cmdArgs, deps: []*action{a1}} + b.do(a) +} + +// runProgram is the action for running a binary that has already +// been compiled. We ignore exit status. +func (b *builder) runProgram(a *action) error { + if buildN || buildX { + b.showcmd("", "%s %s", a.deps[0].target, strings.Join(a.args, " ")) + if buildN { + return nil + } + } + run(a.deps[0].target, a.args) + return nil +} diff --git a/src/cmd/go/script b/src/cmd/go/script new file mode 100755 index 000000000..340a7e824 --- /dev/null +++ b/src/cmd/go/script @@ -0,0 +1,23 @@ +#!/bin/sh + +x() { + echo '--- ' "$@" + "$@" + echo '---' + echo +} + +x go help +x go help build +x go help clean +x go help install +x go help fix +x go help fmt +x go help get +x go help list +x go help test +x go help version +x go help vet +x go help gopath +x go help importpath +x go help remote diff --git a/src/cmd/go/script.txt b/src/cmd/go/script.txt new file mode 100644 index 000000000..a67214658 --- /dev/null +++ b/src/cmd/go/script.txt @@ -0,0 +1,352 @@ +--- go help +usage: go command [arguments] + +go manages Go source code. + +The commands are: + + build compile and install packages and dependencies + clean remove intermediate objects + fix run gofix on packages + fmt run gofmt -w on packages + get download and install packages and dependencies + install install packages and dependencies + list list packages + test test packages + version print Go version + vet run govet on packages + +Use "go help [command]" for more information about a command. + +Additional help topics: + + gopath GOPATH environment variable + importpath description of import paths + remote remote import path syntax + +Use "go help [topic]" for more information about that topic. + +--- + +--- go help build +usage: go build [-n] [-v] [importpath...] + +Build compiles the packages named by the import paths, +along with their dependencies, but it does not install the results. + +The -n flag prints the commands but does not run them. +The -v flag prints the commands. + +For more about import paths, see 'go help importpath'. + +See also: go install, go get, go clean. +--- + +--- go help clean +usage: go clean [-nuke] [importpath...] + +Clean removes intermediate object files generated during +the compilation of the packages named by the import paths, +but by default it does not remove the installed package binaries. + +The -nuke flag causes clean to remove the installed package binaries too. + +TODO: Clean does not clean dependencies of the packages. + +For more about import paths, see 'go help importpath'. +--- + +--- go help install +usage: go install [-n] [-v] [importpath...] + +Install compiles and installs the packages named by the import paths, +along with their dependencies. + +The -n flag prints the commands but does not run them. +The -v flag prints the commands. + +For more about import paths, see 'go help importpath'. + +See also: go build, go get, go clean. +--- + +--- go help fix +usage: go fix [importpath...] + +Fix runs the gofix command on the packages named by the import paths. + +For more about gofix, see 'godoc gofix'. +For more about import paths, see 'go help importpath'. + +To run gofix with specific options, run gofix itself. + +See also: go fmt, go vet. +--- + +--- go help fmt +usage: go fmt [importpath...] + +Fmt runs the command 'gofmt -w' on the packages named by the import paths. + +For more about gofmt, see 'godoc gofmt'. +For more about import paths, see 'go help importpath'. + +To run gofmt with specific options, run gofmt itself. + +See also: go fix, go vet. +--- + +--- go help get +usage: go get [importpath...] + +Get downloads and installs the packages named by the import paths, +along with their dependencies. + +After downloading the code, 'go get' looks for a tag beginning +with "go." that corresponds to the local Go version. +For Go "release.r58" it looks for a tag named "go.r58". +For "weekly.2011-06-03" it looks for "go.weekly.2011-06-03". +If the specific "go.X" tag is not found, it uses the latest earlier +version it can find. Otherwise, it uses the default version for +the version control system: HEAD for git, tip for Mercurial, +and so on. + +TODO: Explain versions better. + +For more about import paths, see 'go help importpath'. + +For more about how 'go get' finds source code to +download, see 'go help remote'. + +See also: go build, go install, go clean. +--- + +--- go help list +usage: go list [-f format] [-json] [importpath...] + +List lists the packages named by the import paths. + +The default output shows the package name and file system location: + + books /home/you/src/google-api-go-client.googlecode.com/hg/books/v1 + oauth /home/you/src/goauth2.googlecode.com/hg/oauth + sqlite /home/you/src/gosqlite.googlecode.com/hg/sqlite + +The -f flag specifies an alternate format for the list, +using the syntax of package template. The default output +is equivalent to -f '{{.Name}} {{.Dir}}' The struct +being passed to the template is: + + type Package struct { + Name string // package name + Doc string // package documentation string + GoFiles []string // names of Go source files in package + ImportPath string // import path denoting package + Imports []string // import paths used by this package + Deps []string // all (recursively) imported dependencies + Dir string // directory containing package sources + Version string // version of installed package + } + +The -json flag causes the package data to be printed in JSON format. + +For more about import paths, see 'go help importpath'. +--- + +--- go help test +usage: go test [importpath...] + +Test runs gotest to test the packages named by the import paths. +It prints a summary of the test results in the format: + + test archive/tar + FAIL archive/zip + test compress/gzip + ... + +followed by gotest output for each failed package. + +For more about import paths, see 'go help importpath'. + +See also: go build, go compile, go vet. +--- + +--- go help version +usage: go version + +Version prints the Go version, as reported by runtime.Version. +--- + +--- go help vet +usage: go vet [importpath...] + +Vet runs the govet command on the packages named by the import paths. + +For more about govet, see 'godoc govet'. +For more about import paths, see 'go help importpath'. + +To run govet with specific options, run govet itself. + +See also: go fmt, go fix. +--- + +--- go help gopath +The GOPATH environment variable lists places to look for Go code. +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 +standard Go tree. + +Each directory listed in GOPATH must have a prescribed structure: + +The src/ directory holds source code. The path below 'src' +determines the import path or executable name. + +The pkg/ directory holds installed package objects. +As in the Go tree, each target operating system and +architecture pair has its own subdirectory of pkg +(pkg/GOOS_GOARCH). + +If DIR is a directory listed in the GOPATH, a package with +source in DIR/src/foo/bar can be imported as "foo/bar" and +has its compiled form installed to "DIR/pkg/GOOS_GOARCH/foo/bar.a". + +The bin/ directory holds compiled commands. +Each command is named for its source directory, but only +the final element, not the entire path. That is, the +command with source in DIR/src/foo/quux is installed into +DIR/bin/quux, not DIR/bin/foo/quux. The foo/ is stripped +so that you can add DIR/bin to your PATH to get at the +installed commands. + +Here's an example directory layout: + + GOPATH=/home/user/gocode + + /home/user/gocode/ + src/ + foo/ + bar/ (go code in package bar) + x.go + quux/ (go code in package main) + y.go + bin/ + quux (installed command) + pkg/ + linux_amd64/ + foo/ + 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 +in the list. +--- + +--- go help importpath +Many commands apply to a set of packages named by import paths: + + go action [importpath...] + +An import path that is a rooted path or that begins with +a . or .. element is interpreted as a file system path and +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'). + +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' +lists all the packages on the local system. + +An import path can also name a package to be downloaded from +a remote repository. Run 'go help remote' for details. + +Every package in a program must have a unique import path. +By convention, this is arranged by starting each path with a +unique prefix that belongs to you. For example, paths used +internally at Google all begin with 'google', and paths +denoting remote repositories begin with the path to the code, +such as 'project.googlecode.com/'. +--- + +--- go help remote +An import path (see 'go help importpath') denotes a package +stored in the local file system. Certain import paths also +describe how to obtain the source code for the package using +a revision control system. + +A few common code hosting sites have special syntax: + + BitBucket (Mercurial) + + import "bitbucket.org/user/project" + import "bitbucket.org/user/project/sub/directory" + + GitHub (Git) + + import "github.com/user/project" + import "github.com/user/project/sub/directory" + + Google Code Project Hosting (Git, Mercurial, Subversion) + + import "project.googlecode.com/git" + import "project.googlecode.com/git/sub/directory" + + import "project.googlecode.com/hg" + import "project.googlecode.com/hg/sub/directory" + + import "project.googlecode.com/svn/trunk" + import "project.googlecode.com/svn/trunk/sub/directory" + + Launchpad (Bazaar) + + import "launchpad.net/project" + import "launchpad.net/project/series" + import "launchpad.net/project/series/sub/directory" + + import "launchpad.net/~user/project/branch" + import "launchpad.net/~user/project/branch/sub/directory" + +For code hosted on other servers, an import path of the form + + repository.vcs/path + +specifies the given repository, with or without the .vcs suffix, +using the named version control system, and then the path inside +that repository. The supported version control systems are: + + Bazaar .bzr + Git .git + Mercurial .hg + Subversion .svn + +For example, + + import "example.org/user/foo.hg" + +denotes the root directory of the Mercurial repository at +example.org/user/foo or foo.hg, and + + import "example.org/repo.git/foo/bar" + +denotes the foo/bar directory of the Git repository at +example.com/repo or repo.git. + +When a version control system supports multiple protocols, +each is tried in turn when downloading. For example, a Git +download tries git://, then https://, then http://. + +New downloaded packages are written to the first directory +listed in the GOPATH environment variable (see 'go help gopath'). + +The go command attempts to download the version of the +package appropriate for the Go release being used. +Run 'go help install' for more. +--- + diff --git a/src/cmd/go/test.go b/src/cmd/go/test.go new file mode 100644 index 000000000..6cd49fe5a --- /dev/null +++ b/src/cmd/go/test.go @@ -0,0 +1,681 @@ +// 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 ( + "bytes" + "fmt" + "go/ast" + "go/build" + "go/parser" + "go/token" + "os" + "os/exec" + "path/filepath" + "strings" + "text/template" + "time" + "unicode" + "unicode/utf8" +) + +// Break init loop. +func init() { + cmdTest.Run = runTest +} + +var cmdTest = &Command{ + CustomFlags: true, + UsageLine: "test [-c] [-file a.go -file b.go ...] [-p n] [-x] [importpath...] [flags for test binary]", + Short: "test packages", + Long: ` +'Go test' automates testing the packages named by the import paths. +It prints a summary of the test results in the format: + + ok archive/tar 0.011s + FAIL archive/zip 0.022s + ok compress/gzip 0.033s + ... + +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. + +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. + +See 'go help importpath' for more about import paths. + +See also: go build, go vet. + `, +} + +var helpTestflag = &Command{ + UsageLine: "testflag", + Short: "description of testing flags", + Long: ` +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: + + -test.v + Verbose output: log all tests as they are run. + + -test.run pattern + Run only those tests matching the regular expression. + + -test.bench pattern + Run benchmarks matching the regular expression. + By default, no benchmarks run. + + -test.cpuprofile cpu.out + Write a CPU profile to the specified file before exiting. + + -test.memprofile mem.out + Write a memory profile to the specified file when all tests + are complete. + + -test.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 + and set the environment variable GOGC=off to disable the + garbage collector, provided the test can run in the available + memory without garbage collection. + + -test.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 + 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 + 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. + + -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. + +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 + +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 + `, +} + +var helpTestfunc = &Command{ + UsageLine: "testfunc", + Short: "description of testing functions", + Long: ` +The 'go test' command expects to find test, benchmark, and example functions +in the "*_test.go" files corresponding to the package under test. + +A test function is one named TestXXX (where XXX is any alphanumeric string +not starting with a lower case letter) and should have the signature, + + func TestXXX(t *testing.T) { ... } + +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. +That output is compared against the function's doc comment. +An example without a doc comment is compiled but not executed. + +Godoc displays the body of ExampleXXX to demonstrate the use +of the function, constant, or variable XXX. An example of a method M with +receiver type T or *T is named ExampleT_M. There may be multiple examples +for a given function, constant, or variable, distinguished by a trailing _xxx, +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.") + } + +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 +) + +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") + } + + if testC && len(pkgs) != 1 { + fatalf("cannot use -c flag with multiple packages") + } + + buildX = testX + if testP > 0 { + buildP = testP + } + + var b builder + b.init() + + var builds, runs, prints []*action + + // Prepare build + run + print actions for all packages being tested. + for _, p := range pkgs { + buildTest, runTest, printTest, err := b.test(p) + if err != nil { + errorf("%s", err) + continue + } + builds = append(builds, buildTest) + runs = append(runs, runTest) + prints = append(prints, printTest) + } + + // Ultimately the goal is to print the output. + root := &action{deps: prints} + + // Force the printing of results to happen in order, + // one at a time. + for i, a := range prints { + if i > 0 { + a.deps = append(a.deps, prints[i-1]) + } + } + + // If we are benchmarking, force everything to + // happen in serial. Could instead allow all the + // builds to run before any benchmarks start, + // but try this for now. + if testBench { + for i, a := range builds { + if i > 0 { + // Make build of test i depend on + // completing the run of test i-1. + a.deps = append(a.deps, runs[i-1]) + } + } + } + + // If we are building any out-of-date packages other + // than those under test, warn. + okBuild := map[*Package]bool{} + for _, p := range pkgs { + okBuild[p] = true + } + + warned := false + for _, a := range actionList(root) { + if a.p != nil && a.f != nil && !okBuild[a.p] && !a.p.fake { + okBuild[a.p] = true // don't warn again + if !warned { + fmt.Fprintf(os.Stderr, "warning: building out-of-date packages:\n") + warned = true + } + fmt.Fprintf(os.Stderr, "\t%s\n", a.p.ImportPath) + } + } + if warned { + fmt.Fprintf(os.Stderr, "installing these packages with 'go install' will speed future tests.\n\n") + } + + b.do(root) +} + +func (b *builder) test(p *Package) (buildAction, runAction, printAction *action, err error) { + if len(p.info.TestGoFiles)+len(p.info.XTestGoFiles) == 0 { + build := &action{p: p} + run := &action{p: p} + print := &action{f: (*builder).notest, p: p, deps: []*action{build}} + return build, run, print, nil + } + + // Build Package structs describing: + // ptest - package + test files + // pxtest - package of external test files + // pmain - test.out binary + var ptest, pxtest, pmain *Package + + // go/build does not distinguish the dependencies used + // by the TestGoFiles from the dependencies used by the + // XTestGoFiles, so we build one list and use it for both + // ptest and pxtest. No harm done. + var imports []*Package + var stk importStack + stk.push(p.ImportPath + "_test") + for _, path := range p.info.TestImports { + p1 := loadPackage(path, &stk) + if p1.Error != nil { + return nil, nil, nil, p1.Error + } + imports = append(imports, p1) + } + stk.pop() + + // 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 + // other tests will see it as the real package. + // Instead we make a _test directory under the import path + // and then repeat the import path there. We tell the + // compiler and linker to look in that _test directory first. + // + // That is, if the package under test is unicode/utf8, + // then the normal place to write the package archive is + // $WORK/unicode/utf8.a, but we write the test package archive to + // $WORK/unicode/utf8/_test/unicode/utf8.a. + // 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")) + + // Create the directory for the .a files. + ptestDir, _ := filepath.Split(ptestObj) + if err := b.mkdir(ptestDir); err != nil { + return nil, nil, nil, err + } + if err := writeTestmain(filepath.Join(testDir, "_testmain.go"), p); err != nil { + return nil, nil, nil, err + } + + // Test package. + if len(p.info.TestGoFiles) > 0 { + ptest = new(Package) + *ptest = *p + ptest.GoFiles = nil + ptest.GoFiles = append(ptest.GoFiles, p.GoFiles...) + ptest.GoFiles = append(ptest.GoFiles, p.info.TestGoFiles...) + ptest.target = "" + ptest.Imports = stringList(p.info.Imports, p.info.TestImports) + ptest.imports = append(append([]*Package{}, p.imports...), imports...) + ptest.pkgdir = testDir + ptest.fake = true + a := b.action(modeBuild, modeBuild, ptest) + a.objdir = testDir + string(filepath.Separator) + a.objpkg = ptestObj + a.target = ptestObj + a.link = false + } else { + ptest = p + } + + // External test package. + if len(p.info.XTestGoFiles) > 0 { + pxtest = &Package{ + Name: p.Name + "_test", + ImportPath: p.ImportPath + "_test", + Dir: p.Dir, + GoFiles: p.info.XTestGoFiles, + Imports: p.info.TestImports, + t: p.t, + info: &build.DirInfo{}, + imports: imports, + pkgdir: testDir, + fake: true, + } + pxtest.imports = append(pxtest.imports, ptest) + a := b.action(modeBuild, modeBuild, pxtest) + a.objdir = testDir + string(filepath.Separator) + a.objpkg = pxtestObj + a.target = pxtestObj + } + + // Action for building test.out. + pmain = &Package{ + Name: "main", + Dir: testDir, + GoFiles: []string{"_testmain.go"}, + t: p.t, + info: &build.DirInfo{}, + imports: []*Package{ptest}, + fake: true, + } + if pxtest != nil { + pmain.imports = append(pmain.imports, pxtest) + } + + // The generated main also imports testing and regexp. + stk.push("testmain") + ptesting := loadPackage("testing", &stk) + if ptesting.Error != nil { + return nil, nil, nil, ptesting.Error + } + pregexp := loadPackage("regexp", &stk) + if pregexp.Error != nil { + return nil, nil, nil, pregexp.Error + } + pmain.imports = append(pmain.imports, ptesting, pregexp) + + 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 + pmainAction := a + + if testC { + // -c flag: create action to copy binary to ./test.out. + runAction = &action{ + f: (*builder).install, + deps: []*action{pmainAction}, + p: pmain, + target: "test.out" + b.exe, + } + printAction = &action{p: p, deps: []*action{runAction}} // nop + } else { + // run test + runAction = &action{ + f: (*builder).runTest, + deps: []*action{pmainAction}, + p: p, + ignoreFail: true, + } + printAction = &action{ + f: (*builder).printTest, + deps: []*action{runAction}, + p: p, + } + } + + return pmainAction, runAction, printAction, nil +} + +// runTest is the action for running a test binary. +func (b *builder) runTest(a *action) error { + args := stringList(a.deps[0].target, testArgs) + a.testOutput = new(bytes.Buffer) + + if buildN || buildX { + b.showcmd("", "%s", strings.Join(args, " ")) + if buildN { + return nil + } + } + + if a.failed { + // 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 + return nil + } + + cmd := exec.Command(args[0], args[1:]...) + cmd.Dir = a.p.Dir + var buf bytes.Buffer + cmd.Stdout = &buf + cmd.Stderr = &buf + + t0 := time.Now() + err := cmd.Start() + + // 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) + if err == nil { + done := make(chan error) + go func() { + done <- cmd.Wait() + }() + select { + case err = <-done: + // ok + case <-tick.C: + cmd.Process.Kill() + err = <-done + fmt.Fprintf(&buf, "*** Test killed: ran too long.\n") + } + tick.Stop() + } + out := buf.Bytes() + 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) + } + return nil + } + + fmt.Fprintf(a.testOutput, "FAIL\t%s\t%s\n", a.p.ImportPath, t) + exitStatus = 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) + } + return nil +} + +// printTest is the action for printing a test result. +func (b *builder) printTest(a *action) error { + run := a.deps[0] + os.Stdout.Write(run.testOutput.Bytes()) + run.testOutput = nil + return nil +} + +// notest is the action for testing a package with no test files. +func (b *builder) notest(a *action) error { + fmt.Printf("? \t%s\t[no test files]\n", a.p.ImportPath) + return nil +} + +// isTest tells whether name looks like a test (or benchmark, according to prefix). +// It is a Test (say) if there is a character after Test that is not a lower-case letter. +// We don't want TesticularCancer. +func isTest(name, prefix string) bool { + if !strings.HasPrefix(name, prefix) { + return false + } + if len(name) == len(prefix) { // "Test" is ok + return true + } + rune, _ := utf8.DecodeRuneInString(name[len(prefix):]) + return !unicode.IsLower(rune) +} + +// writeTestmain writes the _testmain.go file for package p to +// the file named out. +func writeTestmain(out string, p *Package) error { + t := &testFuncs{ + Package: p, + Info: p.info, + } + for _, file := range p.info.TestGoFiles { + if err := t.load(filepath.Join(p.Dir, file), "_test", &t.NeedTest); err != nil { + return err + } + } + for _, file := range p.info.XTestGoFiles { + if err := t.load(filepath.Join(p.Dir, file), "_xtest", &t.NeedXtest); err != nil { + return err + } + } + + f, err := os.Create(out) + if err != nil { + return err + } + defer f.Close() + + if err := testmainTmpl.Execute(f, t); err != nil { + return err + } + + return nil +} + +type testFuncs struct { + Tests []testFunc + Benchmarks []testFunc + Examples []testFunc + Package *Package + Info *build.DirInfo + NeedTest bool + NeedXtest bool +} + +type testFunc struct { + Package string // imported package name (_test or _xtest) + Name string // function name + Output string // output, for examples +} + +var testFileSet = token.NewFileSet() + +func (t *testFuncs) load(filename, pkg string, seen *bool) error { + f, err := parser.ParseFile(testFileSet, filename, nil, parser.ParseComments) + if err != nil { + return err + } + for _, d := range f.Decls { + n, ok := d.(*ast.FuncDecl) + if !ok { + continue + } + if n.Recv != nil { + continue + } + name := n.Name.String() + switch { + case isTest(name, "Test"): + t.Tests = append(t.Tests, testFunc{pkg, name, ""}) + *seen = true + 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 + } + } + + return nil +} + +var testmainTmpl = template.Must(template.New("main").Parse(` +package main + +import ( + "regexp" + "testing" + +{{if .NeedTest}} + _test {{.Package.ImportPath | printf "%q"}} +{{end}} +{{if .NeedXtest}} + _xtest {{.Package.ImportPath | printf "%s_test" | printf "%q"}} +{{end}} +) + +var tests = []testing.InternalTest{ +{{range .Tests}} + {"{{.Name}}", {{.Package}}.{{.Name}}}, +{{end}} +} + +var benchmarks = []testing.InternalBenchmark{ +{{range .Benchmarks}} + {"{{.Name}}", {{.Package}}.{{.Name}}}, +{{end}} +} + +var examples = []testing.InternalExample{ +{{range .Examples}} + {"{{.Name}}", {{.Package}}.{{.Name}}, {{.Output | printf "%q"}}}, +{{end}} +} + +var matchPat string +var matchRe *regexp.Regexp + +func matchString(pat, str string) (result bool, err error) { + if matchRe == nil || matchPat != pat { + matchPat = pat + matchRe, err = regexp.Compile(matchPat) + if err != nil { + return + } + } + return matchRe.MatchString(str), nil +} + +func main() { + testing.Main(matchString, tests, benchmarks, examples) +} + +`)) diff --git a/src/cmd/go/testflag.go b/src/cmd/go/testflag.go new file mode 100644 index 000000000..a3cacd657 --- /dev/null +++ b/src/cmd/go/testflag.go @@ -0,0 +1,215 @@ +// 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" + "os" + "strconv" + "strings" +) + +// The flag handling part of go test is large and distracting. +// We can't use the flag package because some of the flags from +// our command line are for us, and some are for 6.out, and +// some are for both. + +var usageMessage = `Usage of go test: + -c=false: compile but do not run the test binary + -file=file_test.go: specify file to use for tests; + use multiple times for multiple files + -p=n: build and test up to n packages in parallel + -x=false: print command lines as they are executed + + // 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 + -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 + -parallel=0: passes -test.parallel to test + -run="": passes -test.run to test + -short=false: passes -test.short to test + -timeout=0: passes -test.timeout to test + -v=false: passes -test.v to test +` + +// usage prints a usage message and exits. +func testUsage() { + fmt.Fprint(os.Stderr, usageMessage) + exitStatus = 2 + exit() +} + +// testFlagSpec defines a flag we know about. +type testFlagSpec struct { + name string + isBool bool + passToTest bool // pass to Test + multiOK bool // OK to have multiple instances + present bool // flag has been seen +} + +// testFlagDefn is the set of flags we process. +var testFlagDefn = []*testFlagSpec{ + // local. + {name: "c", isBool: true}, + {name: "file", multiOK: true}, + {name: "p"}, + {name: "x", isBool: true}, + + // passed to 6.out, adding a "test." prefix to the name if necessary: -v becomes -test.v. + {name: "bench", passToTest: true}, + {name: "benchtime", passToTest: true}, + {name: "cpu", passToTest: true}, + {name: "cpuprofile", passToTest: true}, + {name: "memprofile", passToTest: true}, + {name: "memprofilerate", passToTest: true}, + {name: "parallel", passToTest: true}, + {name: "run", passToTest: true}, + {name: "short", isBool: true, passToTest: true}, + {name: "timeout", passToTest: true}, + {name: "v", isBool: true, passToTest: true}, +} + +// testFlags processes the command line, grabbing -x and -c, rewriting known flags +// 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. +// We allow known flags both before and after the package name list, +// to allow both +// go test fmt -custom-flag-for-fmt-test +// go test -x math +func testFlags(args []string) (packageNames, passToTest []string) { + inPkg := false + for i := 0; i < len(args); i++ { + if !strings.HasPrefix(args[i], "-") { + if !inPkg && packageNames == nil { + // First package name we've seen. + inPkg = true + } + if inPkg { + packageNames = append(packageNames, args[i]) + continue + } + } + + if inPkg { + // Found an argument beginning with "-"; end of package list. + inPkg = false + } + + 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 + // arguments, not package names. + inPkg = false + if packageNames == nil { + // make non-nil: we have seen the empty package list + packageNames = []string{} + } + passToTest = append(passToTest, args[i]) + continue + } + switch f.name { + case "c": + setBoolFlag(&testC, value) + case "p": + setIntFlag(&testP, value) + case "x": + setBoolFlag(&testX, value) + case "v": + setBoolFlag(&testV, value) + case "file": + testFiles = append(testFiles, value) + case "bench": + // record that we saw the flag; don't care about the value + testBench = true + } + if extraWord { + i++ + } + if f.passToTest { + passToTest = append(passToTest, "-test."+f.name+"="+value) + } + } + return +} + +// testFlag sees if argument i is a known flag and returns its definition, value, and whether it consumed an extra word. +func testFlag(args []string, i int) (f *testFlagSpec, value string, extra bool) { + arg := args[i] + if strings.HasPrefix(arg, "--") { // reduce two minuses to one + arg = arg[1:] + } + switch arg { + case "-?", "-h", "-help": + usage() + } + if arg == "" || arg[0] != '-' { + return + } + name := arg[1:] + // If there's already "test.", drop it for now. + if strings.HasPrefix(name, "test.") { + name = name[5:] + } + equals := strings.Index(name, "=") + if equals >= 0 { + value = name[equals+1:] + name = name[:equals] + } + for _, f = range testFlagDefn { + if name == f.name { + // Booleans are special because they have modes -x, -x=true, -x=false. + if f.isBool { + if equals < 0 { // otherwise, it's been set and will be verified in setBoolFlag + value = "true" + } else { + // verify it parses + setBoolFlag(new(bool), value) + } + } else { // Non-booleans must have a value. + extra = equals < 0 + if extra { + if i+1 >= len(args) { + usage() + } + value = args[i+1] + } + } + if f.present && !f.multiOK { + usage() + } + f.present = true + return + } + } + f = nil + return +} + +// setBoolFlag sets the addressed boolean to the value. +func setBoolFlag(flag *bool, value string) { + x, err := strconv.ParseBool(value) + if err != nil { + fmt.Fprintf(os.Stderr, "go test: illegal bool flag value %s\n", value) + usage() + } + *flag = x +} + +// setIntFlag sets the addressed integer to the value. +func setIntFlag(flag *int, value string) { + x, err := strconv.Atoi(value) + if err != nil { + fmt.Fprintf(os.Stderr, "go test: illegal int flag value %s\n", value) + usage() + } + *flag = x +} diff --git a/src/cmd/go/vcs.go b/src/cmd/go/vcs.go new file mode 100644 index 000000000..da35048d5 --- /dev/null +++ b/src/cmd/go/vcs.go @@ -0,0 +1,425 @@ +// 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 ( + "bytes" + "encoding/json" + "fmt" + "os" + "os/exec" + "regexp" + "strings" +) + +// A vcsCmd describes how to use a version control system +// like Mercurial, Git, or Subversion. +type vcsCmd struct { + name string + cmd string // name of binary to invoke command + + 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 +} + +// A tagCmd describes a command to list available tags +// that can be passed to tagSyncCmd. +type tagCmd struct { + cmd string // command to list tags + pattern string // regexp to extract tags from list +} + +// vcsList lists the known version control systems +var vcsList = []*vcsCmd{ + vcsHg, + vcsGit, + vcsSvn, + vcsBzr, +} + +// vcsByCmd returns the version control system for the given +// command name (hg, git, svn, bzr). +func vcsByCmd(cmd string) *vcsCmd { + for _, vcs := range vcsList { + if vcs.cmd == cmd { + return vcs + } + } + return nil +} + +// vcsHg describes how to use Mercurial. +var vcsHg = &vcsCmd{ + name: "Mercurial", + cmd: "hg", + + createCmd: "clone -U {repo} {dir}", + downloadCmd: "pull", + + // We allow both tag and branch names as 'tags' + // for selecting a version. This lets people have + // a go.release.r60 branch and a go.1 branch + // and make changes in both, without constantly + // editing .hgtags. + tagCmd: []tagCmd{ + {"tags", `^(\S+)`}, + {"branches", `^(\S+)`}, + }, + tagDefault: "default", + tagSyncCmd: "update -r {tag}", +} + +// vcsGit describes how to use Git. +var vcsGit = &vcsCmd{ + name: "Git", + cmd: "git", + + createCmd: "clone {repo} {dir}", + downloadCmd: "fetch", + + tagCmd: []tagCmd{{"tag", `^(\S+)$`}}, + tagDefault: "master", + tagSyncCmd: "checkout {tag}", +} + +// vcsBzr describes how to use Bazaar. +var vcsBzr = &vcsCmd{ + name: "Bazaar", + cmd: "bzr", + + createCmd: "branch {repo} {dir}", + + // Without --overwrite bzr will not pull tags that changed. + // 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}", +} + +// vcsSvn describes how to use Subversion. +var vcsSvn = &vcsCmd{ + name: "Subversion", + cmd: "svn", + + createCmd: "checkout {repo} {dir}", + downloadCmd: "update", + + // There is no tag command in subversion. + // The branch information is all in the path names. +} + +func (v *vcsCmd) String() string { + return v.name +} + +// run runs the command line cmd in the given directory. +// keyval is a list of key, value pairs. run expands +// instances of {key} in cmd into value, but only after +// splitting cmd into individual arguments. +// If an error occurs, run prints the command line and the +// 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) + 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) +} + +// run1 is the generalized implementation of run and runOutput. +func (v *vcsCmd) run1(dir string, output bool, cmdline string, keyval []string) ([]byte, error) { + m := make(map[string]string) + for i := 0; i < len(keyval); i += 2 { + m[keyval[i]] = keyval[i+1] + } + args := strings.Fields(cmdline) + for i, arg := range args { + args[i] = expand(m, arg) + } + + cmd := exec.Command(v.cmd, args...) + cmd.Dir = dir + if buildX { + fmt.Printf("cd %s\n", dir) + fmt.Printf("%s %s\n", v.cmd, strings.Join(args, " ")) + } + var buf bytes.Buffer + cmd.Stdout = &buf + cmd.Stderr = &buf + out := buf.Bytes() + err := cmd.Run() + if err != nil { + 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 +} + +// 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 { + return v.run(".", v.createCmd, "dir", dir, "repo", repo) +} + +// download downloads any new changes for the repo in dir. +func (v *vcsCmd) download(dir string) error { + return v.run(dir, v.downloadCmd) +} + +// tags returns the list of available tags for the repo in dir. +func (v *vcsCmd) tags(dir string) ([]string, error) { + var tags []string + for _, tc := range v.tagCmd { + out, err := v.runOutput(dir, tc.cmd) + if err != nil { + return nil, err + } + re := regexp.MustCompile(`(?m-s)` + tc.pattern) + tags = append(tags, re.FindAllString(string(out), -1)...) + } + return tags, nil +} + +// tagSync syncs the repo in dir to the named tag, +// which either is a tag returned by tags or is v.tagDefault. +func (v *vcsCmd) tagSync(dir, tag string) error { + if v.tagSyncCmd == "" { + return nil + } + return v.run(dir, v.tagSyncCmd, "tag", tag) +} + +// A vcsPath describes how to convert an import path into a +// version control system and repository name. +type vcsPath struct { + prefix string // prefix this description applies to + re string // pattern for import path + 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 + + regexp *regexp.Regexp // cached compiled form of re +} + +// vcsForImportPath analyzes importPath to determine the +// version control system, and code repository to use. +// On return, repo is the repository URL and root is the +// import path corresponding to the root of the repository +// (thus root is a prefix of importPath). +func vcsForImportPath(importPath string) (vcs *vcsCmd, repo, root string, err error) { + for _, srv := range vcsPaths { + if !strings.HasPrefix(importPath, srv.prefix) { + continue + } + m := srv.regexp.FindStringSubmatch(importPath) + if m == nil { + if srv.prefix != "" { + return nil, "", "", fmt.Errorf("invalid %s import path %q", srv.prefix, importPath) + } + continue + } + + // Build map of named subexpression matches for expand. + match := map[string]string{ + "prefix": srv.prefix, + "import": importPath, + } + for i, name := range srv.regexp.SubexpNames() { + if name != "" && match[name] == "" { + match[name] = m[i] + } + } + if srv.vcs != "" { + match["vcs"] = expand(match, srv.vcs) + } + if srv.repo != "" { + match["repo"] = expand(match, srv.repo) + } + if srv.check != nil { + if err := srv.check(match); err != nil { + return nil, "", "", err + } + } + vcs := vcsByCmd(match["vcs"]) + if vcs == nil { + return nil, "", "", fmt.Errorf("unknown version control system %q", match["vcs"]) + } + return vcs, match["repo"], match["root"], nil + } + return nil, "", "", fmt.Errorf("unrecognized import path %q", importPath) +} + +// expand rewrites s to replace {k} with match[k] for each key k in match. +func expand(match map[string]string, s string) string { + for k, v := range match { + s = strings.Replace(s, "{"+k+"}", v, -1) + } + return s +} + +// vcsPaths lists the known vcs paths. +var vcsPaths = []*vcsPath{ + // Google Code - new syntax + { + prefix: "code.google.com/", + re: `^(?P<root>code\.google\.com/p/(?P<project>[a-z0-9\-]+)(\.(?P<subrepo>[a-z0-9\-]+))?)(/[A-Za-z0-9_.\-]+)*$`, + repo: "https://{root}", + check: googleCodeVCS, + }, + + // Google Code - old syntax + { + re: `^(?P<project>[a-z0-9_\-.]+)\.googlecode\.com/(git|hg|svn)(?P<path>/.*)?$`, + check: oldGoogleCode, + }, + + // Github + { + prefix: "github.com/", + re: `^(?P<root>github\.com/[A-Za-z0-9_.\-]+/[A-Za-z0-9_.\-]+)(/[A-Za-z0-9_.\-]+)*$`, + vcs: "git", + repo: "https://{root}", + check: noVCSSuffix, + }, + + // Bitbucket + { + prefix: "bitbucket.org/", + re: `^(?P<root>bitbucket\.org/(?P<bitname>[A-Za-z0-9_.\-]+/[A-Za-z0-9_.\-]+))(/[A-Za-z0-9_.\-]+)*$`, + repo: "https://{root}", + check: bitbucketVCS, + }, + + // Launchpad + { + prefix: "launchpad.net/", + re: `^(?P<root>launchpad\.net/((?P<project>[A-Za-z0-9_.\-]+)(?P<series>/[A-Za-z0-9_.\-]+)?|~[A-Za-z0-9_.\-]+/(\+junk|[A-Za-z0-9_.\-]+)/[A-Za-z0-9_.\-]+))(/[A-Za-z0-9_.\-]+)*$`, + vcs: "bzr", + repo: "https://{root}", + check: launchpadVCS, + }, + + // 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_.\-]+)*$`, + }, +} + +func init() { + // fill in cached regexps. + // Doing this eagerly discovers invalid regexp syntax + // without having to run a command that needs that regexp. + for _, srv := range vcsPaths { + srv.regexp = regexp.MustCompile(srv.re) + } +} + +// noVCSSuffix checks that the repository name does not +// end in .foo for any version control system foo. +// The usual culprit is ".git". +func noVCSSuffix(match map[string]string) error { + repo := match["repo"] + for _, vcs := range vcsList { + if strings.HasSuffix(repo, "."+vcs.cmd) { + return fmt.Errorf("invalid version control suffix in %s path", match["prefix"]) + } + } + return nil +} + +var googleCheckout = regexp.MustCompile(`id="checkoutcmd">(hg|git|svn)`) + +// googleCodeVCS determines the version control system for +// a code.google.com repository, by scraping the project's +// /source/checkout page. +func googleCodeVCS(match map[string]string) error { + if err := noVCSSuffix(match); err != nil { + return err + } + data, err := httpGET(expand(match, "https://code.google.com/p/{project}/source/checkout?repo={subrepo}")) + if err != nil { + return err + } + + if m := googleCheckout.FindSubmatch(data); m != nil { + if vcs := vcsByCmd(string(m[1])); vcs != nil { + // Subversion requires the old URLs. + // TODO: Test. + if vcs == vcsSvn { + if match["subrepo"] != "" { + return fmt.Errorf("sub-repositories not supported in Google Code Subversion projects") + } + match["repo"] = expand(match, "https://{project}.googlecode.com/svn") + } + match["vcs"] = vcs.cmd + return nil + } + } + + return fmt.Errorf("unable to detect version control system for code.google.com/ path") +} + +// oldGoogleCode is invoked for old-style foo.googlecode.com paths. +// It prints an error giving the equivalent new path. +func oldGoogleCode(match map[string]string) error { + return fmt.Errorf("invalid Google Code import path: use %s instead", + expand(match, "code.google.com/p/{project}{path}")) +} + +// bitbucketVCS determines the version control system for a +// BitBucket repository, by using the BitBucket API. +func bitbucketVCS(match map[string]string) error { + if err := noVCSSuffix(match); err != nil { + return err + } + + var resp struct { + SCM string `json:"scm"` + } + url := expand(match, "https://api.bitbucket.org/1.0/repositories/{bitname}") + data, err := httpGET(url) + if err != nil { + return err + } + if err := json.Unmarshal(data, &resp); err != nil { + return fmt.Errorf("decoding %s: %v", url, err) + } + + if vcsByCmd(resp.SCM) != nil { + match["vcs"] = resp.SCM + if resp.SCM == "git" { + match["repo"] += ".git" + } + return nil + } + + return fmt.Errorf("unable to detect version control system for bitbucket.org/ path") +} + +// launchpadVCS solves the ambiguity for "lp.net/project/foo". In this case, +// "foo" could be a series name registered in Launchpad with its own branch, +// and it could also be the name of a directory within the main project +// branch one level up. +func launchpadVCS(match map[string]string) error { + if match["project"] == "" || match["series"] == "" { + return nil + } + _, err := httpGET(expand(match, "https://code.launchpad.net/{project}{series}/.bzr/branch-format")) + if err != nil { + match["root"] = expand(match, "launchpad.net/{project}") + match["repo"] = expand(match, "https://{root}") + } + return nil +} diff --git a/src/cmd/go/version.go b/src/cmd/go/version.go new file mode 100644 index 000000000..09e2f1633 --- /dev/null +++ b/src/cmd/go/version.go @@ -0,0 +1,25 @@ +// 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" + "runtime" +) + +var cmdVersion = &Command{ + Run: runVersion, + UsageLine: "version", + Short: "print Go version", + Long: `Version prints the Go version, as reported by runtime.Version.`, +} + +func runVersion(cmd *Command, args []string) { + if len(args) != 0 { + cmd.Usage() + } + + fmt.Printf("go version %s\n", runtime.Version()) +} diff --git a/src/cmd/go/vet.go b/src/cmd/go/vet.go new file mode 100644 index 000000000..52c320032 --- /dev/null +++ b/src/cmd/go/vet.go @@ -0,0 +1,30 @@ +// 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 + +var cmdVet = &Command{ + Run: runVet, + UsageLine: "vet [importpath...]", + Short: "run govet on packages", + Long: ` +Vet runs the govet command on the packages named by the import paths. + +For more about govet, see 'godoc govet'. +For more about import paths, see 'go help importpath'. + +To run govet with specific options, run govet itself. + +See also: go fmt, go fix. + `, +} + +func runVet(cmd *Command, args []string) { + for _, pkg := range packages(args) { + // 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)) + } +} |