diff options
Diffstat (limited to 'src/cmd/go')
42 files changed, 7677 insertions, 0 deletions
diff --git a/src/cmd/go/bootstrap.go b/src/cmd/go/bootstrap.go new file mode 100644 index 000000000..32941404c --- /dev/null +++ b/src/cmd/go/bootstrap.go @@ -0,0 +1,30 @@ +// 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" + "io" +) + +var errHTTP = errors.New("no http in bootstrap go command") + +func httpGET(url string) ([]byte, error) { + return nil, errHTTP +} + +func httpsOrHTTP(importPath string) (string, io.ReadCloser, error) { + return "", nil, errHTTP +} + +func parseMetaGoImports(r io.Reader) (imports []metaImport) { + panic("unreachable") +} diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go new file mode 100644 index 000000000..4bb83f161 --- /dev/null +++ b/src/cmd/go/build.go @@ -0,0 +1,1566 @@ +// 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" + "log" + "os" + "os/exec" + "path" + "path/filepath" + "regexp" + "runtime" + "strings" + "sync" + "time" +) + +var cmdBuild = &Command{ + UsageLine: "build [-o output] [build flags] [packages]", + 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. +Otherwise build compiles the packages but discards the results, +serving only as a check that the packages can be built. + +The -o flag specifies the output file name. If not specified, the +name is packagename.a (for a non-main package) or the base +name of the first source file (for a main package). + +The build flags are shared by the build, install, run, and test commands: + + -a + force rebuilding of packages that are already up-to-date. + -n + print the commands but do not run them. + -p n + the number of builds that can be run in parallel. + The default is the number of CPUs available. + -v + print the names of packages as they are compiled. + -work + print the name of the temporary work directory and + do not delete it when exiting. + -x + print the commands. + + -compiler name + name of compiler to use, as in runtime.Compiler (gccgo or gc) + -gccgoflags 'arg list' + arguments to pass on each gccgo compiler/linker invocation + -gcflags 'arg list' + arguments to pass on each 5g, 6g, or 8g compiler invocation + -ldflags 'flag list' + arguments to pass on each 5l, 6l, or 8l linker invocation + -tags 'tag list' + a list of build tags to consider satisfied during the build. + See the documentation for the go/build package for + more information about build tags. + +For more about specifying packages, see 'go help packages'. +For more about where packages and binaries are installed, +see 'go help gopath'. + +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 buildWork bool // -work flag +var buildGcflags []string // -gcflags flag +var buildLdflags []string // -ldflags flag +var buildGccgoflags []string // -gccgoflags flag + +var buildContext = build.Default +var buildToolchain toolchain = noToolchain{} + +// buildCompiler implements flag.Var. +// It implements Set by updating both +// buildToolchain and buildContext.Compiler. +type buildCompiler struct{} + +func (c buildCompiler) Set(value string) error { + switch value { + case "gc": + buildToolchain = gcToolchain{} + case "gccgo": + buildToolchain = gccgcToolchain{} + default: + return fmt.Errorf("unknown compiler %q", value) + } + buildContext.Compiler = value + return nil +} + +func (c buildCompiler) String() string { + return buildContext.Compiler +} + +func init() { + switch build.Default.Compiler { + case "gc": + buildToolchain = gcToolchain{} + case "gccgo": + buildToolchain = gccgcToolchain{} + } +} + +// addBuildFlags adds the flags common to the build and install commands. +func addBuildFlags(cmd *Command) { + // NOTE: If you add flags here, also add them to testflag.go. + 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, "") + cmd.Flag.BoolVar(&buildWork, "work", false, "") + cmd.Flag.Var((*stringsFlag)(&buildGcflags), "gcflags", "") + cmd.Flag.Var((*stringsFlag)(&buildLdflags), "ldflags", "") + cmd.Flag.Var((*stringsFlag)(&buildGccgoflags), "gccgoflags", "") + cmd.Flag.Var((*stringsFlag)(&buildContext.BuildTags), "tags", "") + cmd.Flag.Var(buildCompiler{}, "compiler", "") +} + +type stringsFlag []string + +func (v *stringsFlag) Set(s string) error { + *v = strings.Fields(s) + return nil +} + +func (v *stringsFlag) String() string { + return "<stringsFlag>" +} + +func runBuild(cmd *Command, args []string) { + var b builder + b.init() + + pkgs := packagesForBuild(args) + + if len(pkgs) == 1 && pkgs[0].Name == "main" && *buildO == "" { + _, *buildO = path.Split(pkgs[0].ImportPath) + *buildO += exeSuffix + } + + 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 [build flags] [packages]", + Short: "compile and install packages and dependencies", + Long: ` +Install compiles and installs the packages named by the import paths, +along with their dependencies. + +For more about the build flags, see 'go help build'. +For more about specifying packages, see 'go help packages'. + +See also: go build, go get, go clean. + `, +} + +func runInstall(cmd *Command, args []string) { + pkgs := packagesForBuild(args) + + for _, p := range pkgs { + if p.Target == "" && (!p.Standard || p.ImportPath != "unsafe") { + errorf("go install: no install location for %s", p.ImportPath) + } + } + exitIfErrors() + + var b builder + b.init() + a := &action{} + for _, p := range pkgs { + a.deps = append(a.deps, b.action(modeInstall, modeInstall, p)) + } + b.do(a) +} + +// Global build parameters (used during package load) +var ( + goarch string + goos string + archChar string + exeSuffix string +) + +func init() { + goarch = buildContext.GOARCH + goos = buildContext.GOOS + if goos == "windows" { + exeSuffix = ".exe" + } + var err error + archChar, err = build.ArchChar(goarch) + if err != nil { + fatalf("%s", err) + } +} + +// A builder holds global state about a build. +// It does not hold per-package state, because we +// build packages in parallel, and the builder is shared. +type builder struct { + work string // the temporary work directory (ends in filepath.Separator) + actionCache map[cacheKey]*action // a cache of already-constructed actions + mkdirCache map[string]bool // a cache of created directories + print func(args ...interface{}) (int, error) + + output sync.Mutex + scriptDir string // current directory in printed script + + 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 +) + +var ( + goroot = filepath.Clean(runtime.GOROOT()) + gobin = os.Getenv("GOBIN") + gorootBin = filepath.Join(goroot, "bin") + gorootSrcPkg = filepath.Join(goroot, "src/pkg") + gorootPkg = filepath.Join(goroot, "pkg") + gorootSrc = filepath.Join(goroot, "src") +) + +func (b *builder) init() { + var err error + b.print = fmt.Print + b.actionCache = make(map[cacheKey]*action) + b.mkdirCache = make(map[string]bool) + + if buildN { + b.work = "$WORK" + } else { + b.work, err = ioutil.TempDir("", "go-build") + if err != nil { + fatalf("%s", err) + } + if buildX || buildWork { + fmt.Printf("WORK=%s\n", b.work) + } + if !buildWork { + atexit(func() { os.RemoveAll(b.work) }) + } + } +} + +// goFilesPackage creates a package for building a collection of Go files +// (typically named on the command line). The target is named p.a for +// package p or named after the first Go file for package main. +func goFilesPackage(gofiles []string) *Package { + // TODO: Remove this restriction. + for _, f := range gofiles { + if !strings.HasSuffix(f, ".go") { + fatalf("named files must be .go files") + } + } + + var stk importStack + ctxt := buildContext + ctxt.UseAllFiles = true + + // Synthesize fake "directory" that only shows the named files, + // to make it look like this is a standard package or + // command directory. So that local imports resolve + // consistently, the files must all be in the same directory. + var dirent []os.FileInfo + var dir string + 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) + } + dir1, _ := filepath.Split(file) + if dir == "" { + dir = dir1 + } else if dir != dir1 { + fatalf("named files must all be in one directory; have %s and %s", dir, dir1) + } + dirent = append(dirent, fi) + } + ctxt.ReadDir = func(string) ([]os.FileInfo, error) { return dirent, nil } + + if !filepath.IsAbs(dir) { + dir = filepath.Join(cwd, dir) + } + + bp, err := ctxt.ImportDir(dir, 0) + pkg := new(Package) + pkg.local = true + pkg.load(&stk, bp, err) + pkg.localPrefix = dirToImportPath(dir) + pkg.ImportPath = "command-line-arguments" + pkg.target = "" + + if pkg.Name == "main" { + _, elem := filepath.Split(gofiles[0]) + exe := elem[:len(elem)-len(".go")] + exeSuffix + if *buildO == "" { + *buildO = exe + } + if gobin != "" { + pkg.target = filepath.Join(gobin, exe) + } + } else { + if *buildO == "" { + *buildO = pkg.Name + ".a" + } + } + pkg.Target = pkg.target + pkg.Stale = true + + computeStale(pkg) + 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.build.PkgRoot} + 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 we are not doing a cross-build, then record the binary we'll + // generate for cgo as a dependency of the build of any package + // using cgo, to make sure we do not overwrite the binary while + // a package is using it. If this is a cross-build, then the cgo we + // are writing is not the cgo we need to use. + if goos == runtime.GOOS && goarch == runtime.GOARCH { + if len(p.CgoFiles) > 0 || p.Standard && p.ImportPath == "runtime/cgo" { + var stk importStack + p1 := loadPackage("cmd/cgo", &stk) + if p1.Error != nil { + fatalf("load cmd/cgo: %v", p1.Error) + } + a.cgo = b.action(depMode, depMode, p1) + a.deps = append(a.deps, a.cgo) + } + } + + if p.Standard { + switch p.ImportPath { + case "builtin", "unsafe": + // Fake packages - nothing to build. + return a + } + // gccgo standard library is "fake" too. + if _, ok := buildToolchain.(gccgcToolchain); ok { + // the target name is needed for cgo. + a.target = p.target + return a + } + } + + if !p.Stale && 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 + } + + if p.local && p.target == "" { + // Imported via local path. No permanent target. + mode = modeBuild + } + a.objdir = filepath.Join(b.work, a.p.ImportPath, "_obj") + string(filepath.Separator) + a.objpkg = buildToolchain.pkgpath(b.work, a.p) + 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. + // (This is the name of a temporary file.) + a.target = a.objdir + "a.out" + exeSuffix + } + } + + 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 { + setExitStatus(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) (err error) { + defer func() { + if err != nil && err != errPrintedOutput { + err = fmt.Errorf("go build %s: %v", a.p.ImportPath, err) + } + }() + 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 + } + + cgoExe := tool("cgo") + if a.cgo != nil && a.cgo.target != "" { + cgoExe = a.cgo.target + } + outGo, outObj, err := b.cgo(a.p, cgoExe, obj, gccfiles) + if err != nil { + return err + } + cgoObjects = append(cgoObjects, outObj...) + gofiles = append(gofiles, outGo...) + } + + // Prepare Go import path list. + inc := b.includeArgs("-I", a.deps) + + // Compile Go. + if len(gofiles) > 0 { + if out, err := buildToolchain.gc(b, a.p, obj, inc, gofiles); err != nil { + return err + } else { + 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 := "_" + goos + "_" + goarch + ".h" + _goos := "_" + goos + ".h" + _goarch := "_" + 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(a, 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(a, 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(a, obj+targ, filepath.Join(a.p.Dir, file), 0666); err != nil { + return err + } + } + } + + for _, file := range cfiles { + out := file[:len(file)-len(".c")] + "." + archChar + if err := buildToolchain.cc(b, 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")] + "." + archChar + if err := buildToolchain.asm(b, 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...) + + // Add system object files. + for _, syso := range a.p.SysoFiles { + objects = append(objects, filepath.Join(a.p.Dir, syso)) + } + + // Pack into archive in obj directory + if err := buildToolchain.pack(b, 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 + if err := buildToolchain.ld(b, a.p, a.target, all, a.objpkg, objects); err != nil { + return err + } + } + + return nil +} + +// install is the action for installing a single package or executable. +func (b *builder) install(a *action) (err error) { + defer func() { + if err != nil && err != errPrintedOutput { + err = fmt.Errorf("go install %s: %v", a.p.ImportPath, err) + } + }() + 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 + } + } + + // remove object dir to keep the amount of + // garbage down in a large build. On an operating system + // with aggressive buffering, cleaning incrementally like + // this keeps the intermediate objects from hitting the disk. + if !buildWork { + defer os.RemoveAll(a1.objdir) + defer os.Remove(a1.target) + } + + return b.copyFile(a, 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 + gorootPkg: true, + "": 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.build.PkgRoot && !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.build.PkgRoot && !incMap[dir] { + incMap[dir] = true + if _, ok := buildToolchain.(gccgcToolchain); ok { + dir = filepath.Join(dir, "gccgo") + } else { + dir = filepath.Join(dir, goos+"_"+goarch) + } + inc = append(inc, flag, dir) + } + } + + return inc +} + +// copyFile is like 'cp src dst'. +func (b *builder) copyFile(a *action, 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() + + // Be careful about removing/overwriting dst. + // Do not remove/overwrite if dst exists and is a directory + // or a non-object file. + if fi, err := os.Stat(dst); err == nil { + if fi.IsDir() { + return fmt.Errorf("build output %q already exists and is a directory", dst) + } + if !isObject(dst) { + return fmt.Errorf("build output %q already exists and is not an object file", dst) + } + } + + // On Windows, remove lingering ~ file from last attempt. + if toolIsWindows { + if _, err := os.Stat(dst + "~"); err == nil { + os.Remove(dst + "~") + } + } + + os.Remove(dst) + df, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm) + if err != nil && toolIsWindows { + // Windows does not allow deletion of a binary file + // while it is executing. Try to move it out of the way. + // If the remove fails, which is likely, we'll try again the + // next time we do an install of this binary. + if err := os.Rename(dst, dst+"~"); err == nil { + os.Remove(dst + "~") + } + df, err = os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm) + } + if err != nil { + return err + } + + _, err = io.Copy(df, sf) + df.Close() + if err != nil { + os.Remove(dst) + return fmt.Errorf("copying %s to %s: %v", src, dst, err) + } + return nil +} + +var objectMagic = [][]byte{ + {'!', '<', 'a', 'r', 'c', 'h', '>', '\n'}, // Package archive + {'\x7F', 'E', 'L', 'F'}, // ELF + {0xFE, 0xED, 0xFA, 0xCE}, // Mach-O big-endian 32-bit + {0xFE, 0xED, 0xFA, 0xCF}, // Mach-O big-endian 64-bit + {0xCE, 0xFA, 0xED, 0xFE}, // Mach-O little-endian 32-bit + {0xCF, 0xFA, 0xED, 0xFE}, // Mach-O little-endian 64-bit + {0x4d, 0x5a, 0x90, 0x00, 0x03, 0x00, 0x04, 0x00}, // PE (Windows) as generated by 6l/8l +} + +func isObject(s string) bool { + f, err := os.Open(s) + if err != nil { + return false + } + defer f.Close() + buf := make([]byte, 64) + io.ReadFull(f, buf) + for _, magic := range objectMagic { + if bytes.HasPrefix(buf, magic) { + return true + } + } + return false +} + +// fmtcmd formats a command in the manner of fmt.Sprintf but also: +// +// If dir is non-empty and the script is not in dir right now, +// fmtcmd inserts "cd dir\n" before the command. +// +// fmtcmd replaces the value of b.work with $WORK. +// fmtcmd replaces the value of 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 != "" && dir != "/" { + cmd = strings.Replace(" "+cmd, " "+dir, " .", -1)[1:] + if b.scriptDir != dir { + b.scriptDir = dir + cmd = "cd " + dir + "\n" + cmd + } + } + if b.work != "" { + cmd = strings.Replace(cmd, b.work, "$WORK", -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() + b.print(b.fmtcmd(dir, format, args...) + "\n") +} + +// 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 + if reldir := shortPath(dir); reldir != 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() + b.print(prefix, suffix) +} + +// shortPath returns an absolute or relative name for path, whatever is shorter. +func shortPath(path string) string { + if rel, err := filepath.Rel(cwd, path); err == nil && len(rel) < len(path) { + return rel + } + return path +} + +// 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 command fails, run prints information about the failure +// and returns a non-nil error. +func (b *builder) run(dir string, desc string, cmdargs ...interface{}) error { + out, err := b.runOut(dir, desc, cmdargs...) + if len(out) > 0 { + if out[len(out)-1] != '\n' { + out = append(out, '\n') + } + if desc == "" { + desc = b.fmtcmd(dir, "%s", strings.Join(stringList(cmdargs...), " ")) + } + b.showOutput(dir, desc, string(out)) + 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 + } + } + + nbusy := 0 + for { + 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() + + // cmd.Run will fail on Unix if some other process has the binary + // we want to run open for writing. This can happen here because + // we build and install the cgo command and then run it. + // If another command was kicked off while we were writing the + // cgo binary, the child process for that command may be holding + // a reference to the fd, keeping us from running exec. + // + // But, you might reasonably wonder, how can this happen? + // The cgo fd, like all our fds, is close-on-exec, so that we need + // not worry about other processes inheriting the fd accidentally. + // The answer is that running a command is fork and exec. + // A child forked while the cgo fd is open inherits that fd. + // Until the child has called exec, it holds the fd open and the + // kernel will not let us run cgo. Even if the child were to close + // the fd explicitly, it would still be open from the time of the fork + // until the time of the explicit close, and the race would remain. + // + // On Unix systems, this results in ETXTBSY, which formats + // as "text file busy". Rather than hard-code specific error cases, + // we just look for that string. If this happens, sleep a little + // and try again. We let this happen three times, with increasing + // sleep lengths: 100+200+400 ms = 0.7 seconds. + // + // An alternate solution might be to split the cmd.Run into + // separate cmd.Start and cmd.Wait, and then use an RWLock + // to make sure that copyFile only executes when no cmd.Start + // call is in progress. However, cmd.Start (really syscall.forkExec) + // only guarantees that when it returns, the exec is committed to + // happen and succeed. It uses a close-on-exec file descriptor + // itself to determine this, so we know that when cmd.Start returns, + // at least one close-on-exec file descriptor has been closed. + // However, we cannot be sure that all of them have been closed, + // so the program might still encounter ETXTBSY even with such + // an RWLock. The race window would be smaller, perhaps, but not + // guaranteed to be gone. + // + // Sleeping when we observe the race seems to be the most reliable + // option we have. + // + // http://golang.org/issue/3001 + // + if err != nil && nbusy < 3 && strings.Contains(err.Error(), "text file busy") { + time.Sleep(100 * time.Millisecond << uint(nbusy)) + nbusy++ + continue + } + + return buf.Bytes(), err + } + panic("unreachable") +} + +// 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) +} + +type toolchain interface { + // gc runs the compiler in a specific directory on a set of files + // and returns the name of the generated output file. + // The compiler runs in the directory dir. + gc(b *builder, p *Package, obj string, importArgs []string, gofiles []string) (ofile string, err error) + // cc runs the toolchain's C compiler in a directory on a C file + // to produce an output file. + cc(b *builder, p *Package, objdir, ofile, cfile string) error + // asm runs the assembler in a specific directory on a specific file + // to generate the named output file. + asm(b *builder, p *Package, obj, ofile, sfile string) error + // pkgpath builds an appropriate path for a temporary package file. + pkgpath(basedir string, p *Package) string + // pack runs the archive packer in a specific directory to create + // an archive from a set of object files. + // typically it is run in the object directory. + pack(b *builder, p *Package, objDir, afile string, ofiles []string) error + // ld runs the linker to create a package starting at mainpkg. + ld(b *builder, p *Package, out string, allactions []*action, mainpkg string, ofiles []string) error + + compiler() string + linker() string +} + +type noToolchain struct{} + +func noCompiler() error { + log.Fatalf("unknown compiler %q", buildContext.Compiler) + return nil +} + +func (noToolchain) compiler() string { + noCompiler() + return "" +} + +func (noToolchain) linker() string { + noCompiler() + return "" +} + +func (noToolchain) gc(b *builder, p *Package, obj string, importArgs []string, gofiles []string) (ofile string, err error) { + return "", noCompiler() +} + +func (noToolchain) asm(b *builder, p *Package, obj, ofile, sfile string) error { + return noCompiler() +} + +func (noToolchain) pkgpath(basedir string, p *Package) string { + noCompiler() + return "" +} + +func (noToolchain) pack(b *builder, p *Package, objDir, afile string, ofiles []string) error { + return noCompiler() +} + +func (noToolchain) ld(b *builder, p *Package, out string, allactions []*action, mainpkg string, ofiles []string) error { + return noCompiler() +} + +func (noToolchain) cc(b *builder, p *Package, objdir, ofile, cfile string) error { + return noCompiler() +} + +// The Go toolchain. +type gcToolchain struct{} + +func (gcToolchain) compiler() string { + return tool(archChar + "g") +} + +func (gcToolchain) linker() string { + return tool(archChar + "l") +} + +func (gcToolchain) gc(b *builder, p *Package, obj string, importArgs []string, gofiles []string) (ofile string, err error) { + out := "_go_." + archChar + ofile = obj + out + gcargs := []string{"-p", p.ImportPath} + if p.Standard && p.ImportPath == "runtime" { + // runtime compiles with a special 6g flag to emit + // additional reflect type data. + gcargs = append(gcargs, "-+") + } + + args := stringList(tool(archChar+"g"), "-o", ofile, buildGcflags, gcargs, "-D", p.localPrefix, importArgs) + for _, f := range gofiles { + args = append(args, mkAbs(p.Dir, f)) + } + return ofile, b.run(p.Dir, p.ImportPath, args) +} + +func (gcToolchain) asm(b *builder, p *Package, obj, ofile, sfile string) error { + sfile = mkAbs(p.Dir, sfile) + return b.run(p.Dir, p.ImportPath, tool(archChar+"a"), "-I", obj, "-o", ofile, "-DGOOS_"+goos, "-DGOARCH_"+goarch, sfile) +} + +func (gcToolchain) pkgpath(basedir string, p *Package) string { + end := filepath.FromSlash(p.ImportPath + ".a") + return filepath.Join(basedir, end) +} + +func (gcToolchain) pack(b *builder, p *Package, objDir, afile string, ofiles []string) error { + var absOfiles []string + for _, f := range ofiles { + absOfiles = append(absOfiles, mkAbs(objDir, f)) + } + return b.run(p.Dir, p.ImportPath, tool("pack"), "grc", mkAbs(objDir, afile), absOfiles) +} + +func (gcToolchain) ld(b *builder, p *Package, out string, allactions []*action, mainpkg string, ofiles []string) error { + importArgs := b.includeArgs("-L", allactions) + return b.run(".", p.ImportPath, tool(archChar+"l"), "-o", out, importArgs, buildLdflags, mainpkg) +} + +func (gcToolchain) cc(b *builder, p *Package, objdir, ofile, cfile string) error { + inc := filepath.Join(goroot, "pkg", fmt.Sprintf("%s_%s", goos, goarch)) + cfile = mkAbs(p.Dir, cfile) + return b.run(p.Dir, p.ImportPath, tool(archChar+"c"), "-FVw", + "-I", objdir, "-I", inc, "-o", ofile, + "-DGOOS_"+goos, "-DGOARCH_"+goarch, cfile) +} + +// The Gccgo toolchain. +type gccgcToolchain struct{} + +var gccgoBin, _ = exec.LookPath("gccgo") + +func (gccgcToolchain) compiler() string { + return gccgoBin +} + +func (gccgcToolchain) linker() string { + return gccgoBin +} + +func (gccgcToolchain) gc(b *builder, p *Package, obj string, importArgs []string, gofiles []string) (ofile string, err error) { + out := p.Name + ".o" + ofile = obj + out + gcargs := []string{"-g"} + if prefix := gccgoPrefix(p); prefix != "" { + gcargs = append(gcargs, "-fgo-prefix="+gccgoPrefix(p)) + } + args := stringList("gccgo", importArgs, "-c", gcargs, "-o", ofile, buildGccgoflags) + for _, f := range gofiles { + args = append(args, mkAbs(p.Dir, f)) + } + return ofile, b.run(p.Dir, p.ImportPath, args) +} + +func (gccgcToolchain) asm(b *builder, p *Package, obj, ofile, sfile string) error { + sfile = mkAbs(p.Dir, sfile) + return b.run(p.Dir, p.ImportPath, "gccgo", "-I", obj, "-o", ofile, "-DGOOS_"+goos, "-DGOARCH_"+goarch, sfile) +} + +func (gccgcToolchain) pkgpath(basedir string, p *Package) string { + end := filepath.FromSlash(p.ImportPath + ".a") + afile := filepath.Join(basedir, end) + // add "lib" to the final element + return filepath.Join(filepath.Dir(afile), "lib"+filepath.Base(afile)) +} + +func (gccgcToolchain) pack(b *builder, p *Package, objDir, afile string, ofiles []string) error { + var absOfiles []string + for _, f := range ofiles { + absOfiles = append(absOfiles, mkAbs(objDir, f)) + } + return b.run(p.Dir, p.ImportPath, "ar", "cru", mkAbs(objDir, afile), absOfiles) +} + +func (tools gccgcToolchain) ld(b *builder, p *Package, out string, allactions []*action, mainpkg string, ofiles []string) error { + // gccgo needs explicit linking with all package dependencies, + // and all LDFLAGS from cgo dependencies. + afiles := make(map[*Package]string) + ldflags := []string{} + cgoldflags := []string{} + for _, a := range allactions { + if a.p != nil { + if !a.p.Standard { + if afiles[a.p] == "" || a.objpkg != a.target { + afiles[a.p] = a.target + } + } + cgoldflags = append(cgoldflags, a.p.CgoLDFLAGS...) + } + } + for _, afile := range afiles { + ldflags = append(ldflags, afile) + } + ldflags = append(ldflags, cgoldflags...) + return b.run(".", p.ImportPath, "gccgo", "-o", out, buildGccgoflags, ofiles, "-Wl,-(", ldflags, "-Wl,-)") +} + +func (gccgcToolchain) cc(b *builder, p *Package, objdir, ofile, cfile string) error { + inc := filepath.Join(goroot, "pkg", fmt.Sprintf("%s_%s", goos, goarch)) + cfile = mkAbs(p.Dir, cfile) + return b.run(p.Dir, p.ImportPath, "gcc", "-Wall", "-g", + "-I", objdir, "-I", inc, "-o", ofile, + "-DGOOS_"+goos, "-DGOARCH_"+goarch, "-c", cfile) +} + +func gccgoPrefix(p *Package) string { + switch { + case p.build.IsCommand() && !p.forceLibrary: + return "" + case p.fake: + return "fake_" + p.ImportPath + } + return "go_" + p.ImportPath +} + +// 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 { + // NOTE: env.go's mkEnv knows that the first three + // strings returned are "gcc", "-I", objdir (and cuts them off). + + // TODO: HOST_CC? + a := []string{"gcc", "-I", objdir, "-g", "-O2"} + + // Definitely want -fPIC but on Windows gcc complains + // "-fPIC ignored for target (all code is position independent)" + if goos != "windows" { + a = append(a, "-fPIC") + } + switch archChar { + case "8": + a = append(a, "-m32") + case "6": + a = append(a, "-m64") + } + // gcc-4.5 and beyond require explicit "-pthread" flag + // for multithreading with pthread library. + if buildContext.CgoEnabled { + switch goos { + case "windows": + a = append(a, "-mthreads") + default: + a = append(a, "-pthread") + } + } + + // On OS X, some of the compilers behave as if -fno-common + // is always set, and the Mach-O linker in 6l/8l assumes this. + // See http://golang.org/issue/3253. + if goos == "darwin" { + a = append(a, "-fno-common") + } + + return a +} + +func envList(key string) []string { + return strings.Fields(os.Getenv(key)) +} + +var cgoRe = regexp.MustCompile(`[/\\:]`) + +func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo, outObj []string, err error) { + if goos != toolGOOS { + return nil, nil, errors.New("cannot use cgo when compiling for a different operating system") + } + + cgoCFLAGS := stringList(envList("CGO_CFLAGS"), p.CgoCFLAGS) + cgoLDFLAGS := stringList(envList("CGO_LDFLAGS"), p.CgoLDFLAGS) + + if pkgs := p.CgoPkgConfig; len(pkgs) > 0 { + out, err := b.runOut(p.Dir, p.ImportPath, "pkg-config", "--cflags", pkgs) + if err != nil { + b.showOutput(p.Dir, "pkg-config --cflags "+strings.Join(pkgs, " "), string(out)) + b.print(err.Error() + "\n") + return nil, nil, errPrintedOutput + } + if len(out) > 0 { + cgoCFLAGS = append(cgoCFLAGS, strings.Fields(string(out))...) + } + out, err = b.runOut(p.Dir, p.ImportPath, "pkg-config", "--libs", pkgs) + if err != nil { + b.showOutput(p.Dir, "pkg-config --libs "+strings.Join(pkgs, " "), string(out)) + b.print(err.Error() + "\n") + return nil, nil, errPrintedOutput + } + if len(out) > 0 { + cgoLDFLAGS = append(cgoLDFLAGS, strings.Fields(string(out))...) + } + } + + // Allows including _cgo_export.h from .[ch] files in the package. + cgoCFLAGS = append(cgoCFLAGS, "-I", obj) + + // cgo + // TODO: CGOPKGPATH, CGO_FLAGS? + gofiles := []string{obj + "_cgo_gotypes.go"} + 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" + + cgoflags := []string{} + // TODO: make cgo not depend on $GOARCH? + + objExt := archChar + + if p.Standard && p.ImportPath == "runtime/cgo" { + cgoflags = append(cgoflags, "-import_runtime_cgo=false") + } + if _, ok := buildToolchain.(gccgcToolchain); ok { + cgoflags = append(cgoflags, "-gccgo") + if prefix := gccgoPrefix(p); prefix != "" { + cgoflags = append(cgoflags, "-gccgoprefix="+gccgoPrefix(p)) + } + objExt = "o" + } + if err := b.run(p.Dir, p.ImportPath, cgoExe, "-objdir", obj, cgoflags, "--", cgoCFLAGS, p.CgoFiles); err != nil { + return nil, nil, err + } + outGo = append(outGo, gofiles...) + + // cc _cgo_defun.c + defunObj := obj + "_cgo_defun." + objExt + if err := buildToolchain.cc(b, 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 + } + + if _, ok := buildToolchain.(gccgcToolchain); ok { + // we don't use dynimport when using gccgo. + return outGo, outObj, nil + } + + // cgo -dynimport + importC := obj + "_cgo_import.c" + if err := b.run(p.Dir, p.ImportPath, cgoExe, "-objdir", obj, "-dynimport", dynobj, "-dynout", importC); err != nil { + return nil, nil, err + } + + // cc _cgo_import.ARCH + importObj := obj + "_cgo_import." + objExt + if err := buildToolchain.cc(b, p, obj, importObj, importC); err != nil { + return nil, nil, err + } + + // NOTE(rsc): The importObj is a 5c/6c/8c object and on Windows + // must be processed before the gcc-generated objects. + // Put it first. http://golang.org/issue/2601 + outObj = append([]string{importObj}, outObj...) + + 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/clean.go b/src/cmd/go/clean.go new file mode 100644 index 000000000..773951826 --- /dev/null +++ b/src/cmd/go/clean.go @@ -0,0 +1,199 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strings" +) + +var cmdClean = &Command{ + UsageLine: "clean [-i] [-r] [-n] [-x] [packages]", + Short: "remove object files", + Long: ` +Clean removes object files from package source directories. +The go command builds most objects in a temporary directory, +so go clean is mainly concerned with object files left by other +tools or by manual invocations of go build. + +Specifically, clean removes the following files from each of the +source directories corresponding to the import paths: + + _obj/ old object directory, left from Makefiles + _test/ old test directory, left from Makefiles + _testmain.go old gotest file, left from Makefiles + test.out old test log, left from Makefiles + build.out old test log, left from Makefiles + *.[568ao] object files, left from Makefiles + + DIR(.exe) from go build + DIR.test(.exe) from go test -c + MAINFILE(.exe) from go build MAINFILE.go + +In the list, DIR represents the final path element of the +directory, and MAINFILE is the base name of any Go source +file in the directory that is not included when building +the package. + +The -i flag causes clean to remove the corresponding installed +archive or binary (what 'go install' would create). + +The -n flag causes clean to print the remove commands it would execute, +but not run them. + +The -r flag causes clean to be applied recursively to all the +dependencies of the packages named by the import paths. + +The -x flag causes clean to print remove commands as it executes them. + +For more about specifying packages, see 'go help packages'. + `, +} + +var cleanI bool // clean -i flag +var cleanN bool // clean -n flag +var cleanR bool // clean -r flag +var cleanX bool // clean -x flag + +func init() { + // break init cycle + cmdClean.Run = runClean + + cmdClean.Flag.BoolVar(&cleanI, "i", false, "") + cmdClean.Flag.BoolVar(&cleanN, "n", false, "") + cmdClean.Flag.BoolVar(&cleanR, "r", false, "") + cmdClean.Flag.BoolVar(&cleanX, "x", false, "") +} + +func runClean(cmd *Command, args []string) { + for _, pkg := range packagesAndErrors(args) { + clean(pkg) + } +} + +var cleaned = map[*Package]bool{} + +// TODO: These are dregs left by Makefile-based builds. +// Eventually, can stop deleting these. +var cleanDir = map[string]bool{ + "_test": true, + "_obj": true, +} + +var cleanFile = map[string]bool{ + "_testmain.go": true, + "test.out": true, + "build.out": true, + "a.out": true, +} + +var cleanExt = map[string]bool{ + ".5": true, + ".6": true, + ".8": true, + ".a": true, + ".o": true, +} + +func clean(p *Package) { + if cleaned[p] { + return + } + if p.Dir == "" { + errorf("can't load package: %v", p.Error) + return + } + dirs, err := ioutil.ReadDir(p.Dir) + if err != nil { + errorf("go clean %s: %v", p.Dir, err) + return + } + + var b builder + b.print = fmt.Print + + packageFile := map[string]bool{} + if p.Name != "main" { + // Record which files are not in package main. + // The others are. + keep := func(list []string) { + for _, f := range list { + packageFile[f] = true + } + } + keep(p.GoFiles) + keep(p.CgoFiles) + keep(p.TestGoFiles) + keep(p.XTestGoFiles) + } + + _, elem := filepath.Split(p.Dir) + allRemove := []string{ + elem, + elem + ".exe", + elem + ".test", + elem + ".test.exe", + } + for _, dir := range dirs { + name := dir.Name() + if packageFile[name] { + continue + } + if !dir.IsDir() && strings.HasSuffix(name, ".go") { + base := name[:len(name)-len(".go")] + allRemove = append(allRemove, base, base+".exe") + } + } + if cleanN || cleanX { + b.showcmd(p.Dir, "rm -f %s", strings.Join(allRemove, " ")) + } + + toRemove := map[string]bool{} + for _, name := range allRemove { + toRemove[name] = true + } + for _, dir := range dirs { + name := dir.Name() + if dir.IsDir() { + // TODO: Remove once Makefiles are forgotten. + if cleanDir[name] { + if cleanN || cleanX { + b.showcmd(p.Dir, "rm -r %s", name) + if cleanN { + continue + } + } + os.RemoveAll(filepath.Join(p.Dir, name)) + } + continue + } + + if cleanN { + continue + } + + if cleanFile[name] || cleanExt[filepath.Ext(name)] || toRemove[name] { + os.Remove(filepath.Join(p.Dir, name)) + } + } + + if cleanI && p.target != "" { + if cleanN || cleanX { + b.showcmd("", "rm -f %s", p.target) + } + if !cleanN { + os.Remove(p.target) + } + } + + if cleanR { + for _, p1 := range p.imports { + clean(p1) + } + } +} diff --git a/src/cmd/go/discovery.go b/src/cmd/go/discovery.go new file mode 100644 index 000000000..d9f930867 --- /dev/null +++ b/src/cmd/go/discovery.go @@ -0,0 +1,63 @@ +// 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 ( + "encoding/xml" + "io" + "strings" +) + +// parseMetaGoImports returns meta imports from the HTML in r. +// Parsing ends at the end of the <head> section or the beginning of the <body>. +func parseMetaGoImports(r io.Reader) (imports []metaImport) { + d := xml.NewDecoder(r) + d.Strict = false + for { + t, err := d.Token() + if err != nil { + return + } + if e, ok := t.(xml.StartElement); ok && strings.EqualFold(e.Name.Local, "body") { + return + } + if e, ok := t.(xml.EndElement); ok && strings.EqualFold(e.Name.Local, "head") { + return + } + e, ok := t.(xml.StartElement) + if !ok || !strings.EqualFold(e.Name.Local, "meta") { + continue + } + if attrValue(e.Attr, "name") != "go-import" { + continue + } + if f := strings.Fields(attrValue(e.Attr, "content")); len(f) == 3 { + imports = append(imports, metaImport{ + Prefix: f[0], + VCS: f[1], + RepoRoot: f[2], + }) + } + } + return +} + +// attrValue returns the attribute value for the case-insensitive key +// `name', or the empty string if nothing is found. +func attrValue(attrs []xml.Attr, name string) string { + for _, a := range attrs { + if strings.EqualFold(a.Name.Local, name) { + return a.Value + } + } + return "" +} diff --git a/src/cmd/go/doc.go b/src/cmd/go/doc.go new file mode 100644 index 000000000..4bfd5236d --- /dev/null +++ b/src/cmd/go/doc.go @@ -0,0 +1,769 @@ +// 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 + clean remove object files + doc run godoc on package sources + env print Go environment information + fix run go tool fix on packages + fmt run gofmt on package sources + get download and install packages and dependencies + install compile and install packages and dependencies + list list packages + run compile and run Go program + test test packages + tool run specified go tool + version print Go version + vet run go tool vet on packages + +Use "go help [command]" for more information about a command. + +Additional help topics: + + gopath GOPATH environment variable + packages description of package lists + 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 [-o output] [build flags] [packages] + +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. +Otherwise build compiles the packages but discards the results, +serving only as a check that the packages can be built. + +The -o flag specifies the output file name. If not specified, the +name is packagename.a (for a non-main package) or the base +name of the first source file (for a main package). + +The build flags are shared by the build, install, run, and test commands: + + -a + force rebuilding of packages that are already up-to-date. + -n + print the commands but do not run them. + -p n + the number of builds that can be run in parallel. + The default is the number of CPUs available. + -v + print the names of packages as they are compiled. + -work + print the name of the temporary work directory and + do not delete it when exiting. + -x + print the commands. + + -compiler name + name of compiler to use, as in runtime.Compiler (gccgo or gc) + -gccgoflags 'arg list' + arguments to pass on each gccgo compiler/linker invocation + -gcflags 'arg list' + arguments to pass on each 5g, 6g, or 8g compiler invocation + -ldflags 'flag list' + arguments to pass on each 5l, 6l, or 8l linker invocation + -tags 'tag list' + a list of build tags to consider satisfied during the build. + See the documentation for the go/build package for + more information about build tags. + +For more about specifying packages, see 'go help packages'. +For more about where packages and binaries are installed, +see 'go help gopath'. + +See also: go install, go get, go clean. + + +Remove object files + +Usage: + + go clean [-i] [-r] [-n] [-x] [packages] + +Clean removes object files from package source directories. +The go command builds most objects in a temporary directory, +so go clean is mainly concerned with object files left by other +tools or by manual invocations of go build. + +Specifically, clean removes the following files from each of the +source directories corresponding to the import paths: + + _obj/ old object directory, left from Makefiles + _test/ old test directory, left from Makefiles + _testmain.go old gotest file, left from Makefiles + test.out old test log, left from Makefiles + build.out old test log, left from Makefiles + *.[568ao] object files, left from Makefiles + + DIR(.exe) from go build + DIR.test(.exe) from go test -c + MAINFILE(.exe) from go build MAINFILE.go + +In the list, DIR represents the final path element of the +directory, and MAINFILE is the base name of any Go source +file in the directory that is not included when building +the package. + +The -i flag causes clean to remove the corresponding installed +archive or binary (what 'go install' would create). + +The -n flag causes clean to print the remove commands it would execute, +but not run them. + +The -r flag causes clean to be applied recursively to all the +dependencies of the packages named by the import paths. + +The -x flag causes clean to print remove commands as it executes them. + +For more about specifying packages, see 'go help packages'. + + +Run godoc on package sources + +Usage: + + go doc [packages] + +Doc runs the godoc command on the packages named by the +import paths. + +For more about godoc, see 'godoc godoc'. +For more about specifying packages, see 'go help packages'. + +To run godoc with specific options, run godoc itself. + +See also: go fix, go fmt, go vet. + + +Print Go environment information + +Usage: + + go env [var ...] + +Env prints Go environment information. + +By default env prints information as a shell script +(on Windows, a batch file). If one or more variable +names is given as arguments, env prints the value of +each named variable on its own line. + + +Run go tool fix on packages + +Usage: + + go fix [packages] + +Fix runs the Go fix command on the packages named by the import paths. + +For more about fix, see 'godoc fix'. +For more about specifying packages, see 'go help packages'. + +To run fix with specific options, run 'go tool fix'. + +See also: go fmt, go vet. + + +Run gofmt on package sources + +Usage: + + go fmt [packages] + +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 specifying packages, see 'go help packages'. + +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] [packages] + +Get downloads and installs the packages named by the import paths, +along with their dependencies. + +The -a, -n, -v, -x, and -p flags have the same meaning as in 'go build' +and 'go install'. See 'go help build'. + +The -d flag instructs get to stop after downloading the packages; that is, +it instructs get not to install the packages. + +The -fix flag instructs get to run the fix tool on the downloaded packages +before resolving dependencies or building the code. + +The -u flag instructs get to use the network to update the named packages +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. + +When checking out or updating a package, get looks for a branch or +tag that matches the locally installed version of Go. If the local +version "is release.rNN", it searches for "go.rNN". (For an +installation using Go version "weekly.YYYY-MM-DD", it searches for a +package version labeled "go.YYYY-MM-DD".) If the desired version +cannot be found but others exist with labels in the correct format, +get retrieves the most recent version before the desired label. +Finally, if all else fails it retrieves the most recent version of +the package. + +For more about specifying packages, see 'go help packages'. + +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 [build flags] [packages] + +Install compiles and installs the packages named by the import paths, +along with their dependencies. + +For more about the build flags, see 'go help build'. +For more about specifying packages, see 'go help packages'. + +See also: go build, go get, go clean. + + +List packages + +Usage: + + go list [-e] [-f format] [-json] [packages] + +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 { + Dir string // directory containing package sources + ImportPath string // import path of package in dir + Name string // package name + Doc string // package documentation string + Target string // install path + Goroot bool // is this package in the Go root? + Standard bool // is this package part of the standard Go library? + Stale bool // would 'go install' do anything for this package? + Root string // Go root or Go path dir containing this package + + // Source files + GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles) + CgoFiles []string // .go sources files that import "C" + CFiles []string // .c source files + HFiles []string // .h source files + SFiles []string // .s source files + SysoFiles []string // .syso object files to add to archive + + // Cgo directives + CgoCFLAGS []string // cgo: flags for C compiler + CgoLDFLAGS []string // cgo: flags for linker + CgoPkgConfig []string // cgo: pkg-config names + + // 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 + + TestGoFiles []string // _test.go files in package + TestImports []string // imports from TestGoFiles + XTestGoFiles []string // _test.go files outside package + XTestImports []string // imports from XTestGoFiles + } + +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 specifying packages, see 'go help packages'. + + +Compile and run Go program + +Usage: + + go run [build flags] gofiles... [arguments...] + +Run compiles and runs the main package comprising the named Go source files. + +For more about build flags, see 'go help build'. + +See also: go build. + + +Test packages + +Usage: + + go test [-c] [-i] [build flags] [packages] [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. + +The package is built in a temporary directory so it does not interfere with the +non-test installation. + +In addition to the build flags, the flags handled by 'go test' itself are: + + -c Compile the test binary to pkg.test but do not run it. + + -i + Install packages that are dependencies of the test. + Do not run the test. + +The test binary also accepts flags that control execution of the test; these +flags are also accessible by 'go test'. See 'go help testflag' for details. + +For more about build flags, see 'go help build'. +For more about specifying packages, see 'go help packages'. + +See also: go build, go vet. + + +Run specified go tool + +Usage: + + go tool [-n] command [args...] + +Tool runs the go tool command identified by the arguments. +With no arguments it prints the list of known tools. + +The -n flag causes tool to print the command that would be +executed but not execute it. + +For more about each tool command, see 'go tool command -h'. + + +Print Go version + +Usage: + + go version + +Version prints the Go version, as reported by runtime.Version. + + +Run go tool vet on packages + +Usage: + + go vet [packages] + +Vet runs the Go vet command on the packages named by the import paths. + +For more about vet, see 'godoc vet'. +For more about specifying packages, see 'go help packages'. + +To run the vet tool with specific options, run 'go tool vet'. + +See also: go fmt, go fix. + + +GOPATH environment variable + +The Go path is used to resolve import statements. +It is implemented by and documented in the go/build package. + +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. If the GOBIN environment variable is +set, commands are installed to the directory it names instead +of DIR/bin. + +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 package lists + +Many commands apply to a set of packages: + + go action [packages] + +Usually, [packages] is a list of import paths. + +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. As a special case, x/... matches x as well as x's subdirectories. +For example, net/... expands to net and packages in its subdirectories. + +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'. + +As a special case, if the package list is a list of .go files from a +single directory, the command is applied to a single synthesized +package made up of exactly those files, ignoring any build constraints +in those files and ignoring any other files in the directory. + + +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, import paths may either be qualified +with the version control type, or the go tool can dynamically fetch +the import path over https/http and discover where the code resides +from a <meta> tag in the HTML. + +To declare the code location, 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://. + +If the import path is not a known code hosting site and also lacks a +version control qualifier, the go tool attempts to fetch the import +over https/http and looks for a <meta> tag in the document's HTML +<head>. + +The meta tag has the form: + + <meta name="go-import" content="import-prefix vcs repo-root"> + +The import-prefix is the import path correponding to the repository +root. It must be a prefix or an exact match of the package being +fetched with "go get". If it's not an exact match, another http +request is made at the prefix to verify the <meta> tags match. + +The vcs is one of "git", "hg", "svn", etc, + +The repo-root is the root of the version control system +containing a scheme and not containing a .vcs qualifier. + +For example, + + import "example.org/pkg/foo" + +will result in the following request(s): + + https://example.org/pkg/foo?go-get=1 (preferred) + http://example.org/pkg/foo?go-get=1 (fallback) + +If that page contains the meta tag + + <meta name="go-import" content="example.org git https://code.org/r/p/exproj"> + +the go tool will verify that https://example.org/?go-get=1 contains the +same meta tag and then git clone https://code.org/r/p/exproj into +GOPATH/src/example.org. + +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 test binary, called pkg.test, where pkg is the name of the +directory containing the package sources, has its own flags: + + -test.v + Verbose output: log all tests as they are run. + + -test.run pattern + Run only those tests and examples 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 + +will compile the test binary and then run it as + + pkg.test -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 "Output:" comment, which +must be the last comment in the function body (see example below). An +example with no such comment, or with no text after "Output:" is compiled +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: + + func ExamplePrintln() { + Println("The output of\nthis example.") + // Output: The output of + // this example. + } + +The entire test file is presented as the example when it contains a single +example function, at least one other function, type, variable, or constant +declaration, and no test or benchmark functions. + +See the documentation of the testing package for more information. + + +*/ +package documentation + +// NOTE: cmdDoc is in fmt.go. diff --git a/src/cmd/go/env.go b/src/cmd/go/env.go new file mode 100644 index 000000000..d5b034809 --- /dev/null +++ b/src/cmd/go/env.go @@ -0,0 +1,89 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "runtime" + "strings" +) + +var cmdEnv = &Command{ + Run: runEnv, + UsageLine: "env [var ...]", + Short: "print Go environment information", + Long: ` +Env prints Go environment information. + +By default env prints information as a shell script +(on Windows, a batch file). If one or more variable +names is given as arguments, env prints the value of +each named variable on its own line. + `, +} + +type envVar struct { + name, value string +} + +func mkEnv() []envVar { + var b builder + b.init() + + env := []envVar{ + {"GOROOT", goroot}, + {"GOBIN", gobin}, + {"GOARCH", goarch}, + {"GOCHAR", archChar}, + {"GOOS", goos}, + {"GOEXE", exeSuffix}, + {"GOHOSTARCH", runtime.GOARCH}, + {"GOHOSTOS", runtime.GOOS}, + {"GOTOOLDIR", toolDir}, + {"GOGCCFLAGS", strings.Join(b.gccCmd(".")[3:], " ")}, + } + + if buildContext.CgoEnabled { + env = append(env, envVar{"CGO_ENABLED", "1"}) + } else { + env = append(env, envVar{"CGO_ENABLED", "0"}) + } + + return env +} + +func findEnv(env []envVar, name string) string { + for _, e := range env { + if e.name == name { + return e.value + } + } + return "" +} + +func runEnv(cmd *Command, args []string) { + env := mkEnv() + if len(args) > 0 { + for _, name := range args { + fmt.Printf("%s\n", findEnv(env, name)) + } + return + } + + switch runtime.GOOS { + default: + for _, e := range env { + fmt.Printf("%s=\"%s\"\n", e.name, e.value) + } + case "plan9": + for _, e := range env { + fmt.Printf("%s='%s'\n", e.name, strings.Replace(e.value, "'", "''", -1)) + } + case "windows": + for _, e := range env { + fmt.Printf("set %s=%s\n", e.name, e.value) + } + } +} diff --git a/src/cmd/go/fix.go b/src/cmd/go/fix.go new file mode 100644 index 000000000..ef02b5739 --- /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 [packages]", + Short: "run go tool fix on packages", + Long: ` +Fix runs the Go fix command on the packages named by the import paths. + +For more about fix, see 'godoc fix'. +For more about specifying packages, see 'go help packages'. + +To run fix with specific options, run 'go tool fix'. + +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(tool("fix"), relPaths(pkg.gofiles))) + } +} diff --git a/src/cmd/go/fmt.go b/src/cmd/go/fmt.go new file mode 100644 index 000000000..cea9b0a51 --- /dev/null +++ b/src/cmd/go/fmt.go @@ -0,0 +1,58 @@ +// 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 [packages]", + 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 specifying packages, see 'go help packages'. + +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 [packages]", + 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 specifying packages, see 'go help packages'. + +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) { + if pkg.ImportPath == "command-line arguments" { + errorf("go doc: cannot use package file list") + continue + } + run("godoc", pkg.Dir) + } +} diff --git a/src/cmd/go/get.go b/src/cmd/go/get.go new file mode 100644 index 000000000..f70b6761d --- /dev/null +++ b/src/cmd/go/get.go @@ -0,0 +1,428 @@ +// 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" + "regexp" + "runtime" + "strconv" + "strings" +) + +var cmdGet = &Command{ + UsageLine: "get [-a] [-d] [-fix] [-n] [-p n] [-u] [-v] [-x] [packages]", + Short: "download and install packages and dependencies", + Long: ` +Get downloads and installs the packages named by the import paths, +along with their dependencies. + +The -a, -n, -v, -x, and -p flags have the same meaning as in 'go build' +and 'go install'. See 'go help build'. + +The -d flag instructs get to stop after downloading the packages; that is, +it instructs get not to install the packages. + +The -fix flag instructs get to run the fix tool on the downloaded packages +before resolving dependencies or building the code. + +The -u flag instructs get to use the network to update the named packages +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. + +When checking out or updating a package, get looks for a branch or +tag that matches the locally installed version of Go. If the local +version "is release.rNN", it searches for "go.rNN". (For an +installation using Go version "weekly.YYYY-MM-DD", it searches for a +package version labeled "go.YYYY-MM-DD".) If the desired version +cannot be found but others exist with labels in the correct format, +get retrieves the most recent version before the desired label. +Finally, if all else fails it retrieves the most recent version of +the package. + +For more about specifying packages, see 'go help packages'. + +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. + var stk importStack + for _, arg := range downloadPaths(args) { + download(arg, &stk) + } + exitIfErrors() + + // Phase 2. Rescan packages and reevaluate args list. + + // 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) + } + + args = importPaths(args) + + // Phase 3. Install. + if *getD { + // Download only. + // Check delayed until now so that importPaths + // has a chance to print errors. + return + } + + runInstall(cmd, args) +} + +// downloadPath prepares the list of paths to pass to download. +// It expands ... patterns that can be expanded. If there is no match +// for a particular pattern, downloadPaths leaves it in the result list, +// in the hope that we can figure out the repository from the +// initial ...-free prefix. +func downloadPaths(args []string) []string { + args = importPathsNoDotExpansion(args) + var out []string + for _, a := range args { + if strings.Contains(a, "...") { + var expand []string + // Use matchPackagesInFS to avoid printing + // warnings. They will be printed by the + // eventual call to importPaths instead. + if build.IsLocalImport(a) { + expand = matchPackagesInFS(a) + } else { + expand = matchPackages(a) + } + if len(expand) > 0 { + out = append(out, expand...) + continue + } + } + out = append(out, a) + } + return out +} + +// 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 + + pkgs := []*Package{p} + wildcardOkay := len(*stk) == 0 + + // Download if the package is missing, or update if we're using -u. + if p.Dir == "" || *getU { + // The actual download. + stk.push(p.ImportPath) + err := downloadPackage(p) + if err != nil { + errorf("%s", &PackageError{ImportStack: stk.copy(), Err: err.Error()}) + stk.pop() + return + } + + args := []string{arg} + // If the argument has a wildcard in it, re-evaluate the wildcard. + // We delay this until after reloadPackage so that the old entry + // for p has been replaced in the package cache. + if wildcardOkay && strings.Contains(arg, "...") { + if build.IsLocalImport(arg) { + args = matchPackagesInFS(arg) + } else { + args = matchPackages(arg) + } + } + + // Clear all relevant package cache entries before + // doing any new loads. + for _, arg := range args { + p := packageCache[arg] + if p != nil { + delete(packageCache, p.Dir) + delete(packageCache, p.ImportPath) + } + } + + pkgs = pkgs[:0] + for _, arg := range args { + stk.push(arg) + p := loadPackage(arg, stk) + stk.pop() + if p.Error != nil { + errorf("%s", p.Error) + continue + } + pkgs = append(pkgs, p) + } + } + + // Process package, which might now be multiple packages + // due to wildcard expansion. + for _, p := range pkgs { + if *getFix { + run(stringList(tool("fix"), 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 { + var ( + vcs *vcsCmd + repo, rootPath string + err error + ) + if p.build.SrcRoot != "" { + // Directory exists. Look for checkout along path to src. + vcs, rootPath, err = vcsForDir(p) + if err != nil { + return err + } + repo = "<local>" // should be unused; make distinctive + } else { + // Analyze the import path to determine the version control system, + // repository, and the import path for the root of the repository. + rr, err := repoRootForImportPath(p.ImportPath) + if err != nil { + return err + } + vcs, repo, rootPath = rr.vcs, rr.repo, rr.root + } + + if p.build.SrcRoot == "" { + // Package not found. Put in first directory of $GOPATH or else $GOROOT. + // Guard against people setting GOPATH=$GOROOT. We have to use + // $GOROOT's directory hierarchy (src/pkg, not just src) in that case. + if list := filepath.SplitList(buildContext.GOPATH); len(list) > 0 && list[0] != goroot { + p.build.SrcRoot = filepath.Join(list[0], "src") + p.build.PkgRoot = filepath.Join(list[0], "pkg") + } else { + p.build.SrcRoot = filepath.Join(goroot, "src", "pkg") + p.build.PkgRoot = filepath.Join(goroot, "pkg") + } + } + root := filepath.Join(p.build.SrcRoot, 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 + } + } + + if buildN { + // Do not show tag sync in -n; it's noise more than anything, + // and since we're not running commands, no tag will be found. + // But avoid printing nothing. + fmt.Fprintf(os.Stderr, "# cd %s; %s sync/update\n", root, vcs.cmd) + return nil + } + + // 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] + } + if err := vcs.tagSync(root, selectTag(vers, tags)); err != nil { + return err + } + + return nil +} + +// goTag matches go release tags such as go1 and go1.2.3. +// The numbers involved must be small (at most 4 digits), +// have no unnecessary leading zeros, and the version cannot +// end in .0 - it is go1, not go1.0 or go1.0.0. +var goTag = regexp.MustCompile( + `^go((0|[1-9][0-9]{0,3})\.)*([1-9][0-9]{0,3})$`, +) + +// selectTag returns the closest matching tag for a given version. +// Closest means the latest one that is not after the current release. +// Version "goX" (or "goX.Y" or "goX.Y.Z") matches tags of the same form. +// Version "release.rN" matches tags of the form "go.rN" (N being a floating-point number). +// 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 + } + } + } + + if goTag.MatchString(goVersion) { + v := goVersion + for _, t := range tags { + if !goTag.MatchString(t) { + continue + } + if cmpGoVersion(match, t) < 0 && cmpGoVersion(t, v) <= 0 { + match = t + } + } + } + + return match +} + +// cmpGoVersion returns -1, 0, +1 reporting whether +// x < y, x == y, or x > y. +func cmpGoVersion(x, y string) int { + // Malformed strings compare less than well-formed strings. + if !goTag.MatchString(x) { + return -1 + } + if !goTag.MatchString(y) { + return +1 + } + + // Compare numbers in sequence. + xx := strings.Split(x[len("go"):], ".") + yy := strings.Split(y[len("go"):], ".") + + for i := 0; i < len(xx) && i < len(yy); i++ { + // The Atoi are guaranteed to succeed + // because the versions match goTag. + xi, _ := strconv.Atoi(xx[i]) + yi, _ := strconv.Atoi(yy[i]) + if xi < yi { + return -1 + } else if xi > yi { + return +1 + } + } + + if len(xx) < len(yy) { + return -1 + } + if len(xx) > len(yy) { + return +1 + } + return 0 +} diff --git a/src/cmd/go/help.go b/src/cmd/go/help.go new file mode 100644 index 000000000..47ea0c711 --- /dev/null +++ b/src/cmd/go/help.go @@ -0,0 +1,238 @@ +// 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 helpPackages = &Command{ + UsageLine: "packages", + Short: "description of package lists", + Long: ` +Many commands apply to a set of packages: + + go action [packages] + +Usually, [packages] is a list of import paths. + +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. As a special case, x/... matches x as well as x's subdirectories. +For example, net/... expands to net and packages in its subdirectories. + +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'. + +As a special case, if the package list is a list of .go files from a +single directory, the command is applied to a single synthesized +package made up of exactly those files, ignoring any build constraints +in those files and ignoring any other files in the directory. + `, +} + +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, import paths may either be qualified +with the version control type, or the go tool can dynamically fetch +the import path over https/http and discover where the code resides +from a <meta> tag in the HTML. + +To declare the code location, 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://. + +If the import path is not a known code hosting site and also lacks a +version control qualifier, the go tool attempts to fetch the import +over https/http and looks for a <meta> tag in the document's HTML +<head>. + +The meta tag has the form: + + <meta name="go-import" content="import-prefix vcs repo-root"> + +The import-prefix is the import path correponding to the repository +root. It must be a prefix or an exact match of the package being +fetched with "go get". If it's not an exact match, another http +request is made at the prefix to verify the <meta> tags match. + +The vcs is one of "git", "hg", "svn", etc, + +The repo-root is the root of the version control system +containing a scheme and not containing a .vcs qualifier. + +For example, + + import "example.org/pkg/foo" + +will result in the following request(s): + + https://example.org/pkg/foo?go-get=1 (preferred) + http://example.org/pkg/foo?go-get=1 (fallback) + +If that page contains the meta tag + + <meta name="go-import" content="example.org git https://code.org/r/p/exproj"> + +the go tool will verify that https://example.org/?go-get=1 contains the +same meta tag and then git clone https://code.org/r/p/exproj into +GOPATH/src/example.org. + +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 Go path is used to resolve import statements. +It is implemented by and documented in the go/build package. + +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. If the GOBIN environment variable is +set, commands are installed to the directory it names instead +of DIR/bin. + +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..6de9a3e1e --- /dev/null +++ b/src/cmd/go/http.go @@ -0,0 +1,87 @@ +// 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" + "io/ioutil" + "log" + "net/http" + "net/url" +) + +// 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 +} + +// httpClient is the default HTTP client, but a variable so it can be +// changed by tests, without modifying http.DefaultClient. +var httpClient = http.DefaultClient + +// httpsOrHTTP returns the body of either the importPath's +// https resource or, if unavailable, the http resource. +func httpsOrHTTP(importPath string) (urlStr string, body io.ReadCloser, err error) { + fetch := func(scheme string) (urlStr string, res *http.Response, err error) { + u, err := url.Parse(scheme + "://" + importPath) + if err != nil { + return "", nil, err + } + u.RawQuery = "go-get=1" + urlStr = u.String() + if buildV { + log.Printf("Fetching %s", urlStr) + } + res, err = httpClient.Get(urlStr) + return + } + closeBody := func(res *http.Response) { + if res != nil { + res.Body.Close() + } + } + urlStr, res, err := fetch("https") + if err != nil || res.StatusCode != 200 { + if buildV { + if err != nil { + log.Printf("https fetch failed.") + } else { + log.Printf("ignoring https fetch with status code %d", res.StatusCode) + } + } + closeBody(res) + urlStr, res, err = fetch("http") + } + if err != nil { + closeBody(res) + return "", nil, err + } + // Note: accepting a non-200 OK here, so people can serve a + // meta import in their http 404 page. + if buildV { + log.Printf("Parsing meta tags from %s (status code %d)", urlStr, res.StatusCode) + } + return urlStr, res.Body, nil +} diff --git a/src/cmd/go/list.go b/src/cmd/go/list.go new file mode 100644 index 000000000..edb59aa79 --- /dev/null +++ b/src/cmd/go/list.go @@ -0,0 +1,168 @@ +// 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 ( + "bufio" + "encoding/json" + "io" + "os" + "text/template" +) + +var cmdList = &Command{ + UsageLine: "list [-e] [-f format] [-json] [packages]", + 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 { + Dir string // directory containing package sources + ImportPath string // import path of package in dir + Name string // package name + Doc string // package documentation string + Target string // install path + Goroot bool // is this package in the Go root? + Standard bool // is this package part of the standard Go library? + Stale bool // would 'go install' do anything for this package? + Root string // Go root or Go path dir containing this package + + // Source files + GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles) + CgoFiles []string // .go sources files that import "C" + CFiles []string // .c source files + HFiles []string // .h source files + SFiles []string // .s source files + SysoFiles []string // .syso object files to add to archive + + // Cgo directives + CgoCFLAGS []string // cgo: flags for C compiler + CgoLDFLAGS []string // cgo: flags for linker + CgoPkgConfig []string // cgo: pkg-config names + + // 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 + + TestGoFiles []string // _test.go files in package + TestImports []string // imports from TestGoFiles + XTestGoFiles []string // _test.go files outside package + XTestImports []string // imports from XTestGoFiles + } + +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 specifying packages, see 'go help packages'. + `, +} + +func init() { + cmdList.Run = runList // break init cycle + cmdList.Flag.Var(buildCompiler{}, "compiler", "") +} + +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) { + out := newCountingWriter(os.Stdout) + defer out.w.Flush() + + var do func(*Package) + if *listJson { + do = func(p *Package) { + b, err := json.MarshalIndent(p, "", "\t") + if err != nil { + out.Flush() + fatalf("%s", err) + } + out.Write(b) + out.Write(nl) + } + } else { + tmpl, err := template.New("main").Parse(*listFmt) + if err != nil { + fatalf("%s", err) + } + do = func(p *Package) { + out.Reset() + if err := tmpl.Execute(out, p); err != nil { + out.Flush() + fatalf("%s", err) + } + if out.Count() > 0 { + out.w.WriteRune('\n') + } + } + } + + load := packages + if *listE { + load = packagesAndErrors + } + + for _, pkg := range load(args) { + do(pkg) + } +} + +// CountingWriter counts its data, so we can avoid appending a newline +// if there was no actual output. +type CountingWriter struct { + w *bufio.Writer + count int64 +} + +func newCountingWriter(w io.Writer) *CountingWriter { + return &CountingWriter{ + w: bufio.NewWriter(w), + } +} + +func (cw *CountingWriter) Write(p []byte) (n int, err error) { + cw.count += int64(len(p)) + return cw.w.Write(p) +} + +func (cw *CountingWriter) Flush() { + cw.w.Flush() +} + +func (cw *CountingWriter) Reset() { + cw.count = 0 +} + +func (cw *CountingWriter) Count() int64 { + return cw.count +} diff --git a/src/cmd/go/main.go b/src/cmd/go/main.go new file mode 100644 index 000000000..73c2f54a7 --- /dev/null +++ b/src/cmd/go/main.go @@ -0,0 +1,541 @@ +// 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" + "runtime" + "strings" + "sync" + "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) +} + +// Runnable reports whether the command can be run; otherwise +// it is a documentation pseudo-command such as importpath. +func (c *Command) Runnable() bool { + return c.Run != nil +} + +// Commands lists the available commands and help topics. +// The order here is the order in which they are printed by 'go help'. +var commands = []*Command{ + cmdBuild, + cmdClean, + cmdDoc, + cmdEnv, + cmdFix, + cmdFmt, + cmdGet, + cmdInstall, + cmdList, + cmdRun, + cmdTest, + cmdTool, + cmdVersion, + cmdVet, + + helpGopath, + helpPackages, + helpRemote, + helpTestflag, + helpTestfunc, +} + +var exitStatus = 0 +var exitMu sync.Mutex + +func setExitStatus(n int) { + exitMu.Lock() + if exitStatus < n { + exitStatus = n + } + exitMu.Unlock() +} + +func main() { + flag.Usage = usage + flag.Parse() + log.SetFlags(0) + + args := flag.Args() + if len(args) < 1 { + usage() + } + + if args[0] == "help" { + help(args[1:]) + return + } + + // Diagnose common mistake: GOPATH==GOROOT. + // This setting is equivalent to not setting GOPATH at all, + // which is not what most people want when they do it. + if gopath := os.Getenv("GOPATH"); gopath == runtime.GOROOT() { + fmt.Fprintf(os.Stderr, "warning: GOPATH set to GOROOT (%s) has no effect\n", gopath) + } + + 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 .Runnable}} + {{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}} + +Use "go help [command]" for more information about a command. + +Additional help topics: +{{range .}}{{if not .Runnable}} + {{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}} + +Use "go help [topic]" for more information about that topic. + +` + +var helpTemplate = `{{if .Runnable}}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 .Runnable}}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' +} + +// importPathsNoDotExpansion returns the import paths to use for the given +// command line, but it does no ... expansion. +func importPathsNoDotExpansion(args []string) []string { + if len(args) == 0 { + return []string{"."} + } + var out []string + for _, a := range args { + // Arguments are supposed to be import paths, but + // as a courtesy to Windows developers, rewrite \ to / + // in command-line arguments. Handles .\... and so on. + if filepath.Separator == '\\' { + a = strings.Replace(a, `\`, `/`, -1) + } + + // Put argument in canonical form, but preserve leading ./. + if strings.HasPrefix(a, "./") { + a = "./" + path.Clean(a) + if a == "./." { + a = "." + } + } else { + a = path.Clean(a) + } + if a == "all" || a == "std" { + out = append(out, allPackages(a)...) + continue + } + out = append(out, a) + } + return out +} + +// importPaths returns the import paths to use for the given command line. +func importPaths(args []string) []string { + args = importPathsNoDotExpansion(args) + var out []string + for _, a := range args { + if strings.Contains(a, "...") { + if build.IsLocalImport(a) { + out = append(out, allPackagesInFS(a)...) + } else { + 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...) + setExitStatus(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) + // Special case: foo/... matches foo too. + if strings.HasSuffix(re, `/.*`) { + re = re[:len(re)-len(`/.*`)] + `(/.*)?` + } + 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 { + pkgs := matchPackages(pattern) + if len(pkgs) == 0 { + fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern) + } + return pkgs +} + +func matchPackages(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 + 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() || path == cmd { + 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.ImportDir(path, 0) + if err != nil { + return nil + } + + // We use, e.g., cmd/gofmt as the pseudo import path for gofmt. + name = "cmd/" + name + if !have[name] { + have[name] = true + if match(name) { + pkgs = append(pkgs, name) + } + } + return nil + }) + + for _, src := range buildContext.SrcDirs() { + if pattern == "std" && src != gorootSrcPkg { + continue + } + src = filepath.Clean(src) + string(filepath.Separator) + filepath.Walk(src, func(path string, fi os.FileInfo, err error) error { + if err != nil || !fi.IsDir() || path == src { + 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.ImportDir(path, 0) + if err != nil && strings.Contains(err.Error(), "no Go source files") { + return nil + } + if match(name) { + pkgs = append(pkgs, name) + } + return nil + }) + } + 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 { + pkgs := matchPackagesInFS(pattern) + if len(pkgs) == 0 { + fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern) + } + return pkgs +} + +func matchPackagesInFS(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() || path == dir { + 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.ImportDir(path, 0); err != nil { + return nil + } + pkgs = append(pkgs, name) + return nil + }) + 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 +} diff --git a/src/cmd/go/match_test.go b/src/cmd/go/match_test.go new file mode 100644 index 000000000..f058f235a --- /dev/null +++ b/src/cmd/go/match_test.go @@ -0,0 +1,36 @@ +// 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 "testing" + +var matchTests = []struct { + pattern string + path string + match bool +}{ + {"...", "foo", true}, + {"net", "net", true}, + {"net", "net/http", false}, + {"net/http", "net", false}, + {"net/http", "net/http", true}, + {"net...", "netchan", true}, + {"net...", "net", true}, + {"net...", "net/http", true}, + {"net...", "not/http", false}, + {"net/...", "netchan", false}, + {"net/...", "net", true}, + {"net/...", "net/http", true}, + {"net/...", "not/http", false}, +} + +func TestMatchPattern(t *testing.T) { + for _, tt := range matchTests { + match := matchPattern(tt.pattern)(tt.path) + if match != tt.match { + t.Errorf("matchPattern(%q)(%q) = %v, want %v", tt.pattern, tt.path, match, tt.match) + } + } +} 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..30bbfad55 --- /dev/null +++ b/src/cmd/go/pkg.go @@ -0,0 +1,679 @@ +// 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" + "errors" + "fmt" + "go/build" + "go/scanner" + "go/token" + "os" + pathpkg "path" + "path/filepath" + "sort" + "strings" + "time" + "unicode" +) + +// 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 + Dir string `json:",omitempty"` // directory containing package sources + ImportPath string `json:",omitempty"` // import path of package in dir + Name string `json:",omitempty"` // package name + Doc string `json:",omitempty"` // package documentation string + Target string `json:",omitempty"` // install path + Goroot bool `json:",omitempty"` // is this package found in the Go root? + 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? + Root string `json:",omitempty"` // Go root or Go path dir containing this package + + // Source files + GoFiles []string `json:",omitempty"` // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles) + CgoFiles []string `json:",omitempty"` // .go sources files that import "C" + CFiles []string `json:",omitempty"` // .c source files + HFiles []string `json:",omitempty"` // .h source files + SFiles []string `json:",omitempty"` // .s source files + SysoFiles []string `json:",omitempty"` // .syso system object files added to package + + // Cgo directives + CgoCFLAGS []string `json:",omitempty"` // cgo: flags for C compiler + CgoLDFLAGS []string `json:",omitempty"` // cgo: flags for linker + CgoPkgConfig []string `json:",omitempty"` // cgo: pkg-config names + + // Dependency information + Imports []string `json:",omitempty"` // import paths used by this package + Deps []string `json:",omitempty"` // all (recursively) imported dependencies + + // Error information + Incomplete bool `json:",omitempty"` // was there an error loading this package or dependencies? + Error *PackageError `json:",omitempty"` // error loading this package (not dependencies) + DepsErrors []*PackageError `json:",omitempty"` // errors loading dependencies + + // Test information + TestGoFiles []string `json:",omitempty"` // _test.go files in package + TestImports []string `json:",omitempty"` // imports from TestGoFiles + XTestGoFiles []string `json:",omitempty"` // _test.go files outside package + XTestImports []string `json:",omitempty"` // imports from XTestGoFiles + + // Unexported fields are not part of the public API. + build *build.Package + pkgdir string // overrides build.PkgDir + 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 + forceBuild bool // this package must be rebuilt + forceLibrary bool // this package is a library (even if named "main") + local bool // imported via local path (./ or ../) + localPrefix string // interpret ./ and ../ imports relative to this prefix +} + +func (p *Package) copyBuild(pp *build.Package) { + p.build = pp + + p.Dir = pp.Dir + p.ImportPath = pp.ImportPath + p.Name = pp.Name + p.Doc = pp.Doc + p.Root = pp.Root + // TODO? Target + p.Goroot = pp.Goroot + p.Standard = p.Goroot && p.ImportPath != "" && !strings.Contains(p.ImportPath, ".") + p.GoFiles = pp.GoFiles + p.CgoFiles = pp.CgoFiles + p.CFiles = pp.CFiles + p.HFiles = pp.HFiles + p.SFiles = pp.SFiles + p.SysoFiles = pp.SysoFiles + p.CgoCFLAGS = pp.CgoCFLAGS + p.CgoLDFLAGS = pp.CgoLDFLAGS + p.CgoPkgConfig = pp.CgoPkgConfig + p.Imports = pp.Imports + p.TestGoFiles = pp.TestGoFiles + p.TestImports = pp.TestImports + p.XTestGoFiles = pp.XTestGoFiles + p.XTestImports = pp.XTestImports +} + +// A PackageError describes an error loading information about a package. +type PackageError struct { + ImportStack []string // shortest path from package named on command line to this one + Pos string // position of error + Err string // the error itself +} + +func (p *PackageError) Error() string { + if p.Pos != "" { + // Omit import stack. The full path to the file where the error + // is the most important thing. + return p.Pos + ": " + p.Err + } + return "package " + 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) +} + +// dirToImportPath returns the pseudo-import path we use for a package +// outside the Go path. It begins with _/ and then contains the full path +// to the directory. If the package lives in c:\home\gopher\my\pkg then +// the pseudo-import path is _/c_/home/gopher/my/pkg. +// Using a pseudo-import path like this makes the ./ imports no longer +// a special case, so that all the code to deal with ordinary imports works +// automatically. +func dirToImportPath(dir string) string { + return pathpkg.Join("_", strings.Map(makeImportValid, filepath.ToSlash(dir))) +} + +func makeImportValid(r rune) rune { + // Should match Go spec, compilers, and ../../pkg/go/parser/parser.go:/isValidImport. + const illegalChars = `!"#$%&'()*,:;<=>?[\]^{|}` + "`\uFFFD" + if !unicode.IsGraphic(r) || unicode.IsSpace(r) || strings.ContainsRune(illegalChars, r) { + return '_' + } + return r +} + +// loadImport scans the directory named by path, which must be an import path, +// but possibly a local import path (an absolute file system path or one beginning +// with ./ or ../). A local relative path is interpreted relative to srcDir. +// It returns a *Package describing the package found in that directory. +func loadImport(path string, srcDir string, stk *importStack, importPos []token.Position) *Package { + stk.push(path) + defer stk.pop() + + // Determine canonical identifier for this package. + // For a local import the identifier is the pseudo-import path + // we create from the full directory to the package. + // Otherwise it is the usual import path. + importPath := path + isLocal := build.IsLocalImport(path) + if isLocal { + importPath = dirToImportPath(filepath.Join(srcDir, path)) + } + if p := packageCache[importPath]; p != nil { + return reusePackage(p, stk) + } + + p := new(Package) + p.local = isLocal + p.ImportPath = importPath + packageCache[importPath] = p + + // Load package. + // Import always returns bp != nil, even if an error occurs, + // in order to return partial information. + // + // TODO: After Go 1, decide when to pass build.AllowBinary here. + // See issue 3268 for mistakes to avoid. + bp, err := buildContext.Import(path, srcDir, 0) + bp.ImportPath = importPath + if gobin != "" { + bp.BinDir = gobin + } + p.load(stk, bp, err) + if p.Error != nil && len(importPos) > 0 { + pos := importPos[0] + pos.Filename = shortPath(pos.Filename) + p.Error.Pos = pos.String() + } + + return p +} + +// reusePackage reuses package p to satisfy the import at the top +// of the import stack stk. If this use causes an import loop, +// reusePackage updates p's error information to record the loop. +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 +} + +// isGoTool is the list of directories for Go programs that are installed in +// $GOROOT/pkg/tool. +var isGoTool = map[string]bool{ + "cmd/api": true, + "cmd/cgo": true, + "cmd/fix": true, + "cmd/vet": true, + "cmd/yacc": true, + "exp/gotype": true, + "exp/ebnflint": true, +} + +// expandScanner expands a scanner.List error into all the errors in the list. +// The default Error method only shows the first error. +func expandScanner(err error) error { + // Look for parser errors. + if err, ok := err.(scanner.ErrorList); ok { + // Prepare error with \n before each message. + // When printed in something like context: %v + // this will put the leading file positions each on + // its own line. It will also show all the errors + // instead of just the first, as err.Error does. + var buf bytes.Buffer + for _, e := range err { + e.Pos.Filename = shortPath(e.Pos.Filename) + buf.WriteString("\n") + buf.WriteString(e.Error()) + } + return errors.New(buf.String()) + } + return err +} + +// load populates p using information from bp, err, which should +// be the result of calling build.Context.Import. +func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package { + p.copyBuild(bp) + + // The localPrefix is the path we interpret ./ imports relative to. + // Synthesized main packages sometimes override this. + p.localPrefix = dirToImportPath(p.Dir) + + if err != nil { + p.Incomplete = true + err = expandScanner(err) + p.Error = &PackageError{ + ImportStack: stk.copy(), + Err: err.Error(), + } + return p + } + + if p.Name == "main" { + _, elem := filepath.Split(p.Dir) + full := buildContext.GOOS + "_" + buildContext.GOARCH + "/" + elem + if buildContext.GOOS != toolGOOS || buildContext.GOARCH != toolGOARCH { + // Install cross-compiled binaries to subdirectories of bin. + elem = full + } + p.target = filepath.Join(p.build.BinDir, elem) + if p.Goroot && isGoTool[p.ImportPath] { + p.target = filepath.Join(gorootPkg, "tool", full) + } + if buildContext.GOOS == "windows" { + p.target += ".exe" + } + } else if p.local { + // Local import turned into absolute path. + // No permanent install target. + p.target = "" + } else { + p.target = p.build.PkgObj + } + + importPaths := p.Imports + // Packages that use cgo import runtime/cgo implicitly, + // except runtime/cgo itself. + if len(p.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") + } + + // Build list of full paths to all Go files in the package, + // for use by commands like go fmt. + p.gofiles = stringList(p.GoFiles, p.CgoFiles, p.TestGoFiles, p.XTestGoFiles) + for i := range p.gofiles { + p.gofiles[i] = filepath.Join(p.Dir, p.gofiles[i]) + } + sort.Strings(p.gofiles) + + // Build list of imported packages and full dependency list. + imports := make([]*Package, 0, len(p.Imports)) + deps := make(map[string]bool) + for i, path := range importPaths { + if path == "C" { + continue + } + p1 := loadImport(path, p.Dir, stk, p.build.ImportPos[path]) + if p1.local { + if !p.local && p.Error == nil { + p.Error = &PackageError{ + ImportStack: stk.copy(), + Err: fmt.Sprintf("local import %q in non-local package", path), + } + pos := p.build.ImportPos[path] + if len(pos) > 0 { + p.Error.Pos = pos[0].String() + } + } + path = p1.ImportPath + importPaths[i] = path + } + deps[path] = true + imports = append(imports, p1) + for _, dep := range p1.Deps { + deps[dep] = true + } + if p1.Incomplete { + p.Incomplete = 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. + if p.Standard && (p.ImportPath == "unsafe" || buildContext.Compiler == "gccgo") { + p.target = "" + } + + p.Target = p.target + return p +} + +// packageList returns the list of packages in the dag rooted at roots +// as visited in a depth-first post-order traversal. +func packageList(roots []*Package) []*Package { + seen := map[*Package]bool{} + all := []*Package{} + var walk func(*Package) + walk = func(p *Package) { + if seen[p] { + return + } + seen[p] = true + for _, p1 := range p.imports { + walk(p1) + } + all = append(all, p) + } + for _, root := range roots { + walk(root) + } + return all +} + +// computeStale computes the Stale flag in the package dag that starts +// at the named pkgs (command-line arguments). +func computeStale(pkgs ...*Package) { + topRoot := map[string]bool{} + for _, p := range pkgs { + topRoot[p.Root] = true + } + + for _, p := range packageList(pkgs) { + p.Stale = isStale(p, topRoot) + } +} + +// isStale reports whether package p needs to be rebuilt. +func isStale(p *Package, topRoot map[string]bool) bool { + if p.Standard && (p.ImportPath == "unsafe" || buildContext.Compiler == "gccgo") { + // fake, builtin package + return false + } + if p.Error != nil { + return true + } + + // A package without Go sources means we only found + // the installed .a file. Since we don't know how to rebuild + // it, it can't be stale, even if -a is set. This enables binary-only + // distributions of Go packages, although such binaries are + // only useful with the specific version of the toolchain that + // created them. + if len(p.gofiles) == 0 { + return false + } + + if buildA || p.target == "" || p.Stale { + return true + } + + // Package is stale if completely unbuilt. + var built time.Time + if fi, err := os.Stat(p.target); err == nil { + built = fi.ModTime() + } + if built.IsZero() { + return true + } + + olderThan := func(file string) bool { + fi, err := os.Stat(file) + return err != nil || fi.ModTime().After(built) + } + + // Package is stale if a dependency is, or if a dependency is newer. + for _, p1 := range p.deps { + if p1.Stale || p1.target != "" && olderThan(p1.target) { + return true + } + } + + // As a courtesy to developers installing new versions of the compiler + // frequently, define that packages are stale if they are + // older than the compiler, and commands if they are older than + // the linker. This heuristic will not work if the binaries are back-dated, + // as some binary distributions may do, but it does handle a very + // common case. See issue 3036. + if olderThan(buildToolchain.compiler()) { + return true + } + if p.build.IsCommand() && olderThan(buildToolchain.linker()) { + return true + } + + // Have installed copy, probably built using current compilers, + // and built after its imported packages. The only reason now + // that we'd have to rebuild it is if the sources were newer than + // the package. If a package p is not in the same tree as any + // package named on the command-line, assume it is up-to-date + // no matter what the modification times on the source files indicate. + // This avoids rebuilding $GOROOT packages when people are + // working outside the Go root, and it effectively makes each tree + // listed in $GOPATH a separate compilation world. + // See issue 3149. + if p.Root != "" && !topRoot[p.Root] { + return false + } + + srcs := stringList(p.GoFiles, p.CFiles, p.HFiles, p.SFiles, p.CgoFiles, p.SysoFiles) + for _, src := range srcs { + if olderThan(filepath.Join(p.Dir, src)) { + return true + } + } + + return false +} + +var cwd, _ = os.Getwd() + +var cmdCache = map[string]*Package{} + +// loadPackage is like loadImport but is used for command-line arguments, +// not for paths found in import statements. In addition to ordinary import paths, +// loadPackage accepts pseudo-paths beginning with cmd/ to denote commands +// in the Go command directory, as well as paths to those directories. +func loadPackage(arg string, stk *importStack) *Package { + if build.IsLocalImport(arg) { + dir := arg + if !filepath.IsAbs(dir) { + if abs, err := filepath.Abs(dir); err == nil { + // interpret relative to current directory + dir = abs + } + } + if sub, ok := hasSubdir(gorootSrc, dir); ok && strings.HasPrefix(sub, "cmd/") && !strings.Contains(sub[4:], "/") { + arg = sub + } + } + if strings.HasPrefix(arg, "cmd/") && !strings.Contains(arg[4:], "/") { + if p := cmdCache[arg]; p != nil { + return p + } + stk.push(arg) + defer stk.pop() + bp, err := build.ImportDir(filepath.Join(gorootSrc, arg), 0) + bp.ImportPath = arg + bp.Goroot = true + bp.BinDir = gorootBin + if gobin != "" { + bp.BinDir = gobin + } + bp.Root = goroot + bp.SrcRoot = gorootSrc + p := new(Package) + cmdCache[arg] = p + p.load(stk, bp, err) + if p.Error == nil && p.Name != "main" { + p.Error = &PackageError{ + ImportStack: stk.copy(), + Err: fmt.Sprintf("expected package main but found package %s in %s", p.Name, p.Dir), + } + } + return p + } + + // Wasn't a command; must be a package. + // If it is a local import path but names a standard package, + // we treat it as if the user specified the standard package. + // This lets you run go test ./ioutil in package io and be + // referring to io/ioutil rather than a hypothetical import of + // "./ioutil". + if build.IsLocalImport(arg) { + bp, _ := build.ImportDir(filepath.Join(cwd, arg), build.FindOnly) + if bp.ImportPath != "" && bp.ImportPath != "." { + arg = bp.ImportPath + } + } + + return loadImport(arg, cwd, stk, nil) +} + +// 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 { + var pkgs []*Package + for _, pkg := range packagesAndErrors(args) { + if pkg.Error != nil { + errorf("can't load package: %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 { + if len(args) > 0 && strings.HasSuffix(args[0], ".go") { + return []*Package{goFilesPackage(args)} + } + + args = importPaths(args) + var pkgs []*Package + var stk importStack + for _, arg := range args { + pkgs = append(pkgs, loadPackage(arg, &stk)) + } + + computeStale(pkgs...) + + 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("can't load package: %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 +} + +// hasSubdir reports whether dir is a subdirectory of +// (possibly multiple levels below) root. +// If so, it sets rel to the path fragment that must be +// appended to root to reach dir. +func hasSubdir(root, dir string) (rel string, ok bool) { + if p, err := filepath.EvalSymlinks(root); err == nil { + root = p + } + if p, err := filepath.EvalSymlinks(dir); err == nil { + dir = p + } + const sep = string(filepath.Separator) + root = filepath.Clean(root) + if !strings.HasSuffix(root, sep) { + root += sep + } + dir = filepath.Clean(dir) + if !strings.HasPrefix(dir, root) { + return "", false + } + return filepath.ToSlash(dir[len(root):]), true +} diff --git a/src/cmd/go/run.go b/src/cmd/go/run.go new file mode 100644 index 000000000..94cd59296 --- /dev/null +++ b/src/cmd/go/run.go @@ -0,0 +1,85 @@ +// 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" + "os/exec" + "strings" +) + +var cmdRun = &Command{ + UsageLine: "run [build flags] gofiles... [arguments...]", + Short: "compile and run Go program", + Long: ` +Run compiles and runs the main package comprising the named Go source files. + +For more about build flags, see 'go help build'. + +See also: go build. + `, +} + +func init() { + cmdRun.Run = runRun // break init loop + + addBuildFlags(cmdRun) +} + +func printStderr(args ...interface{}) (int, error) { + return fmt.Fprint(os.Stderr, args...) +} + +func runRun(cmd *Command, args []string) { + var b builder + b.init() + b.print = printStderr + i := 0 + for i < len(args) && strings.HasSuffix(args[i], ".go") { + i++ + } + files, cmdArgs := args[:i], args[i:] + if len(files) == 0 { + fatalf("go run: no go files listed") + } + p := goFilesPackage(files) + if p.Error != nil { + fatalf("%s", p.Error) + } + if p.Name != "main" { + fatalf("go run: cannot run non-main package") + } + 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 + } + } + + runStdin(a.deps[0].target, a.args) + return nil +} + +// runStdin is like run, but connects Stdin. +func runStdin(cmdargs ...interface{}) { + cmdline := stringList(cmdargs...) + cmd := exec.Command(cmdline[0], cmdline[1:]...) + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + errorf("%v", err) + } +} diff --git a/src/cmd/go/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/tag_test.go b/src/cmd/go/tag_test.go new file mode 100644 index 000000000..556a84a8e --- /dev/null +++ b/src/cmd/go/tag_test.go @@ -0,0 +1,97 @@ +// 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 "testing" + +var selectTagTestTags = []string{ + "go.r58", + "go.r58.1", + "go.r59", + "go.r59.1", + "go.r61", + "go.r61.1", + "go.weekly.2010-01-02", + "go.weekly.2011-10-12", + "go.weekly.2011-10-12.1", + "go.weekly.2011-10-14", + "go.weekly.2011-11-01", + "go1", + "go1.0.1", + "go1.999", + "go1.9.2", + "go5", + + // these should be ignored: + "release.r59", + "release.r59.1", + "release", + "weekly.2011-10-12", + "weekly.2011-10-12.1", + "weekly", + "foo", + "bar", + "go.f00", + "go!r60", + "go.1999-01-01", + "go.2x", + "go.20000000000000", + "go.2.", + "go.2.0", + "go2x", + "go20000000000000", + "go2.", + "go2.0", +} + +var selectTagTests = []struct { + version string + selected string +}{ + {"release.r57", ""}, + {"release.r58.2", "go.r58.1"}, + {"release.r59", "go.r59"}, + {"release.r59.1", "go.r59.1"}, + {"release.r60", "go.r59.1"}, + {"release.r60.1", "go.r59.1"}, + {"release.r61", "go.r61"}, + {"release.r66", "go.r61.1"}, + {"weekly.2010-01-01", ""}, + {"weekly.2010-01-02", "go.weekly.2010-01-02"}, + {"weekly.2010-01-02.1", "go.weekly.2010-01-02"}, + {"weekly.2010-01-03", "go.weekly.2010-01-02"}, + {"weekly.2011-10-12", "go.weekly.2011-10-12"}, + {"weekly.2011-10-12.1", "go.weekly.2011-10-12.1"}, + {"weekly.2011-10-13", "go.weekly.2011-10-12.1"}, + {"weekly.2011-10-14", "go.weekly.2011-10-14"}, + {"weekly.2011-10-14.1", "go.weekly.2011-10-14"}, + {"weekly.2011-11-01", "go.weekly.2011-11-01"}, + {"weekly.2014-01-01", "go.weekly.2011-11-01"}, + {"weekly.3000-01-01", "go.weekly.2011-11-01"}, + {"go1", "go1"}, + {"go1.1", "go1.0.1"}, + {"go1.998", "go1.9.2"}, + {"go1.1000", "go1.999"}, + {"go6", "go5"}, + + // faulty versions: + {"release.f00", ""}, + {"weekly.1999-01-01", ""}, + {"junk", ""}, + {"", ""}, + {"go2x", ""}, + {"go200000000000", ""}, + {"go2.", ""}, + {"go2.0", ""}, +} + +func TestSelectTag(t *testing.T) { + for _, c := range selectTagTests { + selected := selectTag(c.version, selectTagTestTags) + if selected != c.selected { + t.Errorf("selectTag(%q) = %q, want %q", c.version, selected, c.selected) + } + } +} diff --git a/src/cmd/go/test.bash b/src/cmd/go/test.bash new file mode 100755 index 000000000..fe186d4bb --- /dev/null +++ b/src/cmd/go/test.bash @@ -0,0 +1,127 @@ +#!/bin/bash +# 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. + +set -e +go build -o testgo + +ok=true + +unset GOPATH +unset GOBIN + +# Test that error messages have file:line information +# at beginning of line. +for i in testdata/errmsg/*.go +do + # TODO: |cat should not be necessary here but is. + ./testgo test $i 2>&1 | cat >err.out || true + if ! grep -q "^$i:" err.out; then + echo "$i: missing file:line in error message" + cat err.out + ok=false + fi +done + +# Test local (./) imports. +testlocal() { + local="$1" + ./testgo build -o hello "testdata/$local/easy.go" + ./hello >hello.out + if ! grep -q '^easysub\.Hello' hello.out; then + echo "testdata/$local/easy.go did not generate expected output" + cat hello.out + ok=false + fi + + ./testgo build -o hello "testdata/$local/easysub/main.go" + ./hello >hello.out + if ! grep -q '^easysub\.Hello' hello.out; then + echo "testdata/$local/easysub/main.go did not generate expected output" + cat hello.out + ok=false + fi + + ./testgo build -o hello "testdata/$local/hard.go" + ./hello >hello.out + if ! grep -q '^sub\.Hello' hello.out || ! grep -q '^subsub\.Hello' hello.out ; then + echo "testdata/$local/hard.go did not generate expected output" + cat hello.out + ok=false + fi + + rm -f err.out hello.out hello + + # Test that go install x.go fails. + if ./testgo install "testdata/$local/easy.go" >/dev/null 2>&1; then + echo "go install testdata/$local/easy.go succeeded" + ok=false + fi +} + +# Test local imports +testlocal local + +# Test local imports again, with bad characters in the directory name. +bad='#$%:, &()*;<=>?\^{}' +rm -rf "testdata/$bad" +cp -R testdata/local "testdata/$bad" +testlocal "$bad" +rm -rf "testdata/$bad" + +# Test tests with relative imports. +if ! ./testgo test ./testdata/testimport; then + echo "go test ./testdata/testimport failed" + ok=false +fi + +# Test tests with relative imports in packages synthesized +# from Go files named on the command line. +if ! ./testgo test ./testdata/testimport/*.go; then + echo "go test ./testdata/testimport/*.go failed" + ok=false +fi + +# Test that without $GOBIN set, binaries get installed +# into the GOPATH bin directory. +rm -rf testdata/bin +if ! GOPATH=$(pwd)/testdata ./testgo install go-cmd-test; then + echo "go install go-cmd-test failed" + ok=false +elif ! test -x testdata/bin/go-cmd-test; then + echo "go install go-cmd-test did not write to testdata/bin/go-cmd-test" + ok=false +fi + +# And with $GOBIN set, binaries get installed to $GOBIN. +if ! GOBIN=$(pwd)/testdata/bin1 GOPATH=$(pwd)/testdata ./testgo install go-cmd-test; then + echo "go install go-cmd-test failed" + ok=false +elif ! test -x testdata/bin1/go-cmd-test; then + echo "go install go-cmd-test did not write to testdata/bin1/go-cmd-test" + ok=false +fi + +# Without $GOBIN set, installing a program outside $GOPATH should fail +# (there is nowhere to install it). +if ./testgo install testdata/src/go-cmd-test/helloworld.go; then + echo "go install testdata/src/go-cmd-test/helloworld.go should have failed, did not" + ok=false +fi + +# With $GOBIN set, should install there. +if ! GOBIN=$(pwd)/testdata/bin1 ./testgo install testdata/src/go-cmd-test/helloworld.go; then + echo "go install testdata/src/go-cmd-test/helloworld.go failed" + ok=false +elif ! test -x testdata/bin1/helloworld; then + echo "go install testdata/src/go-cmd-test/helloworld.go did not write testdata/bin1/helloworld" + ok=false +fi + +if $ok; then + echo PASS +else + echo FAIL + exit 1 +fi diff --git a/src/cmd/go/test.go b/src/cmd/go/test.go new file mode 100644 index 000000000..870ab190f --- /dev/null +++ b/src/cmd/go/test.go @@ -0,0 +1,815 @@ +// 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/doc" + "go/parser" + "go/token" + "os" + "os/exec" + "path" + "path/filepath" + "runtime" + "sort" + "strings" + "text/template" + "time" + "unicode" + "unicode/utf8" +) + +// Break init loop. +func init() { + cmdTest.Run = runTest +} + +var cmdTest = &Command{ + CustomFlags: true, + UsageLine: "test [-c] [-i] [build flags] [packages] [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. + +The package is built in a temporary directory so it does not interfere with the +non-test installation. + +In addition to the build flags, the flags handled by 'go test' itself are: + + -c Compile the test binary to pkg.test but do not run it. + + -i + Install packages that are dependencies of the test. + Do not run the test. + +The test binary also accepts flags that control execution of the test; these +flags are also accessible by 'go test'. See 'go help testflag' for details. + +For more about build flags, see 'go help build'. +For more about specifying packages, see 'go help packages'. + +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 test binary, called pkg.test, where pkg is the name of the +directory containing the package sources, has its own flags: + + -test.v + Verbose output: log all tests as they are run. + + -test.run pattern + Run only those tests and examples 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 + +will compile the test binary and then run it as + + pkg.test -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 "Output:" comment, which +must be the last comment in the function body (see example below). An +example with no such comment, or with no text after "Output:" is compiled +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: + + func ExamplePrintln() { + Println("The output of\nthis example.") + // Output: The output of + // this example. + } + +The entire test file is presented as the example when it contains a single +example function, at least one other function, type, variable, or constant +declaration, and no test or benchmark functions. + +See the documentation of the testing package for more information. +`, +} + +var ( + testC bool // -c flag + testI bool // -i flag + testV bool // -v flag + testFiles []string // -file flag(s) TODO: not respected + testTimeout string // -timeout flag + testArgs []string + testBench bool + testStreamOutput bool // show output as it is generated + testShowPass bool // show passing output + + testKillTimeout = 10 * time.Minute +) + +func runTest(cmd *Command, args []string) { + var pkgArgs []string + pkgArgs, testArgs = testFlags(args) + + 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") + } + + // If a test timeout was given and is parseable, set our kill timeout + // to that timeout plus one minute. This is a backup alarm in case + // the test wedges with a goroutine spinning and its background + // timer does not get a chance to fire. + if dt, err := time.ParseDuration(testTimeout); err == nil { + testKillTimeout = dt + 1*time.Minute + } + + // show passing test output (after buffering) with -v flag. + // must buffer because tests are running in parallel, and + // otherwise the output will get mixed. + testShowPass = testV + + // stream test output (no buffering) when no package has + // been given on the command line (implicit current directory) + // or when benchmarking. + // Also stream if we're showing output anyway with a + // single package under test. In that case, streaming the + // output produces the same result as not streaming, + // just more immediately. + testStreamOutput = len(pkgArgs) == 0 || testBench || + (len(pkgs) <= 1 && testShowPass) + + var b builder + b.init() + + if testI { + buildV = testV + + deps := map[string]bool{ + // Dependencies for testmain. + "testing": true, + "regexp": true, + } + for _, p := range pkgs { + // Dependencies for each test. + for _, path := range p.Imports { + deps[path] = true + } + for _, path := range p.TestImports { + deps[path] = true + } + for _, path := range p.XTestImports { + deps[path] = true + } + } + + // translate C to runtime/cgo + if deps["C"] { + delete(deps, "C") + deps["runtime/cgo"] = true + if buildContext.GOOS == runtime.GOOS && buildContext.GOARCH == runtime.GOARCH { + deps["cmd/cgo"] = true + } + } + // Ignore pseudo-packages. + delete(deps, "unsafe") + + all := []string{} + for path := range deps { + all = append(all, path) + } + sort.Strings(all) + + a := &action{} + for _, p := range packagesForBuild(all) { + a.deps = append(a.deps, b.action(modeInstall, modeInstall, p)) + } + b.do(a) + if !testC { + return + } + 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 { + str := err.Error() + if strings.HasPrefix(str, "\n") { + str = str[1:] + } + if p.ImportPath != "" { + errorf("# %s\n%s", p.ImportPath, str) + } else { + errorf("%s", str) + } + 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 && !a.p.local { + 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 { + args := strings.Join(pkgArgs, " ") + if args != "" { + args = " " + args + } + fmt.Fprintf(os.Stderr, "installing these packages with 'go test -i%s' will speed future tests.\n\n", args) + } + + b.do(root) +} + +func (b *builder) test(p *Package) (buildAction, runAction, printAction *action, err error) { + if len(p.TestGoFiles)+len(p.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 - pkg.test binary + var ptest, pxtest, pmain *Package + + var imports, ximports []*Package + var stk importStack + stk.push(p.ImportPath + "_test") + for _, path := range p.TestImports { + p1 := loadImport(path, p.Dir, &stk, p.build.TestImportPos[path]) + if p1.Error != nil { + return nil, nil, nil, p1.Error + } + imports = append(imports, p1) + } + for _, path := range p.XTestImports { + if path == p.ImportPath { + continue + } + p1 := loadImport(path, p.Dir, &stk, p.build.XTestImportPos[path]) + if p1.Error != nil { + return nil, nil, nil, p1.Error + } + ximports = append(ximports, p1) + } + stk.pop() + + // Use last element of import path, not package name. + // They differ when package name is "main". + _, elem := path.Split(p.ImportPath) + testBinary := elem + ".test" + + // The ptest package needs to be importable under the + // same import path that p has, but we cannot put it in + // the usual place in the temporary tree, because then + // 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 := buildToolchain.pkgpath(testDir, p) + + // 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.TestGoFiles) > 0 { + ptest = new(Package) + *ptest = *p + ptest.GoFiles = nil + ptest.GoFiles = append(ptest.GoFiles, p.GoFiles...) + ptest.GoFiles = append(ptest.GoFiles, p.TestGoFiles...) + ptest.target = "" + ptest.Imports = stringList(p.Imports, p.TestImports) + ptest.imports = append(append([]*Package{}, p.imports...), imports...) + ptest.pkgdir = testDir + ptest.fake = true + ptest.forceLibrary = true + ptest.Stale = true + ptest.build = new(build.Package) + *ptest.build = *p.build + m := map[string][]token.Position{} + for k, v := range p.build.ImportPos { + m[k] = append(m[k], v...) + } + for k, v := range p.build.TestImportPos { + m[k] = append(m[k], v...) + } + ptest.build.ImportPos = m + } else { + ptest = p + } + + // External test package. + if len(p.XTestGoFiles) > 0 { + pxtest = &Package{ + Name: p.Name + "_test", + ImportPath: p.ImportPath + "_test", + localPrefix: p.localPrefix, + Root: p.Root, + Dir: p.Dir, + GoFiles: p.XTestGoFiles, + Imports: p.XTestImports, + build: &build.Package{ + ImportPos: p.build.XTestImportPos, + }, + imports: append(ximports, ptest), + pkgdir: testDir, + fake: true, + Stale: true, + } + } + + // Action for building pkg.test. + pmain = &Package{ + Name: "main", + Dir: testDir, + GoFiles: []string{"_testmain.go"}, + ImportPath: "testmain", + Root: p.Root, + imports: []*Package{ptest}, + build: &build.Package{Name: "main"}, + fake: true, + Stale: true, + } + if pxtest != nil { + pmain.imports = append(pmain.imports, pxtest) + } + + // The generated main also imports testing and regexp. + stk.push("testmain") + ptesting := loadImport("testing", "", &stk, nil) + if ptesting.Error != nil { + return nil, nil, nil, ptesting.Error + } + pregexp := loadImport("regexp", "", &stk, nil) + if pregexp.Error != nil { + return nil, nil, nil, pregexp.Error + } + pmain.imports = append(pmain.imports, ptesting, pregexp) + computeStale(pmain) + + if ptest != p { + a := b.action(modeBuild, modeBuild, ptest) + a.objdir = testDir + string(filepath.Separator) + a.objpkg = ptestObj + a.target = ptestObj + a.link = false + } + + if pxtest != nil { + a := b.action(modeBuild, modeBuild, pxtest) + a.objdir = testDir + string(filepath.Separator) + a.objpkg = buildToolchain.pkgpath(testDir, pxtest) + a.target = a.objpkg + } + + a := b.action(modeBuild, modeBuild, pmain) + a.objdir = testDir + string(filepath.Separator) + a.objpkg = filepath.Join(testDir, "main.a") + a.target = filepath.Join(testDir, testBinary) + exeSuffix + 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: testBinary + exeSuffix, + } + printAction = &action{p: p, deps: []*action{runAction}} // nop + } else { + // run test + runAction = &action{ + f: (*builder).runTest, + deps: []*action{pmainAction}, + p: p, + ignoreFail: true, + } + cleanAction := &action{ + f: (*builder).cleanTest, + deps: []*action{runAction}, + p: p, + } + printAction = &action{ + f: (*builder).printTest, + deps: []*action{cleanAction}, + 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) + setExitStatus(1) + return nil + } + + cmd := exec.Command(args[0], args[1:]...) + cmd.Dir = a.p.Dir + var buf bytes.Buffer + if testStreamOutput { + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + } else { + 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. + tick := time.NewTimer(testKillTimeout) + 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 { + if testShowPass { + a.testOutput.Write(out) + } + fmt.Fprintf(a.testOutput, "ok \t%s\t%s\n", a.p.ImportPath, t) + return nil + } + + setExitStatus(1) + if len(out) > 0 { + a.testOutput.Write(out) + // assume printing the test binary's exit status is superfluous + } else { + fmt.Fprintf(a.testOutput, "%s\n", err) + } + fmt.Fprintf(a.testOutput, "FAIL\t%s\t%s\n", a.p.ImportPath, t) + + return nil +} + +// cleanTest is the action for cleaning up after a test. +func (b *builder) cleanTest(a *action) error { + if buildWork { + return nil + } + run := a.deps[0] + testDir := filepath.Join(b.work, filepath.FromSlash(run.p.ImportPath+"/_test")) + os.RemoveAll(testDir) + return nil +} + +// printTest is the action for printing a test result. +func (b *builder) printTest(a *action) error { + clean := a.deps[0] + run := clean.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, + } + for _, file := range p.TestGoFiles { + if err := t.load(filepath.Join(p.Dir, file), "_test", &t.NeedTest); err != nil { + return err + } + } + for _, file := range p.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 + 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 expandScanner(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 + } + } + for _, e := range doc.Examples(f) { + if e.Output == "" { + // Don't run examples with no output. + continue + } + t.Examples = append(t.Examples, testFunc{pkg, "Example" + e.Name, e.Output}) + *seen = true + } + return nil +} + +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/testdata/errmsg/x.go b/src/cmd/go/testdata/errmsg/x.go new file mode 100644 index 000000000..60f5b6e98 --- /dev/null +++ b/src/cmd/go/testdata/errmsg/x.go @@ -0,0 +1,3 @@ +package foo + +import "bar" diff --git a/src/cmd/go/testdata/errmsg/x1_test.go b/src/cmd/go/testdata/errmsg/x1_test.go new file mode 100644 index 000000000..eb1a6798c --- /dev/null +++ b/src/cmd/go/testdata/errmsg/x1_test.go @@ -0,0 +1,3 @@ +package foo_test + +import "bar" diff --git a/src/cmd/go/testdata/errmsg/x_test.go b/src/cmd/go/testdata/errmsg/x_test.go new file mode 100644 index 000000000..60f5b6e98 --- /dev/null +++ b/src/cmd/go/testdata/errmsg/x_test.go @@ -0,0 +1,3 @@ +package foo + +import "bar" diff --git a/src/cmd/go/testdata/local/easy.go b/src/cmd/go/testdata/local/easy.go new file mode 100644 index 000000000..4eeb517da --- /dev/null +++ b/src/cmd/go/testdata/local/easy.go @@ -0,0 +1,7 @@ +package main + +import "./easysub" + +func main() { + easysub.Hello() +} diff --git a/src/cmd/go/testdata/local/easysub/easysub.go b/src/cmd/go/testdata/local/easysub/easysub.go new file mode 100644 index 000000000..07040daee --- /dev/null +++ b/src/cmd/go/testdata/local/easysub/easysub.go @@ -0,0 +1,7 @@ +package easysub + +import "fmt" + +func Hello() { + fmt.Println("easysub.Hello") +} diff --git a/src/cmd/go/testdata/local/easysub/main.go b/src/cmd/go/testdata/local/easysub/main.go new file mode 100644 index 000000000..6c30b5236 --- /dev/null +++ b/src/cmd/go/testdata/local/easysub/main.go @@ -0,0 +1,9 @@ +// +build ignore + +package main + +import "." + +func main() { + easysub.Hello() +} diff --git a/src/cmd/go/testdata/local/hard.go b/src/cmd/go/testdata/local/hard.go new file mode 100644 index 000000000..2ffac3fd7 --- /dev/null +++ b/src/cmd/go/testdata/local/hard.go @@ -0,0 +1,7 @@ +package main + +import "./sub" + +func main() { + sub.Hello() +} diff --git a/src/cmd/go/testdata/local/sub/sub.go b/src/cmd/go/testdata/local/sub/sub.go new file mode 100644 index 000000000..d5dbf6d5f --- /dev/null +++ b/src/cmd/go/testdata/local/sub/sub.go @@ -0,0 +1,12 @@ +package sub + +import ( + "fmt" + + subsub "./sub" +) + +func Hello() { + fmt.Println("sub.Hello") + subsub.Hello() +} diff --git a/src/cmd/go/testdata/local/sub/sub/subsub.go b/src/cmd/go/testdata/local/sub/sub/subsub.go new file mode 100644 index 000000000..4cc72233e --- /dev/null +++ b/src/cmd/go/testdata/local/sub/sub/subsub.go @@ -0,0 +1,7 @@ +package subsub + +import "fmt" + +func Hello() { + fmt.Println("subsub.Hello") +} diff --git a/src/cmd/go/testdata/src/go-cmd-test/helloworld.go b/src/cmd/go/testdata/src/go-cmd-test/helloworld.go new file mode 100644 index 000000000..002a5c740 --- /dev/null +++ b/src/cmd/go/testdata/src/go-cmd-test/helloworld.go @@ -0,0 +1,5 @@ +package main + +func main() { + println("hello world") +} diff --git a/src/cmd/go/testdata/testimport/p.go b/src/cmd/go/testdata/testimport/p.go new file mode 100644 index 000000000..f94d2cd0e --- /dev/null +++ b/src/cmd/go/testdata/testimport/p.go @@ -0,0 +1,3 @@ +package p + +func F() int { return 1 } diff --git a/src/cmd/go/testdata/testimport/p1/p1.go b/src/cmd/go/testdata/testimport/p1/p1.go new file mode 100644 index 000000000..fd315272e --- /dev/null +++ b/src/cmd/go/testdata/testimport/p1/p1.go @@ -0,0 +1,3 @@ +package p1 + +func F() int { return 1 } diff --git a/src/cmd/go/testdata/testimport/p2/p2.go b/src/cmd/go/testdata/testimport/p2/p2.go new file mode 100644 index 000000000..d4888865d --- /dev/null +++ b/src/cmd/go/testdata/testimport/p2/p2.go @@ -0,0 +1,3 @@ +package p2 + +func F() int { return 1 } diff --git a/src/cmd/go/testdata/testimport/p_test.go b/src/cmd/go/testdata/testimport/p_test.go new file mode 100644 index 000000000..a3fb4a9e2 --- /dev/null +++ b/src/cmd/go/testdata/testimport/p_test.go @@ -0,0 +1,13 @@ +package p + +import ( + "./p1" + + "testing" +) + +func TestF(t *testing.T) { + if F() != p1.F() { + t.Fatal(F()) + } +} diff --git a/src/cmd/go/testdata/testimport/x_test.go b/src/cmd/go/testdata/testimport/x_test.go new file mode 100644 index 000000000..b253e3fd2 --- /dev/null +++ b/src/cmd/go/testdata/testimport/x_test.go @@ -0,0 +1,15 @@ +package p_test + +import ( + . "../testimport" + + "./p2" + + "testing" +) + +func TestF1(t *testing.T) { + if F() != p2.F() { + t.Fatal(F()) + } +} diff --git a/src/cmd/go/testflag.go b/src/cmd/go/testflag.go new file mode 100644 index 000000000..ecf5bf456 --- /dev/null +++ b/src/cmd/go/testflag.go @@ -0,0 +1,235 @@ +// 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) + setExitStatus(2) + exit() +} + +// testFlagSpec defines a flag we know about. +type testFlagSpec struct { + name string + boolVar *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", boolVar: &testC}, + {name: "file", multiOK: true}, + {name: "i", boolVar: &testI}, + + // build flags. + {name: "a", boolVar: &buildA}, + {name: "n", boolVar: &buildN}, + {name: "p"}, + {name: "x", boolVar: &buildX}, + {name: "work", boolVar: &buildWork}, + {name: "gcflags"}, + {name: "ldflags"}, + {name: "gccgoflags"}, + {name: "tags"}, + {name: "compiler"}, + + // 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", boolVar: new(bool), passToTest: true}, + {name: "timeout", passToTest: true}, + {name: "v", boolVar: &testV, 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 +// pkg.test's arguments. +// We allow known flags both before and after the package name list, +// to allow both +// go test fmt -custom-flag-for-fmt-test +// 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 { + // bool flags. + case "a", "c", "i", "n", "x", "v", "work": + setBoolFlag(f.boolVar, value) + case "p": + setIntFlag(&buildP, value) + case "gcflags": + buildGcflags = strings.Fields(value) + case "ldflags": + buildLdflags = strings.Fields(value) + case "gccgoflags": + buildGccgoflags = strings.Fields(value) + case "tags": + buildContext.BuildTags = strings.Fields(value) + case "compiler": + buildCompiler{}.Set(value) + case "file": + testFiles = append(testFiles, value) + case "bench": + // record that we saw the flag; don't care about the value + testBench = true + case "timeout": + testTimeout = value + } + 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.boolVar != nil { + 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/tool.go b/src/cmd/go/tool.go new file mode 100644 index 000000000..cb463a2e7 --- /dev/null +++ b/src/cmd/go/tool.go @@ -0,0 +1,125 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "go/build" + "os" + "os/exec" + "path/filepath" + "runtime" + "sort" + "strings" +) + +var cmdTool = &Command{ + Run: runTool, + UsageLine: "tool [-n] command [args...]", + Short: "run specified go tool", + Long: ` +Tool runs the go tool command identified by the arguments. +With no arguments it prints the list of known tools. + +The -n flag causes tool to print the command that would be +executed but not execute it. + +For more about each tool command, see 'go tool command -h'. +`, +} + +var ( + toolGOOS = runtime.GOOS + toolGOARCH = runtime.GOARCH + toolIsWindows = toolGOOS == "windows" + toolDir = build.ToolDir + + toolN bool +) + +func init() { + cmdTool.Flag.BoolVar(&toolN, "n", false, "") +} + +const toolWindowsExtension = ".exe" + +func tool(name string) string { + p := filepath.Join(toolDir, name) + if toolIsWindows { + p += toolWindowsExtension + } + return p +} + +func runTool(cmd *Command, args []string) { + if len(args) == 0 { + listTools() + return + } + toolName := args[0] + // The tool name must be lower-case letters, numbers or underscores. + for _, c := range toolName { + switch { + case 'a' <= c && c <= 'z', '0' <= c && c <= '9', c == '_': + default: + fmt.Fprintf(os.Stderr, "go tool: bad tool name %q\n", toolName) + setExitStatus(2) + return + } + } + toolPath := tool(toolName) + // Give a nice message if there is no tool with that name. + if _, err := os.Stat(toolPath); err != nil { + fmt.Fprintf(os.Stderr, "go tool: no such tool %q\n", toolName) + setExitStatus(3) + return + } + + if toolN { + fmt.Printf("%s %s\n", toolPath, strings.Join(args[1:], " ")) + return + } + toolCmd := &exec.Cmd{ + Path: toolPath, + Args: args, + Stdin: os.Stdin, + Stdout: os.Stdout, + Stderr: os.Stderr, + } + err := toolCmd.Run() + if err != nil { + fmt.Fprintf(os.Stderr, "go tool %s: %s\n", toolName, err) + setExitStatus(1) + return + } +} + +// listTools prints a list of the available tools in the tools directory. +func listTools() { + f, err := os.Open(toolDir) + if err != nil { + fmt.Fprintf(os.Stderr, "go tool: no tool directory: %s\n", err) + setExitStatus(2) + return + } + defer f.Close() + names, err := f.Readdirnames(-1) + if err != nil { + fmt.Fprintf(os.Stderr, "go tool: can't read directory: %s\n", err) + setExitStatus(2) + return + } + + sort.Strings(names) + for _, name := range names { + // Unify presentation by going to lower case. + name = strings.ToLower(name) + // If it's windows, don't show the .exe suffix. + if toolIsWindows && strings.HasSuffix(name, toolWindowsExtension) { + name = name[:len(name)-len(toolWindowsExtension)] + } + fmt.Println(name) + } +} diff --git a/src/cmd/go/vcs.go b/src/cmd/go/vcs.go new file mode 100644 index 000000000..5f63f8b56 --- /dev/null +++ b/src/cmd/go/vcs.go @@ -0,0 +1,674 @@ +// 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" + "errors" + "fmt" + "log" + "os" + "os/exec" + "path/filepath" + "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 + tagLookupCmd []tagCmd // commands to lookup tags before running tagSyncCmd + tagSyncCmd string // command to sync to specific tag + tagSyncDefault string // command to sync to default tag + + scheme []string + pingCmd string +} + +// A tagCmd describes a command to list available tags +// 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 go1 branch + // and make changes in both, without constantly + // editing .hgtags. + tagCmd: []tagCmd{ + {"tags", `^(\S+)`}, + {"branches", `^(\S+)`}, + }, + tagSyncCmd: "update -r {tag}", + tagSyncDefault: "update default", + + scheme: []string{"https", "http"}, + pingCmd: "identify {scheme}://{repo}", +} + +// vcsGit describes how to use Git. +var vcsGit = &vcsCmd{ + name: "Git", + cmd: "git", + + createCmd: "clone {repo} {dir}", + downloadCmd: "fetch", + + tagCmd: []tagCmd{ + // tags/xxx matches a git tag named xxx + // origin/xxx matches a git branch named xxx on the default remote repository + {"show-ref", `(?:tags|origin)/(\S+)$`}, + }, + tagLookupCmd: []tagCmd{ + {"show-ref tags/{tag} origin/{tag}", `((?:tags|origin)/\S+)$`}, + }, + tagSyncCmd: "checkout {tag}", + tagSyncDefault: "checkout origin/master", + + scheme: []string{"git", "https", "http", "git+ssh"}, + pingCmd: "ls-remote {scheme}://{repo}", +} + +// 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+)`}}, + tagSyncCmd: "update -r {tag}", + tagSyncDefault: "update -r revno:-1", + + scheme: []string{"https", "http", "bzr", "bzr+ssh"}, + pingCmd: "info {scheme}://{repo}", +} + +// 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. + + scheme: []string{"https", "http", "svn", "svn+ssh"}, + pingCmd: "info {scheme}://{repo}", +} + +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, cmd, keyval, true) + return err +} + +// runVerboseOnly is like run but only generates error output to standard error in verbose mode. +func (v *vcsCmd) runVerboseOnly(dir string, cmd string, keyval ...string) error { + _, err := v.run1(dir, cmd, keyval, false) + return err +} + +// runOutput is like run but returns the output of the command. +func (v *vcsCmd) runOutput(dir string, cmd string, keyval ...string) ([]byte, error) { + return v.run1(dir, cmd, keyval, true) +} + +// run1 is the generalized implementation of run and runOutput. +func (v *vcsCmd) run1(dir string, cmdline string, keyval []string, verbose bool) ([]byte, error) { + m := make(map[string]string) + for i := 0; i < len(keyval); i += 2 { + m[keyval[i]] = keyval[i+1] + } + 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 + err := cmd.Run() + out := buf.Bytes() + if err != nil { + if verbose || buildV { + fmt.Fprintf(os.Stderr, "# cd %s; %s %s\n", dir, v.cmd, strings.Join(args, " ")) + os.Stderr.Write(out) + } + return nil, err + } + return out, nil +} + +// ping pings to determine scheme to use. +func (v *vcsCmd) ping(scheme, repo string) error { + return v.runVerboseOnly(".", v.pingCmd, "scheme", scheme, "repo", repo) +} + +// create creates a new copy of repo in dir. +// The parent of dir must exist; dir must not. +func (v *vcsCmd) create(dir, repo string) error { + 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) + for _, m := range re.FindAllStringSubmatch(string(out), -1) { + tags = append(tags, m[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 + } + if tag != "" { + for _, tc := range v.tagLookupCmd { + out, err := v.runOutput(dir, tc.cmd, "tag", tag) + if err != nil { + return err + } + re := regexp.MustCompile(`(?m-s)` + tc.pattern) + m := re.FindStringSubmatch(string(out)) + if len(m) > 1 { + tag = m[1] + break + } + } + } + if tag == "" && v.tagSyncDefault != "" { + return v.run(dir, v.tagSyncDefault) + } + return v.run(dir, v.tagSyncCmd, "tag", tag) +} + +// 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 + ping bool // ping for scheme to use to download repo + + regexp *regexp.Regexp // cached compiled form of re +} + +// vcsForDir inspects dir and its parents to determine the +// version control system and code repository to use. +// On return, root is the import path +// corresponding to the root of the repository +// (thus root is a prefix of importPath). +func vcsForDir(p *Package) (vcs *vcsCmd, root string, err error) { + // Clean and double-check that dir is in (a subdirectory of) srcRoot. + dir := filepath.Clean(p.Dir) + srcRoot := filepath.Clean(p.build.SrcRoot) + if len(dir) <= len(srcRoot) || dir[len(srcRoot)] != filepath.Separator { + return nil, "", fmt.Errorf("directory %q is outside source root %q", dir, srcRoot) + } + + for len(dir) > len(srcRoot) { + for _, vcs := range vcsList { + if fi, err := os.Stat(filepath.Join(dir, "."+vcs.cmd)); err == nil && fi.IsDir() { + return vcs, dir[len(srcRoot)+1:], nil + } + } + + // Move to parent. + ndir := filepath.Dir(dir) + if len(ndir) >= len(dir) { + // Shouldn't happen, but just in case, stop. + break + } + dir = ndir + } + + return nil, "", fmt.Errorf("directory %q is not using a known version control system", dir) +} + +// repoRoot represents a version control system, a repo, and a root of +// where to put it on disk. +type repoRoot struct { + vcs *vcsCmd + + // repo is the repository URL, including scheme + repo string + + // root is the import path corresponding to the root of the + // repository + root string +} + +// repoRootForImportPath analyzes importPath to determine the +// version control system, and code repository to use. +func repoRootForImportPath(importPath string) (*repoRoot, error) { + rr, err := repoRootForImportPathStatic(importPath, "") + if err == errUnknownSite { + rr, err = repoRootForImportDynamic(importPath) + + // repoRootForImportDynamic returns error detail + // that is irrelevant if the user didn't intend to use a + // dynamic import in the first place. + // Squelch it. + if err != nil { + if buildV { + log.Printf("import %q: %v", importPath, err) + } + err = fmt.Errorf("unrecognized import path %q", importPath) + } + } + + if err == nil && strings.Contains(importPath, "...") && strings.Contains(rr.root, "...") { + // Do not allow wildcards in the repo root. + rr = nil + err = fmt.Errorf("cannot expand ... in %q", importPath) + } + return rr, err +} + +var errUnknownSite = errors.New("dynamic lookup required to find mapping") + +// repoRootForImportPathStatic attempts to map importPath to a +// repoRoot using the commonly-used VCS hosting sites in vcsPaths +// (github.com/user/dir), or from a fully-qualified importPath already +// containing its VCS type (foo.com/repo.git/dir) +// +// If scheme is non-empty, that scheme is forced. +func repoRootForImportPathStatic(importPath, scheme string) (*repoRoot, error) { + if strings.Contains(importPath, "://") { + return nil, fmt.Errorf("invalid import path %q", importPath) + } + 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"]) + } + if srv.ping { + if scheme != "" { + match["repo"] = scheme + "://" + match["repo"] + } else { + for _, scheme := range vcs.scheme { + if vcs.ping(scheme, match["repo"]) == nil { + match["repo"] = scheme + "://" + match["repo"] + break + } + } + } + } + rr := &repoRoot{ + vcs: vcs, + repo: match["repo"], + root: match["root"], + } + return rr, nil + } + return nil, errUnknownSite +} + +// repoRootForImportDynamic finds a *repoRoot for a custom domain that's not +// statically known by repoRootForImportPathStatic. +// +// This handles "vanity import paths" like "name.tld/pkg/foo". +func repoRootForImportDynamic(importPath string) (*repoRoot, error) { + slash := strings.Index(importPath, "/") + if slash < 0 { + return nil, fmt.Errorf("missing / in import %q", importPath) + } + urlStr, body, err := httpsOrHTTP(importPath) + if err != nil { + return nil, fmt.Errorf("http/https fetch for import %q: %v", importPath, err) + } + defer body.Close() + metaImport, err := matchGoImport(parseMetaGoImports(body), importPath) + if err != nil { + if err != errNoMatch { + return nil, fmt.Errorf("parse %s: %v", urlStr, err) + } + return nil, fmt.Errorf("parse %s: no go-import meta tags", urlStr) + } + if buildV { + log.Printf("get %q: found meta tag %#v at %s", importPath, metaImport, urlStr) + } + // If the import was "uni.edu/bob/project", which said the + // prefix was "uni.edu" and the RepoRoot was "evilroot.com", + // make sure we don't trust Bob and check out evilroot.com to + // "uni.edu" yet (possibly overwriting/preempting another + // non-evil student). Instead, first verify the root and see + // if it matches Bob's claim. + if metaImport.Prefix != importPath { + if buildV { + log.Printf("get %q: verifying non-authoritative meta tag", importPath) + } + urlStr0 := urlStr + urlStr, body, err = httpsOrHTTP(metaImport.Prefix) + if err != nil { + return nil, fmt.Errorf("fetch %s: %v", urlStr, err) + } + imports := parseMetaGoImports(body) + if len(imports) == 0 { + return nil, fmt.Errorf("fetch %s: no go-import meta tag", urlStr) + } + metaImport2, err := matchGoImport(imports, importPath) + if err != nil || metaImport != metaImport2 { + return nil, fmt.Errorf("%s and %s disagree about go-import for %s", urlStr0, urlStr, metaImport.Prefix) + } + } + + if !strings.Contains(metaImport.RepoRoot, "://") { + return nil, fmt.Errorf("%s: invalid repo root %q; no scheme", urlStr, metaImport.RepoRoot) + } + rr := &repoRoot{ + vcs: vcsByCmd(metaImport.VCS), + repo: metaImport.RepoRoot, + root: metaImport.Prefix, + } + if rr.vcs == nil { + return nil, fmt.Errorf("%s: unknown vcs %q", urlStr, metaImport.VCS) + } + return rr, nil +} + +// metaImport represents the parsed <meta name="go-import" +// content="prefix vcs reporoot" /> tags from HTML files. +type metaImport struct { + Prefix, VCS, RepoRoot string +} + +// errNoMatch is returned from matchGoImport when there's no applicable match. +var errNoMatch = errors.New("no import match") + +// matchGoImport returns the metaImport from imports matching importPath. +// An error is returned if there are multiple matches. +// errNoMatch is returned if none match. +func matchGoImport(imports []metaImport, importPath string) (_ metaImport, err error) { + match := -1 + for i, im := range imports { + if !strings.HasPrefix(importPath, im.Prefix) { + continue + } + if match != -1 { + err = fmt.Errorf("multiple meta tags match import path %q", importPath) + return + } + match = i + } + if match == -1 { + err = errNoMatch + return + } + return imports[match], nil +} + +// 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_.\-]+)*$`, + ping: true, + }, +} + +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..a672b9910 --- /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 [packages]", + Short: "run go tool vet on packages", + Long: ` +Vet runs the Go vet command on the packages named by the import paths. + +For more about vet, see 'godoc vet'. +For more about specifying packages, see 'go help packages'. + +To run the vet tool with specific options, run 'go tool vet'. + +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(tool("vet"), relPaths(pkg.gofiles)) + } +} |