diff options
Diffstat (limited to 'src/cmd/go')
31 files changed, 1708 insertions, 372 deletions
diff --git a/src/cmd/go/bootstrap.go b/src/cmd/go/bootstrap.go index 32941404c..dc7ed5f4c 100644 --- a/src/cmd/go/bootstrap.go +++ b/src/cmd/go/bootstrap.go @@ -25,6 +25,6 @@ func httpsOrHTTP(importPath string) (string, io.ReadCloser, error) { return "", nil, errHTTP } -func parseMetaGoImports(r io.Reader) (imports []metaImport) { +func parseMetaGoImports(r io.Reader) ([]metaImport, error) { panic("unreachable") } diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go index 025b258bf..f70f778d9 100644 --- a/src/cmd/go/build.go +++ b/src/cmd/go/build.go @@ -94,7 +94,8 @@ in an element in the list, surround it with either single or double quotes. For more about specifying packages, see 'go help packages'. For more about where packages and binaries are installed, -see 'go help gopath'. +run 'go help gopath'. For more about calling between Go and C/C++, +run 'go help c'. See also: go install, go get, go clean. `, @@ -185,6 +186,18 @@ func isSpaceByte(c byte) bool { return c == ' ' || c == '\t' || c == '\n' || c == '\r' } +// fileExtSplit expects a filename and returns the name +// and ext (without the dot). If the file has no +// extension, ext will be empty. +func fileExtSplit(file string) (name, ext string) { + dotExt := filepath.Ext(file) + name = file[:len(file)-len(dotExt)] + if dotExt != "" { + ext = dotExt[1:] + } + return +} + type stringsFlag []string func (v *stringsFlag) Set(s string) error { @@ -299,7 +312,13 @@ func runInstall(cmd *Command, args []string) { for _, p := range pkgs { if p.Target == "" && (!p.Standard || p.ImportPath != "unsafe") { - errorf("go install: no install location for directory %s outside GOPATH", p.Dir) + if p.cmdline { + errorf("go install: no install location for .go files listed on command line (GOBIN not set)") + } else if p.ConflictDir != "" { + errorf("go install: no install location for %s: hidden by %s", p.Dir, p.ConflictDir) + } else { + errorf("go install: no install location for directory %s outside GOPATH", p.Dir) + } } } exitIfErrors() @@ -416,7 +435,7 @@ func (b *builder) init() { fatalf("%s", err) } if buildX || buildWork { - fmt.Printf("WORK=%s\n", b.work) + fmt.Fprintf(os.Stderr, "WORK=%s\n", b.work) } if !buildWork { atexit(func() { os.RemoveAll(b.work) }) @@ -470,6 +489,7 @@ func goFilesPackage(gofiles []string) *Package { bp, err := ctxt.ImportDir(dir, 0) pkg := new(Package) pkg.local = true + pkg.cmdline = true pkg.load(&stk, bp, err) pkg.localPrefix = dirToImportPath(dir) pkg.ImportPath = "command-line-arguments" @@ -558,8 +578,12 @@ func (b *builder) action(mode buildMode, depMode buildMode, p *Package) *action // 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) + work := p.pkgdir + if work == "" { + work = b.work + } + a.objdir = filepath.Join(work, a.p.ImportPath, "_obj") + string(filepath.Separator) + a.objpkg = buildToolchain.pkgpath(work, a.p) a.link = p.Name == "main" switch mode { @@ -727,6 +751,15 @@ func hasString(strings []string, s string) bool { // build is the action for building a single package or command. func (b *builder) build(a *action) (err error) { + // Return an error if the package has CXX files but it's not using + // cgo nor SWIG, since the CXX files can only be processed by cgo + // and SWIG (it's possible to have packages with C files without + // using cgo, they will get compiled with the plan9 C compiler and + // linked with the rest of the package). + if len(a.p.CXXFiles) > 0 && !a.p.usesCgo() && !a.p.usesSwig() { + return fmt.Errorf("can't build package %s because it contains C++ files (%s) but it's not using cgo nor SWIG", + a.p.ImportPath, strings.Join(a.p.CXXFiles, ",")) + } defer func() { if err != nil && err != errPrintedOutput { err = fmt.Errorf("go build %s: %v", a.p.ImportPath, err) @@ -747,7 +780,7 @@ func (b *builder) build(a *action) (err error) { if a.p.Standard && a.p.ImportPath == "runtime" && buildContext.Compiler == "gc" && !hasString(a.p.HFiles, "zasm_"+buildContext.GOOS+"_"+buildContext.GOARCH+".h") { - return fmt.Errorf("%s/%s must be bootstrapped using make.bash", buildContext.GOOS, buildContext.GOARCH) + return fmt.Errorf("%s/%s must be bootstrapped using make%v", buildContext.GOOS, buildContext.GOARCH, defaultSuffix()) } // Make build directory. @@ -765,13 +798,32 @@ func (b *builder) build(a *action) (err error) { } var gofiles, cfiles, sfiles, objects, cgoObjects []string - gofiles = append(gofiles, a.p.GoFiles...) + + // If we're doing coverage, preprocess the .go files and put them in the work directory + if a.p.coverMode != "" { + for _, file := range a.p.GoFiles { + sourceFile := filepath.Join(a.p.Dir, file) + cover := a.p.coverVars[file] + if cover == nil || isTestFile(file) { + // Not covering this file. + gofiles = append(gofiles, file) + continue + } + coverFile := filepath.Join(obj, file) + if err := b.cover(a, coverFile, sourceFile, 0666, cover.Var); err != nil { + return err + } + gofiles = append(gofiles, coverFile) + } + } else { + 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. + if a.p.usesCgo() { + // In a package using cgo, cgo compiles the C, 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. @@ -799,7 +851,7 @@ func (b *builder) build(a *action) (err error) { if a.cgo != nil && a.cgo.target != "" { cgoExe = a.cgo.target } - outGo, outObj, err := b.cgo(a.p, cgoExe, obj, gccfiles) + outGo, outObj, err := b.cgo(a.p, cgoExe, obj, gccfiles, a.p.CXXFiles) if err != nil { return err } @@ -814,7 +866,7 @@ func (b *builder) build(a *action) (err error) { gccfiles := append(cfiles, sfiles...) cfiles = nil sfiles = nil - outGo, outObj, err := b.swig(a.p, obj, gccfiles) + outGo, outObj, err := b.swig(a.p, obj, gccfiles, a.p.CXXFiles) if err != nil { return err } @@ -843,23 +895,24 @@ func (b *builder) build(a *action) (err error) { // 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" + _goos_goarch := "_" + goos + "_" + goarch + _goos := "_" + goos + _goarch := "_" + goarch for _, file := range a.p.HFiles { + name, ext := fileExtSplit(file) switch { - case strings.HasSuffix(file, _goos_goarch): - targ := file[:len(file)-len(_goos_goarch)] + "_GOOS_GOARCH.h" + case strings.HasSuffix(name, _goos_goarch): + targ := file[:len(name)-len(_goos_goarch)] + "_GOOS_GOARCH." + ext 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" + case strings.HasSuffix(name, _goarch): + targ := file[:len(name)-len(_goarch)] + "_GOARCH." + ext 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" + case strings.HasSuffix(name, _goos): + targ := file[:len(name)-len(_goos)] + "_GOOS." + ext if err := b.copyFile(a, obj+targ, filepath.Join(a.p.Dir, file), 0666); err != nil { return err } @@ -955,8 +1008,9 @@ func (b *builder) install(a *action) (err error) { return err } soname := a.p.swigSoname(f) + source := filepath.Join(a.objdir, soname) target := filepath.Join(dir, soname) - if err = b.copyFile(a, target, soname, perm); err != nil { + if err = b.copyFile(a, target, source, perm); err != nil { return err } } @@ -997,8 +1051,8 @@ func (b *builder) includeArgs(flag string, all []*action) []string { dir = filepath.Join(dir, "gccgo_"+goos+"_"+goarch) } else { dir = filepath.Join(dir, goos+"_"+goarch) - if buildRace { - dir += "_race" + if buildContext.InstallSuffix != "" { + dir += "_" + buildContext.InstallSuffix } } inc = append(inc, flag, dir) @@ -1047,7 +1101,7 @@ func (b *builder) copyFile(a *action, dst, src string, perm os.FileMode) error { 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 + // If the move 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 + "~") @@ -1067,6 +1121,17 @@ func (b *builder) copyFile(a *action, dst, src string, perm os.FileMode) error { return nil } +// cover runs, in effect, +// go tool cover -mode=b.coverMode -var="varName" -o dst.go src.go +func (b *builder) cover(a *action, dst, src string, perm os.FileMode, varName string) error { + return b.run(a.objdir, "cover "+a.p.ImportPath, nil, + tool("cover"), + "-mode", a.p.coverMode, + "-var", varName, + "-o", dst, + src) +} + var objectMagic = [][]byte{ {'!', '<', 'a', 'r', 'c', 'h', '>', '\n'}, // Package archive {'\x7F', 'E', 'L', 'F'}, // ELF @@ -1454,7 +1519,7 @@ func (gcToolchain) gc(b *builder, p *Package, obj string, importArgs []string, g // so that it can give good error messages about forward declarations. // Exceptions: a few standard packages have forward declarations for // pieces supplied behind-the-scenes by package runtime. - extFiles := len(p.CgoFiles) + len(p.CFiles) + len(p.SFiles) + len(p.SysoFiles) + len(p.SwigFiles) + len(p.SwigCXXFiles) + extFiles := len(p.CgoFiles) + len(p.CFiles) + len(p.CXXFiles) + len(p.SFiles) + len(p.SysoFiles) + len(p.SwigFiles) + len(p.SwigCXXFiles) if p.Standard { switch p.ImportPath { case "os", "runtime/pprof", "sync", "time": @@ -1464,6 +1529,9 @@ func (gcToolchain) gc(b *builder, p *Package, obj string, importArgs []string, g if extFiles == 0 { gcargs = append(gcargs, "-complete") } + if buildContext.InstallSuffix != "" { + gcargs = append(gcargs, "-installsuffix", buildContext.InstallSuffix) + } args := stringList(tool(archChar+"g"), "-o", ofile, buildGcflags, gcargs, "-D", p.localPrefix, importArgs) for _, f := range gofiles { @@ -1496,6 +1564,7 @@ func (gcToolchain) ld(b *builder, p *Package, out string, allactions []*action, importArgs := b.includeArgs("-L", allactions) swigDirs := make(map[string]bool) swigArg := []string{} + cxx := false for _, a := range allactions { if a.p != nil && a.p.usesSwig() { sd := a.p.swigDir(&buildContext) @@ -1506,9 +1575,59 @@ func (gcToolchain) ld(b *builder, p *Package, out string, allactions []*action, swigArg[1] += sd } swigDirs[sd] = true + if a.objdir != "" && !swigDirs[a.objdir] { + swigArg[1] += ":" + swigArg[1] += a.objdir + swigDirs[a.objdir] = true + } + } + if a.p != nil && len(a.p.CXXFiles) > 0 { + cxx = true + } + } + ldflags := buildLdflags + if buildContext.InstallSuffix != "" { + ldflags = append(ldflags, "-installsuffix", buildContext.InstallSuffix) + } + if cxx { + // The program includes C++ code. If the user has not + // specified the -extld option, then default to + // linking with the compiler named by the CXX + // environment variable, or g++ if CXX is not set. + extld := false + for _, f := range ldflags { + if f == "-extld" || strings.HasPrefix(f, "-extld=") { + extld = true + break + } + } + if !extld { + compiler := strings.Fields(os.Getenv("CXX")) + if len(compiler) == 0 { + compiler = []string{"g++"} + } + ldflags = append(ldflags, "-extld="+compiler[0]) + if len(compiler) > 1 { + extldflags := false + add := strings.Join(compiler[1:], " ") + for i, f := range ldflags { + if f == "-extldflags" && i+1 < len(ldflags) { + ldflags[i+1] = add + " " + ldflags[i+1] + extldflags = true + break + } else if strings.HasPrefix(f, "-extldflags=") { + ldflags[i] = "-extldflags=" + add + " " + ldflags[i][len("-extldflags="):] + extldflags = true + break + } + } + if !extldflags { + ldflags = append(ldflags, "-extldflags="+add) + } + } } } - return b.run(".", p.ImportPath, nil, tool(archChar+"l"), "-o", out, importArgs, swigArg, buildLdflags, mainpkg) + return b.run(".", p.ImportPath, nil, tool(archChar+"l"), "-o", out, importArgs, swigArg, ldflags, mainpkg) } func (gcToolchain) cc(b *builder, p *Package, objdir, ofile, cfile string) error { @@ -1584,6 +1703,7 @@ func (tools gccgoToolchain) ld(b *builder, p *Package, out string, allactions [] ldflags := b.gccArchArgs() cgoldflags := []string{} usesCgo := false + cxx := false for _, a := range allactions { if a.p != nil { if !a.p.Standard { @@ -1597,12 +1717,18 @@ func (tools gccgoToolchain) ld(b *builder, p *Package, out string, allactions [] } if a.p.usesSwig() { sd := a.p.swigDir(&buildContext) + if a.objdir != "" { + sd = a.objdir + } for _, f := range stringList(a.p.SwigFiles, a.p.SwigCXXFiles) { soname := a.p.swigSoname(f) sfiles[a.p] = append(sfiles[a.p], filepath.Join(sd, soname)) } usesCgo = true } + if len(a.p.CXXFiles) > 0 { + cxx = true + } } } for _, afile := range afiles { @@ -1615,6 +1741,9 @@ func (tools gccgoToolchain) ld(b *builder, p *Package, out string, allactions [] if usesCgo && goos == "linux" { ldflags = append(ldflags, "-Wl,-E") } + if cxx { + ldflags = append(ldflags, "-lstdc++") + } return b.run(".", p.ImportPath, nil, "gccgo", "-o", out, ofiles, "-Wl,-(", ldflags, "-Wl,-)", buildGccgoflags) } @@ -1689,26 +1818,55 @@ func (b *builder) libgcc(p *Package) (string, error) { // 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, nil, b.gccCmd(p.Dir), flags, "-o", out, "-c", cfile) + return b.ccompile(p, out, flags, cfile, b.gccCmd(p.Dir)) } -// gccld runs the gcc linker to create an executable from a set of object files +// gxx runs the g++ C++ compiler to create an object from a single C++ file. +func (b *builder) gxx(p *Package, out string, flags []string, cxxfile string) error { + return b.ccompile(p, out, flags, cxxfile, b.gxxCmd(p.Dir)) +} + +// ccompile runs the given C or C++ compiler and creates an object from a single source file. +func (b *builder) ccompile(p *Package, out string, flags []string, file string, compiler []string) error { + file = mkAbs(p.Dir, file) + return b.run(p.Dir, p.ImportPath, nil, compiler, flags, "-o", out, "-c", file) +} + +// 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, nil, b.gccCmd(p.Dir), "-o", out, obj, flags) + var cmd []string + if len(p.CXXFiles) > 0 { + cmd = b.gxxCmd(p.Dir) + } else { + cmd = b.gccCmd(p.Dir) + } + return b.run(p.Dir, p.ImportPath, nil, cmd, "-o", out, obj, flags) } // gccCmd returns a gcc command line prefix +// defaultCC is defined in zdefaultcc.go, written by cmd/dist. func (b *builder) gccCmd(objdir string) []string { + return b.ccompilerCmd("CC", defaultCC, objdir) +} + +// gxxCmd returns a g++ command line prefix +// defaultCXX is defined in zdefaultcc.go, written by cmd/dist. +func (b *builder) gxxCmd(objdir string) []string { + return b.ccompilerCmd("CXX", defaultCXX, objdir) +} + +// ccompilerCmd returns a command line prefix for the given environment +// variable and using the default command when the variable is empty +func (b *builder) ccompilerCmd(envvar, defcmd, objdir string) []string { // NOTE: env.go's mkEnv knows that the first three // strings returned are "gcc", "-I", objdir (and cuts them off). - gcc := strings.Fields(os.Getenv("CC")) - if len(gcc) == 0 { - gcc = append(gcc, "gcc") + compiler := strings.Fields(os.Getenv(envvar)) + if len(compiler) == 0 { + compiler = strings.Fields(defcmd) } - a := []string{gcc[0], "-I", objdir, "-g", "-O2"} - a = append(a, gcc[1:]...) + a := []string{compiler[0], "-I", objdir, "-g", "-O2"} + a = append(a, compiler[1:]...) // Definitely want -fPIC but on Windows gcc complains // "-fPIC ignored for target (all code is position independent)" @@ -1727,8 +1885,10 @@ func (b *builder) gccCmd(objdir string) []string { } } - // clang is too smart about command-line arguments if strings.Contains(a[0], "clang") { + // disable ASCII art in clang errors, if possible + a = append(a, "-fno-caret-diagnostics") + // clang is too smart about command-line arguments a = append(a, "-Qunused-arguments") } @@ -1767,12 +1927,14 @@ var ( cgoLibGccFileOnce sync.Once ) -func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo, outObj []string, err error) { +func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string, gxxfiles []string) (outGo, outObj []string, err error) { if goos != toolGOOS { return nil, nil, errors.New("cannot use cgo when compiling for a different operating system") } + cgoCPPFLAGS := stringList(envList("CGO_CPPFLAGS"), p.CgoCPPFLAGS) cgoCFLAGS := stringList(envList("CGO_CFLAGS"), p.CgoCFLAGS) + cgoCXXFLAGS := stringList(envList("CGO_CXXFLAGS"), p.CgoCXXFLAGS) cgoLDFLAGS := stringList(envList("CGO_LDFLAGS"), p.CgoLDFLAGS) if pkgs := p.CgoPkgConfig; len(pkgs) > 0 { @@ -1783,7 +1945,7 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo, return nil, nil, errPrintedOutput } if len(out) > 0 { - cgoCFLAGS = append(cgoCFLAGS, strings.Fields(string(out))...) + cgoCPPFLAGS = append(cgoCPPFLAGS, strings.Fields(string(out))...) } out, err = b.runOut(p.Dir, p.ImportPath, nil, "pkg-config", "--libs", pkgs) if err != nil { @@ -1797,7 +1959,7 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo, } // Allows including _cgo_export.h from .[ch] files in the package. - cgoCFLAGS = append(cgoCFLAGS, "-I", obj) + cgoCPPFLAGS = append(cgoCPPFLAGS, "-I", obj) // cgo // TODO: CGOPKGPATH, CGO_FLAGS? @@ -1839,7 +2001,7 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo, } objExt = "o" } - if err := b.run(p.Dir, p.ImportPath, cgoenv, cgoExe, "-objdir", obj, cgoflags, "--", cgoCFLAGS, p.CgoFiles); err != nil { + if err := b.run(p.Dir, p.ImportPath, cgoenv, cgoExe, "-objdir", obj, cgoflags, "--", cgoCPPFLAGS, cgoCFLAGS, p.CgoFiles); err != nil { return nil, nil, err } outGo = append(outGo, gofiles...) @@ -1855,14 +2017,24 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo, var linkobj []string var bareLDFLAGS []string - // filter out -lsomelib, and -framework X if on Darwin + // filter out -lsomelib, -l somelib, *.{so,dll,dylib}, and (on Darwin) -framework X for i := 0; i < len(cgoLDFLAGS); i++ { f := cgoLDFLAGS[i] - if !strings.HasPrefix(f, "-l") { - if goos == "darwin" && f == "-framework" { // skip the -framework X - i += 1 - continue + switch { + // skip "-lc" or "-l somelib" + case strings.HasPrefix(f, "-l"): + if f == "-l" { + i++ } + // skip "-framework X" on Darwin + case goos == "darwin" && f == "-framework": + i++ + // skip "*.{dylib,so,dll}" + case strings.HasSuffix(f, ".dylib"), + strings.HasSuffix(f, ".so"), + strings.HasSuffix(f, ".dll"): + continue + default: bareLDFLAGS = append(bareLDFLAGS, f) } } @@ -1876,16 +2048,18 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo, var staticLibs []string if goos == "windows" { - // libmingw32 and libmingwex might also use libgcc, so libgcc must come last - staticLibs = []string{"-lmingwex", "-lmingw32"} + // libmingw32 and libmingwex might also use libgcc, so libgcc must come last, + // and they also have some inter-dependencies, so must use linker groups. + staticLibs = []string{"-Wl,--start-group", "-lmingwex", "-lmingw32", "-Wl,--end-group"} } if cgoLibGccFile != "" { staticLibs = append(staticLibs, cgoLibGccFile) } + cflags := stringList(cgoCPPFLAGS, cgoCFLAGS) for _, cfile := range cfiles { ofile := obj + cfile[:len(cfile)-1] + "o" - if err := b.gcc(p, ofile, cgoCFLAGS, obj+cfile); err != nil { + if err := b.gcc(p, ofile, cflags, obj+cfile); err != nil { return nil, nil, err } linkobj = append(linkobj, ofile) @@ -1893,14 +2067,27 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo, 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 { + if err := b.gcc(p, ofile, cflags, file); err != nil { return nil, nil, err } linkobj = append(linkobj, ofile) outObj = append(outObj, ofile) } + + cxxflags := stringList(cgoCPPFLAGS, cgoCXXFLAGS) + for _, file := range gxxfiles { + // Append .o to the file, just in case the pkg has file.c and file.cpp + ofile := obj + cgoRe.ReplaceAllString(file, "_") + ".o" + if err := b.gxx(p, ofile, cxxflags, file); err != nil { + return nil, nil, err + } + linkobj = append(linkobj, ofile) + outObj = append(outObj, ofile) + } + linkobj = append(linkobj, p.SysoFiles...) dynobj := obj + "_cgo_.o" if goarch == "arm" && goos == "linux" { // we need to use -pie for Linux/ARM to get accurate imported sym @@ -1956,7 +2143,27 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo, } // Run SWIG on all SWIG input files. -func (b *builder) swig(p *Package, obj string, gccfiles []string) (outGo, outObj []string, err error) { +// TODO: Don't build a shared library, once SWIG emits the necessary +// pragmas for external linking. +func (b *builder) swig(p *Package, obj string, gccfiles, gxxfiles []string) (outGo, outObj []string, err error) { + + var extraObj []string + for _, file := range gccfiles { + ofile := obj + cgoRe.ReplaceAllString(file[:len(file)-1], "_") + "o" + if err := b.gcc(p, ofile, nil, file); err != nil { + return nil, nil, err + } + extraObj = append(extraObj, ofile) + } + + for _, file := range gxxfiles { + // Append .o to the file, just in case the pkg has file.c and file.cpp + ofile := obj + cgoRe.ReplaceAllString(file, "_") + ".o" + if err := b.gxx(p, ofile, nil, file); err != nil { + return nil, nil, err + } + extraObj = append(extraObj, ofile) + } intgosize, err := b.swigIntSize(obj) if err != nil { @@ -1964,7 +2171,7 @@ func (b *builder) swig(p *Package, obj string, gccfiles []string) (outGo, outObj } for _, f := range p.SwigFiles { - goFile, objFile, err := b.swigOne(p, f, obj, false, intgosize) + goFile, objFile, err := b.swigOne(p, f, obj, false, intgosize, extraObj) if err != nil { return nil, nil, err } @@ -1976,7 +2183,7 @@ func (b *builder) swig(p *Package, obj string, gccfiles []string) (outGo, outObj } } for _, f := range p.SwigCXXFiles { - goFile, objFile, err := b.swigOne(p, f, obj, true, intgosize) + goFile, objFile, err := b.swigOne(p, f, obj, true, intgosize, extraObj) if err != nil { return nil, nil, err } @@ -1999,6 +2206,9 @@ const i int = 1 << 32 // Determine the size of int on the target system for the -intgosize option // of swig >= 2.0.9 func (b *builder) swigIntSize(obj string) (intsize string, err error) { + if buildN { + return "$INTBITS", nil + } src := filepath.Join(b.work, "swig_intsize.go") if err = ioutil.WriteFile(src, []byte(swigIntSizeCode), 0644); err != nil { return @@ -2014,7 +2224,7 @@ func (b *builder) swigIntSize(obj string) (intsize string, err error) { } // Run SWIG on one SWIG input file. -func (b *builder) swigOne(p *Package, file, obj string, cxx bool, intgosize string) (outGo, outObj string, err error) { +func (b *builder) swigOne(p *Package, file, obj string, cxx bool, intgosize string, extraObj []string) (outGo, outObj string, err error) { n := 5 // length of ".swig" if cxx { n = 8 // length of ".swigcxx" @@ -2085,7 +2295,8 @@ func (b *builder) swigOne(p *Package, file, obj string, cxx bool, intgosize stri cxxlib = []string{"-lstdc++"} } ldflags := stringList(osldflags[goos], cxxlib) - b.run(p.Dir, p.ImportPath, nil, b.gccCmd(p.Dir), "-o", soname, gccObj, ldflags) + target := filepath.Join(obj, soname) + b.run(p.Dir, p.ImportPath, nil, b.gccCmd(p.Dir), "-o", target, gccObj, extraObj, ldflags) return obj + goFile, cObj, nil } @@ -2130,3 +2341,16 @@ func raceInit() { buildContext.InstallSuffix += "race" buildContext.BuildTags = append(buildContext.BuildTags, "race") } + +// defaultSuffix returns file extension used for command files in +// current os environment. +func defaultSuffix() string { + switch runtime.GOOS { + case "windows": + return ".bat" + case "plan9": + return ".rc" + default: + return ".bash" + } +} diff --git a/src/cmd/go/clean.go b/src/cmd/go/clean.go index 8345c9af1..16687f72f 100644 --- a/src/cmd/go/clean.go +++ b/src/cmd/go/clean.go @@ -137,22 +137,38 @@ func clean(p *Package) { } _, elem := filepath.Split(p.Dir) - allRemove := []string{ - elem, - elem + ".exe", - elem + ".test", - elem + ".test.exe", + var allRemove []string + + // Remove dir-named executable only if this is package main. + if p.Name == "main" { + allRemove = append(allRemove, + elem, + elem+".exe", + ) } + + // Remove package test executables. + allRemove = append(allRemove, + elem+".test", + elem+".test.exe", + ) + + // Remove a potental executable for each .go file in the directory that + // is not part of the directory's package. for _, dir := range dirs { name := dir.Name() if packageFile[name] { continue } if !dir.IsDir() && strings.HasSuffix(name, ".go") { + // TODO(adg,rsc): check that this .go file is actually + // in "package main", and therefore capable of building + // to an executable file. 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, " ")) } @@ -221,7 +237,23 @@ func clean(p *Package) { // removeFile tries to remove file f, if error other than file doesn't exist // occurs, it will report the error. func removeFile(f string) { - if err := os.Remove(f); err != nil && !os.IsNotExist(err) { - errorf("go clean: %v", err) + err := os.Remove(f) + if err == nil || os.IsNotExist(err) { + return + } + // Windows does not allow deletion of a binary file while it is executing. + if toolIsWindows { + // Remove lingering ~ file from last attempt. + if _, err2 := os.Stat(f + "~"); err2 == nil { + os.Remove(f + "~") + } + // Try to move it out of the way. If the move fails, + // which is likely, we'll try again the + // next time we do an install of this binary. + if err2 := os.Rename(f, f+"~"); err2 == nil { + os.Remove(f + "~") + return + } } + errorf("go clean: %v", err) } diff --git a/src/cmd/go/discovery.go b/src/cmd/go/discovery.go index 047834050..75228b52a 100644 --- a/src/cmd/go/discovery.go +++ b/src/cmd/go/discovery.go @@ -13,17 +13,35 @@ package main import ( "encoding/xml" + "fmt" "io" "strings" ) +// charsetReader returns a reader for the given charset. Currently +// it only supports UTF-8 and ASCII. Otherwise, it returns a meaningful +// error which is printed by go get, so the user can find why the package +// wasn't downloaded if the encoding is not supported. Note that, in +// order to reduce potential errors, ASCII is treated as UTF-8 (i.e. characters +// greater than 0x7f are not rejected). +func charsetReader(charset string, input io.Reader) (io.Reader, error) { + switch strings.ToLower(charset) { + case "ascii": + return input, nil + default: + return nil, fmt.Errorf("can't decode XML document using charset %q", charset) + } +} + // 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) { +func parseMetaGoImports(r io.Reader) (imports []metaImport, err error) { d := xml.NewDecoder(r) + d.CharsetReader = charsetReader d.Strict = false + var t xml.Token for { - t, err := d.Token() + t, err = d.Token() if err != nil { return } diff --git a/src/cmd/go/doc.go b/src/cmd/go/doc.go index df82ab45b..ebb2f37fd 100644 --- a/src/cmd/go/doc.go +++ b/src/cmd/go/doc.go @@ -16,7 +16,6 @@ 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 @@ -33,9 +32,10 @@ Use "go help [command]" for more information about a command. Additional help topics: + c calling between Go and C gopath GOPATH environment variable + importpath import path syntax packages description of package lists - remote remote import path syntax testflag description of testing flags testfunc description of testing functions @@ -112,7 +112,8 @@ in an element in the list, surround it with either single or double quotes. For more about specifying packages, see 'go help packages'. For more about where packages and binaries are installed, -see 'go help gopath'. +run 'go help gopath'. For more about calling between Go and C/C++, +run 'go help c'. See also: go install, go get, go clean. @@ -162,26 +163,6 @@ 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 [-n] [-x] [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'. - -The -n flag prints commands that would be executed. -The -x flag prints commands as they are executed. - -To run godoc with specific options, run godoc itself. - -See also: go fix, go fmt, go vet. - - Print Go environment information Usage: @@ -229,14 +210,14 @@ The -x flag prints commands as they are executed. To run gofmt with specific options, run gofmt itself. -See also: go doc, go fix, go vet. +See also: go fix, go vet. Download and install packages and dependencies Usage: - go get [-d] [-fix] [-u] [build flags] [packages] + go get [-d] [-fix] [-t] [-u] [build flags] [packages] Get downloads and installs the packages named by the import paths, along with their dependencies. @@ -247,6 +228,9 @@ 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 -t flag instructs get to also download the packages required to build +the tests for the specified packages. + 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. @@ -263,7 +247,7 @@ 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'. +download, see 'go help importpath'. See also: go build, go install, go clean. @@ -287,7 +271,7 @@ List packages Usage: - go list [-e] [-f format] [-json] [-tags 'tag list'] [packages] + go list [-e] [-race] [-f format] [-json] [-tags 'tag list'] [packages] List lists the packages named by the import paths, one per line. @@ -318,14 +302,17 @@ which calls strings.Join. The struct being passed to the template is: CgoFiles []string // .go sources files that import "C" IgnoredGoFiles []string // .go sources ignored due to build constraints CFiles []string // .c source files - HFiles []string // .h source files + CXXFiles []string // .cc, .cxx and .cpp source files + HFiles []string // .h, .hh, .hpp and .hxx source files SFiles []string // .s source files - SysoFiles []string // .syso object files to add to archive SwigFiles []string // .swig files SwigCXXFiles []string // .swigcxx files + SysoFiles []string // .syso object files to add to archive // Cgo directives CgoCFLAGS []string // cgo: flags for C compiler + CgoCPPFLAGS []string // cgo: flags for C preprocessor + CgoCXXFLAGS []string // cgo: flags for C++ compiler CgoLDFLAGS []string // cgo: flags for linker CgoPkgConfig []string // cgo: pkg-config names @@ -360,6 +347,9 @@ a non-nil Error field; other information may or may not be missing The -tags flag specifies a list of build tags, like in the 'go build' command. +The -race flag causes the package data to include the dependencies +required by the race detector. + For more about specifying packages, see 'go help packages'. @@ -370,7 +360,7 @@ Usage: go run [build flags] gofiles... [arguments...] Run compiles and runs the main package comprising the named Go source files. -If no files are named, it compiles and runs all non-test Go source files. +A Go source file is defined to be a file ending in a literal ".go" suffix. For more about build flags, see 'go help build'. @@ -381,7 +371,7 @@ Test packages Usage: - go test [-c] [-i] [build flags] [packages] [flags for test binary] + go test [-c] [-i] [build and test 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: @@ -394,8 +384,10 @@ It prints a summary of the test results in the format: 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. +the file pattern "*_test.go". +Files whose names begin with "_" (including "_test.go") or "." are ignored. +These additional files can contain test functions, benchmark functions, and +example functions. See 'go help testfunc' for more. Each listed package causes the execution of a separate test binary. Test files that declare a package with the suffix "_test" will be compiled as a @@ -419,6 +411,11 @@ In addition to the build flags, the flags handled by 'go test' itself are: 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. +If the test binary needs any other flags, they should be presented after the +package names. The go tool treats as a flag the first argument that begins with +a minus sign that it does not recognize itself; that argument and all subsequent +arguments are passed as arguments to the test binary. + For more about build flags, see 'go help build'. For more about specifying packages, see 'go help packages'. @@ -457,7 +454,7 @@ Usage: Vet runs the Go vet command on the packages named by the import paths. -For more about vet, see 'godoc vet'. +For more about vet, see 'godoc code.google.com/p/go.tools/cmd/vet'. For more about specifying packages, see 'go help packages'. To run the vet tool with specific options, run 'go tool vet'. @@ -468,6 +465,25 @@ The -x flag prints commands as they are executed. See also: go fmt, go fix. +Calling between Go and C + +There are two different ways to call between Go and C/C++ code. + +The first is the cgo tool, which is part of the Go distribution. For +information on how to use it see the cgo documentation (godoc cmd/cgo). + +The second is the SWIG program, which is a general tool for +interfacing between languages. For information on SWIG see +http://swig.org/. When running go build, any file with a .swig +extension will be passed to SWIG. Any file with a .swigcxx extension +will be passed to SWIG with the -c++ option. + +When either cgo or SWIG is used, go build will pass any .c, .s, or .S +files to the C compiler, and any .cc, .cpp, .cxx files to the C++ +compiler. The CC or CXX environment variables may be set to determine +the C or C++ compiler, respectively, to use. + + GOPATH environment variable The Go path is used to resolve import statements. @@ -528,59 +544,40 @@ 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 +Import path syntax An import path (see 'go help packages') denotes a package -stored in the local file system. Certain import paths also +stored in the local file system. In general, an import path denotes +either a standard package (such as "unicode/utf8") or a package +found in one of the work spaces (see 'go help gopath'). + +Relative import paths + +An import path beginning with ./ or ../ is called a relative path. +The toolchain supports relative import paths as a shortcut in two ways. + +First, a relative path can be used as a shorthand on the command line. +If you are working in the directory containing the code imported as +"unicode" and want to run the tests for "unicode/utf8", you can type +"go test ./utf8" instead of needing to specify the full path. +Similarly, in the reverse situation, "go test .." will test "unicode" from +the "unicode/utf8" directory. Relative patterns are also allowed, like +"go test ./..." to test all subdirectories. See 'go help packages' for details +on the pattern syntax. + +Second, if you are compiling a Go program not in a work space, +you can use a relative path in an import statement in that program +to refer to nearby code also not in a work space. +This makes it easy to experiment with small multipackage programs +outside of the usual work spaces, but such programs cannot be +installed with "go install" (there is no work space in which to install them), +so they are rebuilt from scratch each time they are built. +To avoid ambiguity, Go programs cannot use relative import paths +within a work space. + +Remote import paths + +Certain import paths also describe how to obtain the source code for the package using a revision control system. @@ -691,6 +688,62 @@ package appropriate for the Go release being used. Run 'go help install' for more. +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. + +There are three reserved names for paths that should not be used +for packages to be built with the go tool: + +- "main" denotes the top-level package in a stand-alone executable. + +- "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. + +- "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 importpath' 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. + +File names that begin with "." or "_" are ignored by the go tool. + + Description of testing flags The 'go test' command takes both flags that apply to 'go test' itself @@ -730,6 +783,30 @@ control the execution of any test: if -test.blockprofile is set without this flag, all blocking events are recorded, equivalent to -test.blockprofilerate=1. + -cover + Enable coverage analysis. + + -covermode set,count,atomic + Set the mode for coverage analysis for the package[s] + being tested. The default is "set". + The values: + set: bool: does this statement run? + count: int: how many times does this statement run? + atomic: int: count, but correct in multithreaded tests; + significantly more expensive. + Sets -cover. + + -coverpkg pkg1,pkg2,pkg3 + Apply coverage analysis in each test to the given list of packages. + The default is for each test to analyze only the package being tested. + Packages are specified as import paths. + Sets -cover. + + -coverprofile cover.out + Write a coverage profile to the specified file after all tests + have passed. + Sets -cover. + -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 @@ -750,6 +827,10 @@ control the execution of any test: garbage collector, provided the test can run in the available memory without garbage collection. + -outputdir directory + Place output files from profiling in the specified directory, + by default the directory in which "go test" is running. + -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 @@ -787,8 +868,8 @@ will compile the test binary and then run it as pkg.test -test.v -test.cpuprofile=prof.out -dir=testdata -update -The test flags that generate profiles also leave the test binary in pkg.test -for use when analyzing the profiles. +The test flags that generate profiles (other than for coverage) also +leave the test binary in pkg.test for use when analyzing the profiles. Flags not recognized by 'go test' must be placed after any specified packages. @@ -837,5 +918,3 @@ See the documentation of the testing package for more information. */ package main - -// NOTE: cmdDoc is in fmt.go. diff --git a/src/cmd/go/env.go b/src/cmd/go/env.go index 00e03e9bd..2db821797 100644 --- a/src/cmd/go/env.go +++ b/src/cmd/go/env.go @@ -45,12 +45,17 @@ func mkEnv() []envVar { {"GORACE", os.Getenv("GORACE")}, {"GOROOT", goroot}, {"GOTOOLDIR", toolDir}, + + // disable escape codes in clang errors + {"TERM", "dumb"}, } if goos != "plan9" { cmd := b.gccCmd(".") env = append(env, envVar{"CC", cmd[0]}) env = append(env, envVar{"GOGCCFLAGS", strings.Join(cmd[3:], " ")}) + cmd = b.gxxCmd(".") + env = append(env, envVar{"CXX", cmd[0]}) } if buildContext.CgoEnabled { diff --git a/src/cmd/go/fmt.go b/src/cmd/go/fmt.go index 9d3c911dd..65dc3ca59 100644 --- a/src/cmd/go/fmt.go +++ b/src/cmd/go/fmt.go @@ -6,7 +6,6 @@ package main func init() { addBuildFlagsNX(cmdFmt) - addBuildFlagsNX(cmdDoc) } var cmdFmt = &Command{ @@ -25,7 +24,7 @@ The -x flag prints commands as they are executed. To run gofmt with specific options, run gofmt itself. -See also: go doc, go fix, go vet. +See also: go fix, go vet. `, } @@ -37,37 +36,3 @@ func runFmt(cmd *Command, args []string) { run(stringList("gofmt", "-l", "-w", relPaths(pkg.allgofiles))) } } - -var cmdDoc = &Command{ - Run: runDoc, - UsageLine: "doc [-n] [-x] [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'. - -The -n flag prints commands that would be executed. -The -x flag prints commands as they are executed. - -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 - } - if pkg.local { - run("godoc", pkg.Dir) - } else { - run("godoc", pkg.ImportPath) - } - } -} diff --git a/src/cmd/go/get.go b/src/cmd/go/get.go index 8c08ab261..e61da7e2a 100644 --- a/src/cmd/go/get.go +++ b/src/cmd/go/get.go @@ -18,7 +18,7 @@ import ( ) var cmdGet = &Command{ - UsageLine: "get [-d] [-fix] [-u] [build flags] [packages]", + UsageLine: "get [-d] [-fix] [-t] [-u] [build flags] [packages]", Short: "download and install packages and dependencies", Long: ` Get downloads and installs the packages named by the import paths, @@ -30,6 +30,9 @@ 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 -t flag instructs get to also download the packages required to build +the tests for the specified packages. + 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. @@ -46,13 +49,14 @@ 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'. +download, see 'go help importpath'. See also: go build, go install, go clean. `, } var getD = cmdGet.Flag.Bool("d", false, "") +var getT = cmdGet.Flag.Bool("t", false, "") var getU = cmdGet.Flag.Bool("u", false, "") var getFix = cmdGet.Flag.Bool("fix", false, "") @@ -65,7 +69,7 @@ func runGet(cmd *Command, args []string) { // Phase 1. Download/update. var stk importStack for _, arg := range downloadPaths(args) { - download(arg, &stk) + download(arg, &stk, *getT) } exitIfErrors() @@ -137,7 +141,7 @@ 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) { +func download(arg string, stk *importStack, getTestDeps bool) { p := loadPackage(arg, stk) // There's nothing to do if this is a package in the standard library. @@ -153,6 +157,7 @@ func download(arg string, stk *importStack) { pkgs := []*Package{p} wildcardOkay := len(*stk) == 0 + isWildcard := false // Download if the package is missing, or update if we're using -u. if p.Dir == "" || *getU { @@ -175,6 +180,7 @@ func download(arg string, stk *importStack) { } else { args = matchPackages(arg) } + isWildcard = true } // Clear all relevant package cache entries before @@ -214,9 +220,30 @@ func download(arg string, stk *importStack) { } } + if isWildcard { + // Report both the real package and the + // wildcard in any error message. + stk.push(p.ImportPath) + } + // Process dependencies, now that we know what they are. for _, dep := range p.deps { - download(dep.ImportPath, stk) + // Don't get test dependencies recursively. + download(dep.ImportPath, stk, false) + } + if getTestDeps { + // Process test dependencies when -t is specified. + // (Don't get test dependencies for test dependencies.) + for _, path := range p.TestImports { + download(path, stk, false) + } + for _, path := range p.XTestImports { + download(path, stk, false) + } + } + + if isWildcard { + stk.pop() } } } @@ -286,7 +313,7 @@ func downloadPackage(p *Package) error { } // Some version control tools require the parent of the target to exist. parent, _ := filepath.Split(root) - if err := os.MkdirAll(parent, 0777); err != nil { + if err = os.MkdirAll(parent, 0777); err != nil { return err } if err = vcs.create(root, repo); err != nil { diff --git a/src/cmd/go/help.go b/src/cmd/go/help.go index c70a25fdd..71e55175a 100644 --- a/src/cmd/go/help.go +++ b/src/cmd/go/help.go @@ -4,6 +4,28 @@ package main +var helpC = &Command{ + UsageLine: "c", + Short: "calling between Go and C", + Long: ` +There are two different ways to call between Go and C/C++ code. + +The first is the cgo tool, which is part of the Go distribution. For +information on how to use it see the cgo documentation (godoc cmd/cgo). + +The second is the SWIG program, which is a general tool for +interfacing between languages. For information on SWIG see +http://swig.org/. When running go build, any file with a .swig +extension will be passed to SWIG. Any file with a .swigcxx extension +will be passed to SWIG with the -c++ option. + +When either cgo or SWIG is used, go build will pass any .c, .s, or .S +files to the C compiler, and any .cc, .cpp, .cxx files to the C++ +compiler. The CC or CXX environment variables may be set to determine +the C or C++ compiler, respectively, to use. + `, +} + var helpPackages = &Command{ UsageLine: "packages", Short: "description of package lists", @@ -25,12 +47,17 @@ 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. +There are three reserved names for paths that should not be used +for packages to be built with the go tool: + +- "main" denotes the top-level package in a stand-alone executable. -The special import path "std" is like all but expands to just the -packages in the standard Go library. +- "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. + +- "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 @@ -40,7 +67,7 @@ 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. +a remote repository. Run 'go help importpath' for details. Every package in a program must have a unique import path. By convention, this is arranged by starting each path with a @@ -53,16 +80,48 @@ 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. + +File names that begin with "." or "_" are ignored by the go tool. `, } -var helpRemote = &Command{ - UsageLine: "remote", - Short: "remote import path syntax", +var helpImportPath = &Command{ + UsageLine: "importpath", + Short: "import path syntax", Long: ` An import path (see 'go help packages') denotes a package -stored in the local file system. Certain import paths also +stored in the local file system. In general, an import path denotes +either a standard package (such as "unicode/utf8") or a package +found in one of the work spaces (see 'go help gopath'). + +Relative import paths + +An import path beginning with ./ or ../ is called a relative path. +The toolchain supports relative import paths as a shortcut in two ways. + +First, a relative path can be used as a shorthand on the command line. +If you are working in the directory containing the code imported as +"unicode" and want to run the tests for "unicode/utf8", you can type +"go test ./utf8" instead of needing to specify the full path. +Similarly, in the reverse situation, "go test .." will test "unicode" from +the "unicode/utf8" directory. Relative patterns are also allowed, like +"go test ./..." to test all subdirectories. See 'go help packages' for details +on the pattern syntax. + +Second, if you are compiling a Go program not in a work space, +you can use a relative path in an import statement in that program +to refer to nearby code also not in a work space. +This makes it easy to experiment with small multipackage programs +outside of the usual work spaces, but such programs cannot be +installed with "go install" (there is no work space in which to install them), +so they are rebuilt from scratch each time they are built. +To avoid ambiguity, Go programs cannot use relative import paths +within a work space. + +Remote import paths + +Certain import paths also describe how to obtain the source code for the package using a revision control system. diff --git a/src/cmd/go/list.go b/src/cmd/go/list.go index 2d23d077e..f56ebed38 100644 --- a/src/cmd/go/list.go +++ b/src/cmd/go/list.go @@ -14,7 +14,7 @@ import ( ) var cmdList = &Command{ - UsageLine: "list [-e] [-f format] [-json] [-tags 'tag list'] [packages]", + UsageLine: "list [-e] [-race] [-f format] [-json] [-tags 'tag list'] [packages]", Short: "list packages", Long: ` List lists the packages named by the import paths, one per line. @@ -46,14 +46,17 @@ which calls strings.Join. The struct being passed to the template is: CgoFiles []string // .go sources files that import "C" IgnoredGoFiles []string // .go sources ignored due to build constraints CFiles []string // .c source files - HFiles []string // .h source files + CXXFiles []string // .cc, .cxx and .cpp source files + HFiles []string // .h, .hh, .hpp and .hxx source files SFiles []string // .s source files - SysoFiles []string // .syso object files to add to archive SwigFiles []string // .swig files SwigCXXFiles []string // .swigcxx files + SysoFiles []string // .syso object files to add to archive // Cgo directives CgoCFLAGS []string // cgo: flags for C compiler + CgoCPPFLAGS []string // cgo: flags for C preprocessor + CgoCXXFLAGS []string // cgo: flags for C++ compiler CgoLDFLAGS []string // cgo: flags for linker CgoPkgConfig []string // cgo: pkg-config names @@ -88,6 +91,9 @@ a non-nil Error field; other information may or may not be missing The -tags flag specifies a list of build tags, like in the 'go build' command. +The -race flag causes the package data to include the dependencies +required by the race detector. + For more about specifying packages, see 'go help packages'. `, } @@ -101,12 +107,17 @@ func init() { var listE = cmdList.Flag.Bool("e", false, "") var listFmt = cmdList.Flag.String("f", "{{.ImportPath}}", "") var listJson = cmdList.Flag.Bool("json", false, "") +var listRace = cmdList.Flag.Bool("race", false, "") var nl = []byte{'\n'} func runList(cmd *Command, args []string) { out := newTrackingWriter(os.Stdout) defer out.w.Flush() + if *listRace { + buildRace = true + } + var do func(*Package) if *listJson { do = func(p *Package) { diff --git a/src/cmd/go/main.go b/src/cmd/go/main.go index 3180dbeed..df0cf1b3f 100644 --- a/src/cmd/go/main.go +++ b/src/cmd/go/main.go @@ -76,7 +76,6 @@ func (c *Command) Runnable() bool { var commands = []*Command{ cmdBuild, cmdClean, - cmdDoc, cmdEnv, cmdFix, cmdFmt, @@ -89,9 +88,10 @@ var commands = []*Command{ cmdVersion, cmdVet, + helpC, helpGopath, + helpImportPath, helpPackages, - helpRemote, helpTestflag, helpTestfunc, } @@ -144,6 +144,11 @@ func main() { } } + if fi, err := os.Stat(goroot); err != nil || !fi.IsDir() { + fmt.Fprintf(os.Stderr, "go: cannot find GOROOT directory: %v\n", goroot) + os.Exit(2) + } + for _, cmd := range commands { if cmd.Name() == args[0] && cmd.Run != nil { cmd.Flag.Usage = func() { cmd.Usage() } @@ -208,8 +213,6 @@ var documentationTemplate = `// Copyright 2011 The Go Authors. All rights reser {{end}}*/ package main - -// NOTE: cmdDoc is in fmt.go. ` // tmpl executes the given template text on data, writing the result to w. @@ -358,7 +361,7 @@ func exitIfErrors() { func run(cmdargs ...interface{}) { cmdline := stringList(cmdargs...) - if buildN || buildV { + if buildN || buildX { fmt.Printf("%s\n", strings.Join(cmdline, " ")) if buildN { return @@ -432,6 +435,37 @@ func matchPattern(pattern string) func(name string) bool { } } +// hasPathPrefix reports whether the path s begins with the +// elements in prefix. +func hasPathPrefix(s, prefix string) bool { + switch { + default: + return false + case len(s) == len(prefix): + return s == prefix + case len(s) > len(prefix): + if prefix != "" && prefix[len(prefix)-1] == '/' { + return strings.HasPrefix(s, prefix) + } + return s[len(prefix)] == '/' && s[:len(prefix)] == prefix + } +} + +// treeCanMatchPattern(pattern)(name) reports whether +// name or children of name can possibly match pattern. +// Pattern is the same limited glob accepted by matchPattern. +func treeCanMatchPattern(pattern string) func(name string) bool { + wildCard := false + if i := strings.Index(pattern, "..."); i >= 0 { + wildCard = true + pattern = pattern[:i] + } + return func(name string) bool { + return len(name) <= len(pattern) && hasPathPrefix(pattern, name) || + wildCard && strings.HasPrefix(name, pattern) + } +} + // 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) @@ -446,8 +480,10 @@ func allPackages(pattern string) []string { func matchPackages(pattern string) []string { match := func(string) bool { return true } + treeCanMatch := func(string) bool { return true } if pattern != "all" && pattern != "std" { match = matchPattern(pattern) + treeCanMatch = treeCanMatchPattern(pattern) } have := map[string]bool{ @@ -465,6 +501,9 @@ func matchPackages(pattern string) []string { return nil } name := path[len(cmd):] + if !treeCanMatch(name) { + return filepath.SkipDir + } // Commands are all in cmd/, not in subdirectories. if strings.Contains(name, string(filepath.Separator)) { return filepath.SkipDir @@ -481,6 +520,9 @@ func matchPackages(pattern string) []string { } _, err = buildContext.ImportDir(path, 0) if err != nil { + if _, noGo := err.(*build.NoGoError); !noGo { + log.Print(err) + } return nil } pkgs = append(pkgs, name) @@ -507,6 +549,9 @@ func matchPackages(pattern string) []string { if pattern == "std" && strings.Contains(name, ".") { return filepath.SkipDir } + if !treeCanMatch(name) { + return filepath.SkipDir + } if have[name] { return nil } @@ -515,8 +560,10 @@ func matchPackages(pattern string) []string { return nil } _, err = buildContext.ImportDir(path, 0) - if err != nil && strings.Contains(err.Error(), "no Go source files") { - return nil + if err != nil { + if _, noGo := err.(*build.NoGoError); noGo { + return nil + } } pkgs = append(pkgs, name) return nil @@ -583,6 +630,9 @@ func matchPackagesInFS(pattern string) []string { return nil } if _, err = build.ImportDir(path, 0); err != nil { + if _, noGo := err.(*build.NoGoError); !noGo { + log.Print(err) + } return nil } pkgs = append(pkgs, name) diff --git a/src/cmd/go/match_test.go b/src/cmd/go/match_test.go index f058f235a..38b9b115e 100644 --- a/src/cmd/go/match_test.go +++ b/src/cmd/go/match_test.go @@ -6,11 +6,7 @@ package main import "testing" -var matchTests = []struct { - pattern string - path string - match bool -}{ +var matchPatternTests = []stringPairTest{ {"...", "foo", true}, {"net", "net", true}, {"net", "net/http", false}, @@ -27,10 +23,66 @@ var matchTests = []struct { } 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) + testStringPairs(t, "matchPattern", matchPatternTests, func(pattern, name string) bool { + return matchPattern(pattern)(name) + }) +} + +var treeCanMatchPatternTests = []stringPairTest{ + {"...", "foo", true}, + {"net", "net", true}, + {"net", "net/http", false}, + {"net/http", "net", true}, + {"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}, + {"abc.../def", "abcxyz", true}, + {"abc.../def", "xyxabc", false}, + {"x/y/z/...", "x", true}, + {"x/y/z/...", "x/y", true}, + {"x/y/z/...", "x/y/z", true}, + {"x/y/z/...", "x/y/z/w", true}, + {"x/y/z", "x", true}, + {"x/y/z", "x/y", true}, + {"x/y/z", "x/y/z", true}, + {"x/y/z", "x/y/z/w", false}, + {"x/.../y/z", "x/a/b/c", true}, + {"x/.../y/z", "y/x/a/b/c", false}, +} + +func TestChildrenCanMatchPattern(t *testing.T) { + testStringPairs(t, "treeCanMatchPattern", treeCanMatchPatternTests, func(pattern, name string) bool { + return treeCanMatchPattern(pattern)(name) + }) +} + +var hasPathPrefixTests = []stringPairTest{ + {"abc", "a", false}, + {"a/bc", "a", true}, + {"a", "a", true}, + {"a/bc", "a/", true}, +} + +func TestHasPathPrefix(t *testing.T) { + testStringPairs(t, "hasPathPrefix", hasPathPrefixTests, hasPathPrefix) +} + +type stringPairTest struct { + in1 string + in2 string + out bool +} + +func testStringPairs(t *testing.T, name string, tests []stringPairTest, f func(string, string) bool) { + for _, tt := range tests { + if out := f(tt.in1, tt.in2); out != tt.out { + t.Errorf("%s(%q, %q) = %v, want %v", name, tt.in1, tt.in2, out, tt.out) } } } diff --git a/src/cmd/go/pkg.go b/src/cmd/go/pkg.go index b33d800bf..71f14c74a 100644 --- a/src/cmd/go/pkg.go +++ b/src/cmd/go/pkg.go @@ -25,29 +25,33 @@ 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 + 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 + ConflictDir string `json:",omitempty"` // Dir is hidden by this other directory // Source files GoFiles []string `json:",omitempty"` // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles) CgoFiles []string `json:",omitempty"` // .go sources files that import "C" IgnoredGoFiles []string `json:",omitempty"` // .go sources ignored due to build constraints CFiles []string `json:",omitempty"` // .c source files - HFiles []string `json:",omitempty"` // .h source files + CXXFiles []string `json:",omitempty"` // .cc, .cpp and .cxx source files + HFiles []string `json:",omitempty"` // .h, .hh, .hpp and .hxx source files SFiles []string `json:",omitempty"` // .s source files - SysoFiles []string `json:",omitempty"` // .syso system object files added to package SwigFiles []string `json:",omitempty"` // .swig files SwigCXXFiles []string `json:",omitempty"` // .swigcxx files + SysoFiles []string `json:",omitempty"` // .syso system object files added to package // Cgo directives CgoCFLAGS []string `json:",omitempty"` // cgo: flags for C compiler + CgoCPPFLAGS []string `json:",omitempty"` // cgo: flags for C preprocessor + CgoCXXFLAGS []string `json:",omitempty"` // cgo: flags for C++ compiler CgoLDFLAGS []string `json:",omitempty"` // cgo: flags for linker CgoPkgConfig []string `json:",omitempty"` // cgo: pkg-config names @@ -73,14 +77,23 @@ type Package struct { deps []*Package gofiles []string // GoFiles+CgoFiles+TestGoFiles+XTestGoFiles files, absolute paths sfiles []string - allgofiles []string // gofiles + IgnoredGoFiles, absolute paths - target string // installed file for this package (may be executable) - fake bool // synthesized package - forceBuild bool // this package must be rebuilt - forceLibrary bool // this package is a library (even if named "main") - local bool // imported via local path (./ or ../) - localPrefix string // interpret ./ and ../ imports relative to this prefix - exeName string // desired name for temporary executable + allgofiles []string // gofiles + IgnoredGoFiles, absolute paths + target string // installed file for this package (may be executable) + fake bool // synthesized package + forceBuild bool // this package must be rebuilt + forceLibrary bool // this package is a library (even if named "main") + cmdline bool // defined by files listed on command line + local bool // imported via local path (./ or ../) + localPrefix string // interpret ./ and ../ imports relative to this prefix + exeName string // desired name for temporary executable + coverMode string // preprocess Go source files with the coverage tool in this mode + coverVars map[string]*CoverVar // variables created by coverage analysis +} + +// CoverVar holds the name of the generated coverage variables targeting the named file. +type CoverVar struct { + File string // local file name + Var string // name of count struct } func (p *Package) copyBuild(pp *build.Package) { @@ -91,6 +104,7 @@ func (p *Package) copyBuild(pp *build.Package) { p.Name = pp.Name p.Doc = pp.Doc p.Root = pp.Root + p.ConflictDir = pp.ConflictDir // TODO? Target p.Goroot = pp.Goroot p.Standard = p.Goroot && p.ImportPath != "" && !strings.Contains(p.ImportPath, ".") @@ -98,12 +112,15 @@ func (p *Package) copyBuild(pp *build.Package) { p.CgoFiles = pp.CgoFiles p.IgnoredGoFiles = pp.IgnoredGoFiles p.CFiles = pp.CFiles + p.CXXFiles = pp.CXXFiles p.HFiles = pp.HFiles p.SFiles = pp.SFiles - p.SysoFiles = pp.SysoFiles p.SwigFiles = pp.SwigFiles p.SwigCXXFiles = pp.SwigCXXFiles + p.SysoFiles = pp.SysoFiles p.CgoCFLAGS = pp.CgoCFLAGS + p.CgoCPPFLAGS = pp.CgoCPPFLAGS + p.CgoCXXFLAGS = pp.CgoCXXFLAGS p.CgoLDFLAGS = pp.CgoLDFLAGS p.CgoPkgConfig = pp.CgoPkgConfig p.Imports = pp.Imports @@ -115,12 +132,17 @@ func (p *Package) copyBuild(pp *build.Package) { // A PackageError describes an error loading information about a package. type PackageError struct { - ImportStack []string // shortest path from package named on command line to this one - Pos string // position of error - Err string // the error itself + ImportStack []string // shortest path from package named on command line to this one + Pos string // position of error + Err string // the error itself + isImportCycle bool // the error is an import cycle } func (p *PackageError) Error() string { + // Import cycles deserve special treatment. + if p.isImportCycle { + return fmt.Sprintf("%s: %s\npackage %s\n", p.Pos, p.Err, strings.Join(p.ImportStack, "\n\timports ")) + } if p.Pos != "" { // Omit import stack. The full path to the file where the error // is the most important thing. @@ -257,26 +279,38 @@ func reusePackage(p *Package, stk *importStack) *Package { if p.imports == nil { if p.Error == nil { p.Error = &PackageError{ - ImportStack: stk.copy(), - Err: "import cycle not allowed", + ImportStack: stk.copy(), + Err: "import cycle not allowed", + isImportCycle: true, } } p.Incomplete = true } - if p.Error != nil && stk.shorterThan(p.Error.ImportStack) { + // Don't rewrite the import stack in the error if we have an import cycle. + // If we do, we'll lose the path that describes the cycle. + if p.Error != nil && !p.Error.isImportCycle && 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, +type targetDir int + +const ( + toRoot targetDir = iota // to bin dir inside package root (default) + toTool // GOROOT/pkg/tool + toBin // GOROOT/bin +) + +// goTools is a map of Go program import path to install target directory. +var goTools = map[string]targetDir{ + "cmd/api": toTool, + "cmd/cgo": toTool, + "cmd/fix": toTool, + "cmd/yacc": toTool, + "code.google.com/p/go.tools/cmd/cover": toTool, + "code.google.com/p/go.tools/cmd/godoc": toBin, + "code.google.com/p/go.tools/cmd/vet": toTool, } // expandScanner expands a scanner.List error into all the errors in the list. @@ -300,6 +334,23 @@ func expandScanner(err error) error { return err } +var raceExclude = map[string]bool{ + "runtime/race": true, + "runtime/cgo": true, + "cmd/cgo": true, + "syscall": true, + "errors": true, +} + +var cgoExclude = map[string]bool{ + "runtime/cgo": true, +} + +var cgoSyscallExclude = map[string]bool{ + "runtime/cgo": true, + "runtime/race": true, +} + // 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 { @@ -326,10 +377,18 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package // Install cross-compiled binaries to subdirectories of bin. elem = full } - if p.build.BinDir != "" { + if p.build.BinDir != gobin && goTools[p.ImportPath] == toBin { + // Override BinDir. + // This is from a subrepo but installs to $GOROOT/bin + // by default anyway (like godoc). + p.target = filepath.Join(gorootBin, elem) + } else if p.build.BinDir != "" { + // Install to GOBIN or bin of GOPATH entry. p.target = filepath.Join(p.build.BinDir, elem) } - if p.Goroot && (isGoTool[p.ImportPath] || strings.HasPrefix(p.ImportPath, "exp/")) { + if goTools[p.ImportPath] == toTool { + // This is for 'go tool'. + // Override all the usual logic and force it into the tool directory. p.target = filepath.Join(gorootPkg, "tool", full) } if p.target != "" && buildContext.GOOS == "windows" { @@ -344,17 +403,22 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package } 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") { + // Packages that use cgo import runtime/cgo implicitly. + // Packages that use cgo also import syscall implicitly, + // to wrap errno. + // Exclude certain packages to avoid circular dependencies. + if len(p.CgoFiles) > 0 && (!p.Standard || !cgoExclude[p.ImportPath]) { importPaths = append(importPaths, "runtime/cgo") } + if len(p.CgoFiles) > 0 && (!p.Standard || !cgoSyscallExclude[p.ImportPath]) { + importPaths = append(importPaths, "syscall") + } // Everything depends on runtime, except runtime and unsafe. if !p.Standard || (p.ImportPath != "runtime" && p.ImportPath != "unsafe") { importPaths = append(importPaths, "runtime") // When race detection enabled everything depends on runtime/race. - // Exclude runtime/cgo and cmd/cgo to avoid circular dependencies. - if buildRace && (!p.Standard || (p.ImportPath != "runtime/race" && p.ImportPath != "runtime/cgo" && p.ImportPath != "cmd/cgo")) { + // Exclude certain packages to avoid circular dependencies. + if buildRace && (!p.Standard || !raceExclude[p.ImportPath]) { importPaths = append(importPaths, "runtime/race") } } @@ -389,6 +453,7 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package p.CgoFiles, p.IgnoredGoFiles, p.CFiles, + p.CXXFiles, p.HFiles, p.SFiles, p.SysoFiles, @@ -476,11 +541,16 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package return p } -// usesSwig returns whether the package needs to run SWIG. +// usesSwig reports whether the package needs to run SWIG. func (p *Package) usesSwig() bool { return len(p.SwigFiles) > 0 || len(p.SwigCXXFiles) > 0 } +// usesCgo reports whether the package needs to run cgo +func (p *Package) usesCgo() bool { + return len(p.CgoFiles) > 0 +} + // swigSoname returns the name of the shared library we create for a // SWIG input file. func (p *Package) swigSoname(file string) string { @@ -611,28 +681,13 @@ func isStale(p *Package, topRoot map[string]bool) bool { return false } - srcs := stringList(p.GoFiles, p.CFiles, p.HFiles, p.SFiles, p.CgoFiles, p.SysoFiles) + srcs := stringList(p.GoFiles, p.CFiles, p.CXXFiles, p.HFiles, p.SFiles, p.CgoFiles, p.SysoFiles, p.SwigFiles, p.SwigCXXFiles) for _, src := range srcs { if olderThan(filepath.Join(p.Dir, src)) { return true } } - for _, src := range stringList(p.SwigFiles, p.SwigCXXFiles) { - if olderThan(filepath.Join(p.Dir, src)) { - return true - } - soname := p.swigSoname(src) - fi, err := os.Stat(soname) - if err != nil { - return true - } - fiSrc, err := os.Stat(src) - if err != nil || fiSrc.ModTime().After(fi.ModTime()) { - return true - } - } - return false } diff --git a/src/cmd/go/run.go b/src/cmd/go/run.go index 91bdc1be2..e6dadd229 100644 --- a/src/cmd/go/run.go +++ b/src/cmd/go/run.go @@ -16,7 +16,7 @@ var cmdRun = &Command{ Short: "compile and run Go program", Long: ` Run compiles and runs the main package comprising the named Go source files. -If no files are named, it compiles and runs all non-test Go source files. +A Go source file is defined to be a file ending in a literal ".go" suffix. For more about build flags, see 'go help build'. diff --git a/src/cmd/go/signal_notunix.go b/src/cmd/go/signal_notunix.go index ef13c1919..29aa9d8c2 100644 --- a/src/cmd/go/signal_notunix.go +++ b/src/cmd/go/signal_notunix.go @@ -11,3 +11,7 @@ import ( ) var signalsToIgnore = []os.Signal{os.Interrupt} + +// signalTrace is the signal to send to make a Go program +// crash with a stack trace. +var signalTrace os.Signal = nil diff --git a/src/cmd/go/signal_unix.go b/src/cmd/go/signal_unix.go index 489a73b83..00c71657f 100644 --- a/src/cmd/go/signal_unix.go +++ b/src/cmd/go/signal_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin freebsd linux netbsd openbsd +// +build darwin dragonfly freebsd linux netbsd openbsd package main @@ -12,3 +12,7 @@ import ( ) var signalsToIgnore = []os.Signal{os.Interrupt, syscall.SIGQUIT} + +// signalTrace is the signal to send to make a Go program +// crash with a stack trace. +var signalTrace os.Signal = syscall.SIGQUIT diff --git a/src/cmd/go/test.bash b/src/cmd/go/test.bash index e2264a46e..f71d67818 100755 --- a/src/cmd/go/test.bash +++ b/src/cmd/go/test.bash @@ -5,15 +5,44 @@ set -e go build -o testgo +go() { + echo TEST ERROR: ran go, not testgo: go "$@" >&2 + exit 2 +} + +started=false +TEST() { + if $started; then + stop + fi + echo TEST: "$@" + started=true + ok=true +} +stop() { + if ! $started; then + echo TEST ERROR: stop missing start >&2 + exit 2 + fi + started=false + if $ok; then + echo PASS + else + echo FAIL + allok=false + fi +} ok=true +allok=true unset GOPATH unset GOBIN +TEST 'file:line in error messages' # Test that error messages have file:line information at beginning of # the line. Also test issue 4917: that the error is on stderr. -d=$(mktemp -d -t testgoXXX) +d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX) fn=$d/err.go echo "package main" > $fn echo 'import "bar"' >> $fn @@ -28,6 +57,7 @@ rm -r $d # Test local (./) imports. testlocal() { local="$1" + TEST local imports $2 '(easy)' ./testgo build -o hello "testdata/$local/easy.go" ./hello >hello.out if ! grep -q '^easysub\.Hello' hello.out; then @@ -36,6 +66,7 @@ testlocal() { ok=false fi + TEST local imports $2 '(easysub)' ./testgo build -o hello "testdata/$local/easysub/main.go" ./hello >hello.out if ! grep -q '^easysub\.Hello' hello.out; then @@ -44,6 +75,7 @@ testlocal() { ok=false fi + TEST local imports $2 '(hard)' ./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 @@ -55,6 +87,7 @@ testlocal() { rm -f hello.out hello # Test that go install x.go fails. + TEST local imports $2 '(go install should fail)' if ./testgo install "testdata/$local/easy.go" >/dev/null 2>&1; then echo "go install testdata/$local/easy.go succeeded" ok=false @@ -62,22 +95,53 @@ testlocal() { } # Test local imports -testlocal local +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" +testlocal "$bad" 'with bad characters in path' rm -rf "testdata/$bad" +TEST error message for syntax error in test go file says FAIL +export GOPATH=$(pwd)/testdata +if ./testgo test syntaxerror 2>testdata/err; then + echo 'go test syntaxerror succeeded' + ok=false +elif ! grep FAIL testdata/err >/dev/null; then + echo 'go test did not say FAIL:' + cat testdata/err + ok=false +fi +rm -f ./testdata/err +unset GOPATH + +TEST wildcards do not look in useless directories +export GOPATH=$(pwd)/testdata +if ./testgo list ... >testdata/err 2>&1; then + echo "go list ... succeeded" + ok=false +elif ! grep badpkg testdata/err >/dev/null; then + echo "go list ... failure does not mention badpkg" + cat testdata/err + ok=false +elif ! ./testgo list m... >testdata/err 2>&1; then + echo "go list m... failed" + ok=false +fi +rm -rf ./testdata/err +unset GOPATH + # Test tests with relative imports. +TEST relative imports '(go test)' if ! ./testgo test ./testdata/testimport; then echo "go test ./testdata/testimport failed" ok=false fi # Test installation with relative imports. +TEST relative imports '(go test -i)' if ! ./testgo test -i ./testdata/testimport; then echo "go test -i ./testdata/testimport failed" ok=false @@ -85,13 +149,40 @@ fi # Test tests with relative imports in packages synthesized # from Go files named on the command line. +TEST relative imports in command-line package if ! ./testgo test ./testdata/testimport/*.go; then echo "go test ./testdata/testimport/*.go failed" ok=false fi +TEST version control error message includes correct directory +export GOPATH=$(pwd)/testdata/shadow/root1 +if ./testgo get -u foo 2>testdata/err; then + echo "go get -u foo succeeded unexpectedly" + ok=false +elif ! grep testdata/shadow/root1/src/foo testdata/err >/dev/null; then + echo "go get -u error does not mention shadow/root1/src/foo:" + cat testdata/err + ok=false +fi +unset GOPATH + +TEST go install fails with no buildable files +export GOPATH=$(pwd)/testdata +export CGO_ENABLED=0 +if ./testgo install cgotest 2>testdata/err; then + echo "go install cgotest succeeded unexpectedly" +elif ! grep 'no buildable Go source files' testdata/err >/dev/null; then + echo "go install cgotest did not report 'no buildable Go source files'" + cat testdata/err + ok=false +fi +unset CGO_ENABLED +unset GOPATH + # Test that without $GOBIN set, binaries get installed # into the GOPATH bin directory. +TEST install into GOPATH rm -rf testdata/bin if ! GOPATH=$(pwd)/testdata ./testgo install go-cmd-test; then echo "go install go-cmd-test failed" @@ -101,7 +192,29 @@ elif ! test -x testdata/bin/go-cmd-test; then ok=false fi +TEST package main_test imports archive not binary +export GOBIN=$(pwd)/testdata/bin +mkdir -p $GOBIN +export GOPATH=$(pwd)/testdata +touch ./testdata/src/main_test/m.go +if ! ./testgo test main_test; then + echo "go test main_test failed without install" + ok=false +elif ! ./testgo install main_test; then + echo "go test main_test failed" + ok=false +elif [ "$(./testgo list -f '{{.Stale}}' main_test)" != false ]; then + echo "after go install, main listed as stale" + ok=false +elif ! ./testgo test main_test; then + echo "go test main_test failed after install" + ok=false +fi +rm -rf $GOBIN +unset GOBIN + # And with $GOBIN set, binaries get installed to $GOBIN. +TEST install into GOBIN if ! GOBIN=$(pwd)/testdata/bin1 GOPATH=$(pwd)/testdata ./testgo install go-cmd-test; then echo "go install go-cmd-test failed" ok=false @@ -112,12 +225,19 @@ 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 +TEST install without destination fails +if ./testgo install testdata/src/go-cmd-test/helloworld.go 2>testdata/err; then echo "go install testdata/src/go-cmd-test/helloworld.go should have failed, did not" ok=false +elif ! grep 'no install location for .go files listed on command line' testdata/err; then + echo "wrong error:" + cat testdata/err + ok=false fi +rm -f testdata/err # With $GOBIN set, should install there. +TEST install to GOBIN '(command-line package)' 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 @@ -126,24 +246,88 @@ elif ! test -x testdata/bin1/helloworld; then ok=false fi +TEST godoc installs into GOBIN +d=$(mktemp -d -t testgoXXX) +export GOPATH=$d +mkdir $d/gobin +GOBIN=$d/gobin ./testgo get code.google.com/p/go.tools/cmd/godoc +if [ ! -x $d/gobin/godoc ]; then + echo did not install godoc to '$GOBIN' + GOBIN=$d/gobin ./testgo list -f 'Target: {{.Target}}' code.google.com/p/go.tools/cmd/godoc + ok=false +fi + +TEST godoc installs into GOROOT +rm -f $GOROOT/bin/godoc +./testgo install code.google.com/p/go.tools/cmd/godoc +if [ ! -x $GOROOT/bin/godoc ]; then + echo did not install godoc to '$GOROOT/bin' + ./testgo list -f 'Target: {{.Target}}' code.google.com/p/go.tools/cmd/godoc + ok=false +fi + +TEST cmd/fix installs into tool +GOOS=$(./testgo env GOOS) +GOARCH=$(./testgo env GOARCH) +rm -f $GOROOT/pkg/tool/${GOOS}_${GOARCH}/fix +./testgo install cmd/fix +if [ ! -x $GOROOT/pkg/tool/${GOOS}_${GOARCH}/fix ]; then + echo 'did not install cmd/fix to $GOROOT/pkg/tool' + GOBIN=$d/gobin ./testgo list -f 'Target: {{.Target}}' cmd/fix + ok=false +fi +rm -f $GOROOT/pkg/tool/${GOOS}_${GOARCH}/fix +GOBIN=$d/gobin ./testgo install cmd/fix +if [ ! -x $GOROOT/pkg/tool/${GOOS}_${GOARCH}/fix ]; then + echo 'did not install cmd/fix to $GOROOT/pkg/tool with $GOBIN set' + GOBIN=$d/gobin ./testgo list -f 'Target: {{.Target}}' cmd/fix + ok=false +fi + +TEST gopath program installs into GOBIN +mkdir $d/src/progname +echo 'package main; func main() {}' >$d/src/progname/p.go +GOBIN=$d/gobin ./testgo install progname +if [ ! -x $d/gobin/progname ]; then + echo 'did not install progname to $GOBIN/progname' + ./testgo list -f 'Target: {{.Target}}' cmd/api + ok=false +fi +rm -f $d/gobin/progname $d/bin/progname + +TEST gopath program installs into GOPATH/bin +./testgo install progname +if [ ! -x $d/bin/progname ]; then + echo 'did not install progname to $GOPATH/bin/progname' + ./testgo list -f 'Target: {{.Target}}' progname + ok=false +fi + +unset GOPATH +rm -rf $d + # Reject relative paths in GOPATH. +TEST reject relative paths in GOPATH '(command-line package)' if GOPATH=. ./testgo build testdata/src/go-cmd-test/helloworld.go; then echo 'GOPATH="." go build should have failed, did not' ok=false fi +TEST reject relative paths in GOPATH if GOPATH=:$(pwd)/testdata:. ./testgo build go-cmd-test; then echo 'GOPATH=":$(pwd)/testdata:." go build should have failed, did not' ok=false fi # issue 4104 +TEST go test with package listed multiple times if [ $(./testgo test fmt fmt fmt fmt fmt | wc -l) -ne 1 ] ; then echo 'go test fmt fmt fmt fmt fmt tested the same package multiple times' ok=false fi # ensure that output of 'go list' is consistent between runs +TEST go list is consistent ./testgo list std > test_std.list if ! ./testgo list std | cmp -s test_std.list - ; then echo "go list std ordering is inconsistent" @@ -151,32 +335,38 @@ if ! ./testgo list std | cmp -s test_std.list - ; then fi rm -f test_std.list -# issue 4096. Validate the output of unsucessful go install foo/quxx +# issue 4096. Validate the output of unsuccessful go install foo/quxx +TEST unsuccessful go install should mention missing package if [ $(./testgo install 'foo/quxx' 2>&1 | grep -c 'cannot find package "foo/quxx" in any of') -ne 1 ] ; then echo 'go install foo/quxx expected error: .*cannot find package "foo/quxx" in any of' ok=false fi # test GOROOT search failure is reported +TEST GOROOT search failure reporting if [ $(./testgo install 'foo/quxx' 2>&1 | egrep -c 'foo/quxx \(from \$GOROOT\)$') -ne 1 ] ; then echo 'go install foo/quxx expected error: .*foo/quxx (from $GOROOT)' ok=false fi # test multiple GOPATH entries are reported separately +TEST multiple GOPATH entries reported separately if [ $(GOPATH=$(pwd)/testdata/a:$(pwd)/testdata/b ./testgo install 'foo/quxx' 2>&1 | egrep -c 'testdata/./src/foo/quxx') -ne 2 ] ; then echo 'go install foo/quxx expected error: .*testdata/a/src/foo/quxx (from $GOPATH)\n.*testdata/b/src/foo/quxx' ok=false fi # test (from $GOPATH) annotation is reported for the first GOPATH entry +TEST mention GOPATH in first GOPATH entry if [ $(GOPATH=$(pwd)/testdata/a:$(pwd)/testdata/b ./testgo install 'foo/quxx' 2>&1 | egrep -c 'testdata/a/src/foo/quxx \(from \$GOPATH\)$') -ne 1 ] ; then echo 'go install foo/quxx expected error: .*testdata/a/src/foo/quxx (from $GOPATH)' ok=false fi # but not on the second +TEST but not the second entry if [ $(GOPATH=$(pwd)/testdata/a:$(pwd)/testdata/b ./testgo install 'foo/quxx' 2>&1 | egrep -c 'testdata/b/src/foo/quxx$') -ne 1 ] ; then echo 'go install foo/quxx expected error: .*testdata/b/src/foo/quxx' ok=false fi # test missing GOPATH is reported +TEST missing GOPATH is reported if [ $(GOPATH= ./testgo install 'foo/quxx' 2>&1 | egrep -c '\(\$GOPATH not set\)$') -ne 1 ] ; then echo 'go install foo/quxx expected error: ($GOPATH not set)' ok=false @@ -184,6 +374,7 @@ fi # issue 4186. go get cannot be used to download packages to $GOROOT # Test that without GOPATH set, go get should fail +TEST without GOPATH, go get fails d=$(mktemp -d -t testgoXXX) mkdir -p $d/src/pkg if GOPATH= GOROOT=$d ./testgo get -d code.google.com/p/go.codereview/cmd/hgpatch ; then @@ -191,7 +382,9 @@ if GOPATH= GOROOT=$d ./testgo get -d code.google.com/p/go.codereview/cmd/hgpatch ok=false fi rm -rf $d + # Test that with GOPATH=$GOROOT, go get should fail +TEST with GOPATH=GOROOT, go get fails d=$(mktemp -d -t testgoXXX) mkdir -p $d/src/pkg if GOPATH=$d GOROOT=$d ./testgo get -d code.google.com/p/go.codereview/cmd/hgpatch ; then @@ -200,7 +393,7 @@ if GOPATH=$d GOROOT=$d ./testgo get -d code.google.com/p/go.codereview/cmd/hgpat fi rm -rf $d -# issue 3941: args with spaces +TEST ldflags arguments with spaces '(issue 3941)' d=$(mktemp -d -t testgoXXX) cat >$d/main.go<<EOF package main @@ -215,9 +408,9 @@ if ! grep -q '^hello world' hello.out; then cat hello.out ok=false fi -rm -rf $d +rm -rf $d hello.out -# test that go test -cpuprofile leaves binary behind +TEST go test -cpuprofile leaves binary behind ./testgo test -cpuprofile strings.prof strings || ok=false if [ ! -x strings.test ]; then echo "go test -cpuprofile did not create strings.test" @@ -225,9 +418,10 @@ if [ ! -x strings.test ]; then fi rm -f strings.prof strings.test -# issue 4568. test that symlinks don't screw things up too badly. +TEST symlinks do not confuse go list '(issue 4568)' old=$(pwd) -d=$(mktemp -d -t testgoXXX) +tmp=$(cd /tmp && pwd -P) +d=$(TMPDIR=$tmp mktemp -d -t testgoXXX) mkdir -p $d/src ( ln -s $d $d/src/dir1 @@ -235,8 +429,8 @@ mkdir -p $d/src echo package p >p.go export GOPATH=$d if [ "$($old/testgo list -f '{{.Root}}' .)" != "$d" ]; then - echo got lost in symlink tree: - pwd + echo Confused by symlinks. + echo "Package in current directory $(pwd) should have Root $d" env|grep WD $old/testgo list -json . dir1 touch $d/failed @@ -247,8 +441,8 @@ if [ -f $d/failed ]; then fi rm -rf $d -# issue 4515. -d=$(mktemp -d -t testgoXXX) +TEST 'install with tags (issue 4515)' +d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX) mkdir -p $d/src/example/a $d/src/example/b $d/bin cat >$d/src/example/a/main.go <<EOF package main @@ -280,8 +474,8 @@ fi unset GOPATH rm -rf $d -# issue 4773. case-insensitive collisions -d=$(mktemp -d -t testgoXXX) +TEST case collisions '(issue 4773)' +d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX) export GOPATH=$d mkdir -p $d/src/example/a $d/src/example/b cat >$d/src/example/a/a.go <<EOF @@ -318,17 +512,120 @@ elif ! grep "case-insensitive file name collision" $d/out >/dev/null; then echo go list example/b did not report file name collision. ok=false fi + +TEST go get cover +./testgo get code.google.com/p/go.tools/cmd/cover || ok=false + unset GOPATH rm -rf $d +TEST shadowing logic +export GOPATH=$(pwd)/testdata/shadow/root1:$(pwd)/testdata/shadow/root2 + +# The math in root1 is not "math" because the standard math is. +cdir=$(./testgo list -f '({{.ImportPath}}) ({{.ConflictDir}})' ./testdata/shadow/root1/src/math) +if [ "$cdir" != "(_$(pwd)/testdata/shadow/root1/src/math) ($GOROOT/src/pkg/math)" ]; then + echo shadowed math is not shadowed: "$cdir" + ok=false +fi + +# The foo in root1 is "foo". +cdir=$(./testgo list -f '({{.ImportPath}}) ({{.ConflictDir}})' ./testdata/shadow/root1/src/foo) +if [ "$cdir" != "(foo) ()" ]; then + echo unshadowed foo is shadowed: "$cdir" + ok=false +fi + +# The foo in root2 is not "foo" because the foo in root1 got there first. +cdir=$(./testgo list -f '({{.ImportPath}}) ({{.ConflictDir}})' ./testdata/shadow/root2/src/foo) +if [ "$cdir" != "(_$(pwd)/testdata/shadow/root2/src/foo) ($(pwd)/testdata/shadow/root1/src/foo)" ]; then + echo shadowed foo is not shadowed: "$cdir" + ok=false +fi + +# The error for go install should mention the conflicting directory. +err=$(! ./testgo install ./testdata/shadow/root2/src/foo 2>&1) +if [ "$err" != "go install: no install location for directory $(pwd)/testdata/shadow/root2/src/foo hidden by $(pwd)/testdata/shadow/root1/src/foo" ]; then + echo wrong shadowed install error: "$err" + ok=false +fi + # Only succeeds if source order is preserved. -./testgo test testdata/example[12]_test.go +TEST source file name order preserved +./testgo test testdata/example[12]_test.go || ok=false + +# Check that coverage analysis works at all. +# Don't worry about the exact numbers +TEST coverage runs +./testgo test -short -coverpkg=strings strings regexp || ok=false +./testgo test -short -cover strings math regexp || ok=false + +TEST cgo depends on syscall +rm -rf $GOROOT/pkg/*_race +d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX) +export GOPATH=$d +mkdir -p $d/src/foo +echo ' +package foo +//#include <stdio.h> +import "C" +' >$d/src/foo/foo.go +./testgo build -race foo || ok=false +rm -rf $d +unset GOPATH + +TEST cgo shows full path names +d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX) +export GOPATH=$d +mkdir -p $d/src/x/y/dirname +echo ' +package foo +import "C" +func f() { +' >$d/src/x/y/dirname/foo.go +if ./testgo build x/y/dirname >$d/err 2>&1; then + echo build succeeded unexpectedly. + ok=false +elif ! grep x/y/dirname $d/err >/dev/null; then + echo error did not use full path. + cat $d/err + ok=false +fi +rm -rf $d +unset GOPATH + +TEST 'cgo handles -Wl,$ORIGIN' +d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX) +export GOPATH=$d +mkdir -p $d/src/origin +echo ' +package origin +// #cgo LDFLAGS: -Wl,-rpath -Wl,$ORIGIN +// void f(void) {} +import "C" + +func f() { C.f() } +' >$d/src/origin/origin.go +if ! ./testgo build origin; then + echo build failed + ok=false +fi +rm -rf $d +unset GOPATH + +TEST 'Issue 6480: "go test -c -test.bench=XXX fmt" should not hang' +if ! ./testgo test -c -test.bench=XXX fmt; then + echo build test failed + ok=false +fi +rm -f fmt.test # clean up +if $started; then stop; fi rm -rf testdata/bin testdata/bin1 rm -f testgo -if $ok; then +if $allok; then echo PASS else echo FAIL diff --git a/src/cmd/go/test.go b/src/cmd/go/test.go index b1db16f77..06ac9d206 100644 --- a/src/cmd/go/test.go +++ b/src/cmd/go/test.go @@ -12,10 +12,12 @@ import ( "go/doc" "go/parser" "go/token" + "log" "os" "os/exec" "path" "path/filepath" + "regexp" "runtime" "sort" "strings" @@ -32,7 +34,7 @@ func init() { var cmdTest = &Command{ CustomFlags: true, - UsageLine: "test [-c] [-i] [build flags] [packages] [flags for test binary]", + UsageLine: "test [-c] [-i] [build and test flags] [packages] [flags for test binary]", Short: "test packages", Long: ` 'Go test' automates testing the packages named by the import paths. @@ -46,8 +48,10 @@ It prints a summary of the test results in the format: 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. +the file pattern "*_test.go". +Files whose names begin with "_" (including "_test.go") or "." are ignored. +These additional files can contain test functions, benchmark functions, and +example functions. See 'go help testfunc' for more. Each listed package causes the execution of a separate test binary. Test files that declare a package with the suffix "_test" will be compiled as a @@ -71,6 +75,11 @@ In addition to the build flags, the flags handled by 'go test' itself are: 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. +If the test binary needs any other flags, they should be presented after the +package names. The go tool treats as a flag the first argument that begins with +a minus sign that it does not recognize itself; that argument and all subsequent +arguments are passed as arguments to the test binary. + For more about build flags, see 'go help build'. For more about specifying packages, see 'go help packages'. @@ -119,6 +128,30 @@ control the execution of any test: if -test.blockprofile is set without this flag, all blocking events are recorded, equivalent to -test.blockprofilerate=1. + -cover + Enable coverage analysis. + + -covermode set,count,atomic + Set the mode for coverage analysis for the package[s] + being tested. The default is "set". + The values: + set: bool: does this statement run? + count: int: how many times does this statement run? + atomic: int: count, but correct in multithreaded tests; + significantly more expensive. + Sets -cover. + + -coverpkg pkg1,pkg2,pkg3 + Apply coverage analysis in each test to the given list of packages. + The default is for each test to analyze only the package being tested. + Packages are specified as import paths. + Sets -cover. + + -coverprofile cover.out + Write a coverage profile to the specified file after all tests + have passed. + Sets -cover. + -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 @@ -139,6 +172,10 @@ control the execution of any test: garbage collector, provided the test can run in the available memory without garbage collection. + -outputdir directory + Place output files from profiling in the specified directory, + by default the directory in which "go test" is running. + -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 @@ -176,8 +213,8 @@ will compile the test binary and then run it as pkg.test -test.v -test.cpuprofile=prof.out -dir=testdata -update -The test flags that generate profiles also leave the test binary in pkg.test -for use when analyzing the profiles. +The test flags that generate profiles (other than for coverage) also +leave the test binary in pkg.test for use when analyzing the profiles. Flags not recognized by 'go test' must be placed after any specified packages. `, @@ -229,12 +266,17 @@ See the documentation of the testing package for more information. } var ( - testC bool // -c flag - testProfile bool // some profiling flag - testI bool // -i flag - testV bool // -v flag - testFiles []string // -file flag(s) TODO: not respected - testTimeout string // -timeout flag + testC bool // -c flag + testCover bool // -cover flag + testCoverMode string // -covermode flag + testCoverPaths []string // -coverpkg flag + testCoverPkgs []*Package // -coverpkg flag + testProfile bool // some profiling flag + testNeedBinary bool // profile needs to keep binary around + 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 @@ -243,6 +285,12 @@ var ( testKillTimeout = 10 * time.Minute ) +var testMainDeps = map[string]bool{ + // Dependencies for testmain. + "testing": true, + "regexp": true, +} + func runTest(cmd *Command, args []string) { var pkgArgs []string pkgArgs, testArgs = testFlags(args) @@ -289,11 +337,11 @@ func runTest(cmd *Command, args []string) { if testI { buildV = testV - deps := map[string]bool{ - // Dependencies for testmain. - "testing": true, - "regexp": true, + deps := make(map[string]bool) + for dep := range testMainDeps { + deps[dep] = true } + for _, p := range pkgs { // Dependencies for each test. for _, path := range p.Imports { @@ -331,7 +379,7 @@ func runTest(cmd *Command, args []string) { a.deps = append(a.deps, b.action(modeInstall, modeInstall, p)) } b.do(a) - if !testC { + if !testC || a.failed { return } b.init() @@ -339,6 +387,34 @@ func runTest(cmd *Command, args []string) { var builds, runs, prints []*action + if testCoverPaths != nil { + // Load packages that were asked about for coverage. + // packagesForBuild exits if the packages cannot be loaded. + testCoverPkgs = packagesForBuild(testCoverPaths) + + // Warn about -coverpkg arguments that are not actually used. + used := make(map[string]bool) + for _, p := range pkgs { + used[p.ImportPath] = true + for _, dep := range p.Deps { + used[dep] = true + } + } + for _, p := range testCoverPkgs { + if !used[p.ImportPath] { + log.Printf("warning: no packages being tested depend on %s", p.ImportPath) + } + } + + // Mark all the coverage packages for rebuilding with coverage. + for _, p := range testCoverPkgs { + p.Stale = true // rebuild + p.fake = true // do not warn about rebuild + p.coverMode = testCoverMode + p.coverVars = declareCoverVars(p.ImportPath, p.GoFiles...) + } + } + // Prepare build + run + print actions for all packages being tested. for _, p := range pkgs { buildTest, runTest, printTest, err := b.test(p) @@ -347,10 +423,12 @@ func runTest(cmd *Command, args []string) { if strings.HasPrefix(str, "\n") { str = str[1:] } + failed := fmt.Sprintf("FAIL\t%s [setup failed]\n", p.ImportPath) + if p.ImportPath != "" { - errorf("# %s\n%s", p.ImportPath, str) + errorf("# %s\n%s\n%s", p.ImportPath, str, failed) } else { - errorf("%s", str) + errorf("%s\n%s", str, failed) } continue } @@ -370,16 +448,15 @@ func runTest(cmd *Command, args []string) { } } - // 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]) + // Force benchmarks to run in serial. + if !testC && testBench { + // The first run must wait for all builds. + // Later runs must wait for the previous run's print. + for i, run := range runs { + if i == 0 { + run.deps = append(run.deps, builds...) + } else { + run.deps = append(run.deps, prints[i-1]) } } } @@ -390,11 +467,22 @@ func runTest(cmd *Command, args []string) { 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 a.p == nil || okBuild[a.p] { + continue + } + okBuild[a.p] = true // warn at most once + + // Don't warn about packages being rebuilt because of + // things like coverage analysis. + for _, p1 := range a.p.imports { + if p1.fake { + a.p.fake = true + } + } + + if a.f != nil && !okBuild[a.p] && !a.p.fake && !a.p.local { if !warned { fmt.Fprintf(os.Stderr, "warning: building out-of-date packages:\n") warned = true @@ -417,11 +505,20 @@ func runTest(cmd *Command, args []string) { b.do(root) } +func contains(x []string, s string) bool { + for _, t := range x { + if t == s { + return true + } + } + return false +} + 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}} + run := &action{p: p, deps: []*action{build}} + print := &action{f: (*builder).notest, p: p, deps: []*action{run}} return build, run, print, nil } @@ -487,12 +584,15 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action, 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 - } + + // Should we apply coverage analysis locally, + // only for this package and only for this test? + // Yes, if -cover is on but -coverpkg has not specified + // a list of packages for global coverage. + localCover := testCover && testCoverPaths == nil // Test package. - if len(p.TestGoFiles) > 0 { + if len(p.TestGoFiles) > 0 || localCover || p.Name == "main" { ptest = new(Package) *ptest = *p ptest.GoFiles = nil @@ -515,6 +615,11 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action, m[k] = append(m[k], v...) } ptest.build.ImportPos = m + + if localCover { + ptest.coverMode = testCoverMode + ptest.coverVars = declareCoverVars(ptest.ImportPath, ptest.GoFiles...) + } } else { ptest = p } @@ -548,6 +653,7 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action, Root: p.Root, imports: []*Package{ptest}, build: &build.Package{Name: "main"}, + pkgdir: testDir, fake: true, Stale: true, } @@ -557,15 +663,50 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action, // 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 + for dep := range testMainDeps { + if ptest.ImportPath != dep { + p1 := loadImport("testing", "", &stk, nil) + if p1.Error != nil { + return nil, nil, nil, p1.Error + } + pmain.imports = append(pmain.imports, p1) + } + } + + if testCoverPkgs != nil { + // Add imports, but avoid duplicates. + seen := map[*Package]bool{p: true, ptest: true} + for _, p1 := range pmain.imports { + seen[p1] = true + } + for _, p1 := range testCoverPkgs { + if !seen[p1] { + seen[p1] = true + pmain.imports = append(pmain.imports, p1) + } + } } - pregexp := loadImport("regexp", "", &stk, nil) - if pregexp.Error != nil { - return nil, nil, nil, pregexp.Error + + if ptest != p && localCover { + // We have made modifications to the package p being tested + // and are rebuilding p (as ptest), writing it to the testDir tree. + // Arrange to rebuild, writing to that same tree, all packages q + // such that the test depends on q, and q depends on p. + // This makes sure that q sees the modifications to p. + // Strictly speaking, the rebuild is only necessary if the + // modifications to p change its export metadata, but + // determining that is a bit tricky, so we rebuild always. + // + // This will cause extra compilation, so for now we only do it + // when testCover is set. The conditions are more general, though, + // and we may find that we need to do it always in the future. + recompileForTest(pmain, p, ptest, testDir) } - pmain.imports = append(pmain.imports, ptesting, pregexp) + + if err := writeTestmain(filepath.Join(testDir, "_testmain.go"), pmain, ptest); err != nil { + return nil, nil, nil, err + } + computeStale(pmain) if ptest != p { @@ -589,7 +730,7 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action, a.target = filepath.Join(testDir, testBinary) + exeSuffix pmainAction := a - if testC || testProfile { + if testC || testNeedBinary { // -c or profiling flag: create action to copy binary to ./test.out. runAction = &action{ f: (*builder).install, @@ -624,6 +765,75 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action, return pmainAction, runAction, printAction, nil } +func recompileForTest(pmain, preal, ptest *Package, testDir string) { + // The "test copy" of preal is ptest. + // For each package that depends on preal, make a "test copy" + // that depends on ptest. And so on, up the dependency tree. + testCopy := map[*Package]*Package{preal: ptest} + for _, p := range packageList([]*Package{pmain}) { + // Copy on write. + didSplit := false + split := func() { + if didSplit { + return + } + didSplit = true + if p.pkgdir != testDir { + p1 := new(Package) + testCopy[p] = p1 + *p1 = *p + p1.imports = make([]*Package, len(p.imports)) + copy(p1.imports, p.imports) + p = p1 + p.pkgdir = testDir + p.target = "" + p.fake = true + p.Stale = true + } + } + + // Update p.deps and p.imports to use at test copies. + for i, dep := range p.deps { + if p1 := testCopy[dep]; p1 != nil && p1 != dep { + split() + p.deps[i] = p1 + } + } + for i, imp := range p.imports { + if p1 := testCopy[imp]; p1 != nil && p1 != imp { + split() + p.imports[i] = p1 + } + } + } +} + +var coverIndex = 0 + +// isTestFile reports whether the source file is a set of tests and should therefore +// be excluded from coverage analysis. +func isTestFile(file string) bool { + // We don't cover tests, only the code they test. + return strings.HasSuffix(file, "_test.go") +} + +// declareCoverVars attaches the required cover variables names +// to the files, to be used when annotating the files. +func declareCoverVars(importPath string, files ...string) map[string]*CoverVar { + coverVars := make(map[string]*CoverVar) + for _, file := range files { + if isTestFile(file) { + continue + } + coverVars[file] = &CoverVar{ + File: filepath.Join(importPath, file), + Var: fmt.Sprintf("GoCover_%d", coverIndex), + } + coverIndex++ + } + return coverVars +} + // runTest is the action for running a test binary. func (b *builder) runTest(a *action) error { args := stringList(a.deps[0].target, testArgs) @@ -688,10 +898,23 @@ func (b *builder) runTest(a *action) error { go func() { done <- cmd.Wait() }() + Outer: select { case err = <-done: // ok case <-tick.C: + if signalTrace != nil { + // Send a quit signal in the hope that the program will print + // a stack trace and exit. Give it five seconds before resorting + // to Kill. + cmd.Process.Signal(signalTrace) + select { + case err = <-done: + fmt.Fprintf(&buf, "*** Test killed with %v: ran too long (%v).\n", signalTrace, testKillTimeout) + break Outer + case <-time.After(5 * time.Second): + } + } cmd.Process.Kill() err = <-done fmt.Fprintf(&buf, "*** Test killed: ran too long (%v).\n", testKillTimeout) @@ -704,7 +927,7 @@ func (b *builder) runTest(a *action) error { if testShowPass { a.testOutput.Write(out) } - fmt.Fprintf(a.testOutput, "ok \t%s\t%s\n", a.p.ImportPath, t) + fmt.Fprintf(a.testOutput, "ok \t%s\t%s%s\n", a.p.ImportPath, t, coveragePercentage(out)) return nil } @@ -720,6 +943,25 @@ func (b *builder) runTest(a *action) error { return nil } +// coveragePercentage returns the coverage results (if enabled) for the +// test. It uncovers the data by scanning the output from the test run. +func coveragePercentage(out []byte) string { + if !testCover { + return "" + } + // The string looks like + // test coverage for encoding/binary: 79.9% of statements + // Extract the piece from the percentage to the end of the line. + re := regexp.MustCompile(`coverage: (.*)\n`) + matches := re.FindSubmatch(out) + if matches == nil { + // Probably running "go test -cover" not "go test -cover fmt". + // The coverage output will appear in the output directly. + return "" + } + return fmt.Sprintf("\tcoverage: %s", matches[1]) +} + // cleanTest is the action for cleaning up after a test. func (b *builder) cleanTest(a *action) error { if buildWork { @@ -760,11 +1002,24 @@ func isTest(name, prefix string) bool { return !unicode.IsLower(rune) } +type coverInfo struct { + Package *Package + Vars map[string]*CoverVar +} + // writeTestmain writes the _testmain.go file for package p to // the file named out. -func writeTestmain(out string, p *Package) error { +func writeTestmain(out string, pmain, p *Package) error { + var cover []coverInfo + for _, cp := range pmain.imports { + if len(cp.coverVars) > 0 { + cover = append(cover, coverInfo{cp, cp.coverVars}) + } + } + t := &testFuncs{ Package: p, + Cover: cover, } for _, file := range p.TestGoFiles { if err := t.load(filepath.Join(p.Dir, file), "_test", &t.NeedTest); err != nil { @@ -797,6 +1052,31 @@ type testFuncs struct { Package *Package NeedTest bool NeedXtest bool + Cover []coverInfo +} + +func (t *testFuncs) CoverMode() string { + return testCoverMode +} + +func (t *testFuncs) CoverEnabled() bool { + return testCover +} + +// Covered returns a string describing which packages are being tested for coverage. +// If the covered package is the same as the tested package, it returns the empty string. +// Otherwise it is a comma-separated human-readable list of packages beginning with +// " in", ready for use in the coverage message. +func (t *testFuncs) Covered() string { + if testCoverPaths == nil { + return "" + } + return " in " + strings.Join(testCoverPaths, ", ") +} + +// Tested returns the name of the package being tested. +func (t *testFuncs) Tested() string { + return t.Package.Name } type testFunc struct { @@ -862,6 +1142,9 @@ import ( {{if .NeedXtest}} _xtest {{.Package.ImportPath | printf "%s_test" | printf "%q"}} {{end}} +{{range $i, $p := .Cover}} + _cover{{$i}} {{$p.Package.ImportPath | printf "%q"}} +{{end}} ) var tests = []testing.InternalTest{ @@ -896,7 +1179,54 @@ func matchString(pat, str string) (result bool, err error) { return matchRe.MatchString(str), nil } +{{if .CoverEnabled}} + +// Only updated by init functions, so no need for atomicity. +var ( + coverCounters = make(map[string][]uint32) + coverBlocks = make(map[string][]testing.CoverBlock) +) + +func init() { + {{range $i, $p := .Cover}} + {{range $file, $cover := $p.Vars}} + coverRegisterFile({{printf "%q" $cover.File}}, _cover{{$i}}.{{$cover.Var}}.Count[:], _cover{{$i}}.{{$cover.Var}}.Pos[:], _cover{{$i}}.{{$cover.Var}}.NumStmt[:]) + {{end}} + {{end}} +} + +func coverRegisterFile(fileName string, counter []uint32, pos []uint32, numStmts []uint16) { + if 3*len(counter) != len(pos) || len(counter) != len(numStmts) { + panic("coverage: mismatched sizes") + } + if coverCounters[fileName] != nil { + // Already registered. + return + } + coverCounters[fileName] = counter + block := make([]testing.CoverBlock, len(counter)) + for i := range counter { + block[i] = testing.CoverBlock{ + Line0: pos[3*i+0], + Col0: uint16(pos[3*i+2]), + Line1: pos[3*i+1], + Col1: uint16(pos[3*i+2]>>16), + Stmts: numStmts[i], + } + } + coverBlocks[fileName] = block +} +{{end}} + func main() { +{{if .CoverEnabled}} + testing.RegisterCover(testing.Cover{ + Mode: {{printf "%q" .CoverMode}}, + Counters: coverCounters, + Blocks: coverBlocks, + CoveredPackages: {{printf "%q" .Covered}}, + }) +{{end}} testing.Main(matchString, tests, benchmarks, examples) } diff --git a/src/cmd/go/testdata/shadow/root1/src/foo/foo.go b/src/cmd/go/testdata/shadow/root1/src/foo/foo.go new file mode 100644 index 000000000..f52652b1b --- /dev/null +++ b/src/cmd/go/testdata/shadow/root1/src/foo/foo.go @@ -0,0 +1 @@ +package foo diff --git a/src/cmd/go/testdata/shadow/root1/src/math/math.go b/src/cmd/go/testdata/shadow/root1/src/math/math.go new file mode 100644 index 000000000..c91c24e96 --- /dev/null +++ b/src/cmd/go/testdata/shadow/root1/src/math/math.go @@ -0,0 +1 @@ +package math diff --git a/src/cmd/go/testdata/shadow/root2/src/foo/foo.go b/src/cmd/go/testdata/shadow/root2/src/foo/foo.go new file mode 100644 index 000000000..f52652b1b --- /dev/null +++ b/src/cmd/go/testdata/shadow/root2/src/foo/foo.go @@ -0,0 +1 @@ +package foo diff --git a/src/cmd/go/testdata/src/badpkg/x.go b/src/cmd/go/testdata/src/badpkg/x.go new file mode 100644 index 000000000..dda35e8ed --- /dev/null +++ b/src/cmd/go/testdata/src/badpkg/x.go @@ -0,0 +1 @@ +pkg badpkg diff --git a/src/cmd/go/testdata/src/cgotest/m.go b/src/cmd/go/testdata/src/cgotest/m.go new file mode 100644 index 000000000..4d68307cf --- /dev/null +++ b/src/cmd/go/testdata/src/cgotest/m.go @@ -0,0 +1,5 @@ +package cgotest + +import "C" + +var _ C.int diff --git a/src/cmd/go/testdata/src/main_test/m.go b/src/cmd/go/testdata/src/main_test/m.go new file mode 100644 index 000000000..c682f030b --- /dev/null +++ b/src/cmd/go/testdata/src/main_test/m.go @@ -0,0 +1,4 @@ +package main + +func F() {} +func main() {} diff --git a/src/cmd/go/testdata/src/main_test/m_test.go b/src/cmd/go/testdata/src/main_test/m_test.go new file mode 100644 index 000000000..f865b7734 --- /dev/null +++ b/src/cmd/go/testdata/src/main_test/m_test.go @@ -0,0 +1,10 @@ +package main_test + +import ( + . "main_test" + "testing" +) + +func Test1(t *testing.T) { + F() +} diff --git a/src/cmd/go/testdata/src/syntaxerror/x.go b/src/cmd/go/testdata/src/syntaxerror/x.go new file mode 100644 index 000000000..c89cd18d0 --- /dev/null +++ b/src/cmd/go/testdata/src/syntaxerror/x.go @@ -0,0 +1 @@ +package p diff --git a/src/cmd/go/testdata/src/syntaxerror/x_test.go b/src/cmd/go/testdata/src/syntaxerror/x_test.go new file mode 100644 index 000000000..2460743e5 --- /dev/null +++ b/src/cmd/go/testdata/src/syntaxerror/x_test.go @@ -0,0 +1,4 @@ +package p + +func f() (x.y, z int) { +} diff --git a/src/cmd/go/testflag.go b/src/cmd/go/testflag.go index b2ca66b09..aea81d8f8 100644 --- a/src/cmd/go/testflag.go +++ b/src/cmd/go/testflag.go @@ -27,12 +27,17 @@ var usageMessage = `Usage of go test: -bench="": passes -test.bench to test -benchmem=false: print memory allocation statistics for benchmarks -benchtime=1s: passes -test.benchtime to test + -cover=false: enable coverage analysis + -covermode="set": specifies mode for coverage analysis + -coverpkg="": comma-separated list of packages for coverage analysis + -coverprofile="": passes -test.coverprofile to test if -cover -cpu="": passes -test.cpu to test -cpuprofile="": passes -test.cpuprofile to test -memprofile="": passes -test.memprofile to test -memprofilerate=0: passes -test.memprofilerate to test -blockprofile="": pases -test.blockprofile to test -blockprofilerate=0: passes -test.blockprofilerate to test + -outputdir=$PWD: passes -test.outputdir to test -parallel=0: passes -test.parallel to test -run="": passes -test.run to test -short=false: passes -test.short to test @@ -62,6 +67,8 @@ var testFlagDefn = []*testFlagSpec{ {name: "c", boolVar: &testC}, {name: "file", multiOK: true}, {name: "i", boolVar: &testI}, + {name: "cover", boolVar: &testCover}, + {name: "coverpkg"}, // build flags. {name: "a", boolVar: &buildA}, @@ -75,17 +82,21 @@ var testFlagDefn = []*testFlagSpec{ {name: "tags"}, {name: "compiler"}, {name: "race", boolVar: &buildRace}, + {name: "installsuffix"}, // passed to 6.out, adding a "test." prefix to the name if necessary: -v becomes -test.v. {name: "bench", passToTest: true}, {name: "benchmem", boolVar: new(bool), passToTest: true}, {name: "benchtime", passToTest: true}, + {name: "covermode"}, + {name: "coverprofile", passToTest: true}, {name: "cpu", passToTest: true}, {name: "cpuprofile", passToTest: true}, {name: "memprofile", passToTest: true}, {name: "memprofilerate", passToTest: true}, {name: "blockprofile", passToTest: true}, {name: "blockprofilerate", passToTest: true}, + {name: "outputdir", passToTest: true}, {name: "parallel", passToTest: true}, {name: "run", passToTest: true}, {name: "short", boolVar: new(bool), passToTest: true}, @@ -104,6 +115,8 @@ var testFlagDefn = []*testFlagSpec{ // go test -x math func testFlags(args []string) (packageNames, passToTest []string) { inPkg := false + outputDir := "" + testCoverMode = "set" for i := 0; i < len(args); i++ { if !strings.HasPrefix(args[i], "-") { if !inPkg && packageNames == nil { @@ -137,7 +150,7 @@ func testFlags(args []string) (packageNames, passToTest []string) { var err error switch f.name { // bool flags. - case "a", "c", "i", "n", "x", "v", "work", "race": + case "a", "c", "i", "n", "x", "v", "race", "cover", "work": setBoolFlag(f.boolVar, value) case "p": setIntFlag(&buildP, value) @@ -169,6 +182,27 @@ func testFlags(args []string) (packageNames, passToTest []string) { testTimeout = value case "blockprofile", "cpuprofile", "memprofile": testProfile = true + testNeedBinary = true + case "coverpkg": + testCover = true + if value == "" { + testCoverPaths = nil + } else { + testCoverPaths = strings.Split(value, ",") + } + case "coverprofile": + testCover = true + testProfile = true + case "covermode": + switch value { + case "set", "count", "atomic": + testCoverMode = value + default: + fatalf("invalid flag argument for -cover: %q", value) + } + testCover = true + case "outputdir": + outputDir = value } if extraWord { i++ @@ -177,6 +211,15 @@ func testFlags(args []string) (packageNames, passToTest []string) { passToTest = append(passToTest, "-test."+f.name+"="+value) } } + + // Tell the test what directory we're running in, so it can write the profiles there. + if testProfile && outputDir == "" { + dir, err := os.Getwd() + if err != nil { + fatalf("error from os.Getwd: %s", err) + } + passToTest = append(passToTest, "-test.outputdir", dir) + } return } @@ -215,13 +258,13 @@ func testFlag(args []string, i int) (f *testFlagSpec, value string, extra bool) extra = equals < 0 if extra { if i+1 >= len(args) { - usage() + testSyntaxError("missing argument for flag " + f.name) } value = args[i+1] } } if f.present && !f.multiOK { - usage() + testSyntaxError(f.name + " flag may be set only once") } f.present = true return @@ -235,8 +278,7 @@ func testFlag(args []string, i int) (f *testFlagSpec, value string, extra bool) 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() + testSyntaxError("illegal bool flag value " + value) } *flag = x } @@ -245,8 +287,13 @@ func setBoolFlag(flag *bool, value string) { 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() + testSyntaxError("illegal int flag value " + value) } *flag = x } + +func testSyntaxError(msg string) { + fmt.Fprintf(os.Stderr, "go test: %s\n", msg) + fmt.Fprintf(os.Stderr, `run "go help test" or "go help testflag" for more information`+"\n") + os.Exit(2) +} diff --git a/src/cmd/go/tool.go b/src/cmd/go/tool.go index 299b94cb3..6d26f7a4b 100644 --- a/src/cmd/go/tool.go +++ b/src/cmd/go/tool.go @@ -45,12 +45,30 @@ func init() { const toolWindowsExtension = ".exe" -func tool(name string) string { - p := filepath.Join(toolDir, name) - if toolIsWindows && name != "pprof" { - p += toolWindowsExtension +func tool(toolName string) string { + toolPath := filepath.Join(toolDir, toolName) + if toolIsWindows && toolName != "pprof" { + toolPath += toolWindowsExtension } - return p + // Give a nice message if there is no tool with that name. + if _, err := os.Stat(toolPath); err != nil { + if isInGoToolsRepo(toolName) { + fmt.Fprintf(os.Stderr, "go tool: no such tool %q; to install:\n\tgo get code.google.com/p/go.tools/cmd/%s\n", toolName, toolName) + } else { + fmt.Fprintf(os.Stderr, "go tool: no such tool %q\n", toolName) + } + setExitStatus(3) + exit() + } + return toolPath +} + +func isInGoToolsRepo(toolName string) bool { + switch toolName { + case "cover", "vet": + return true + } + return false } func runTool(cmd *Command, args []string) { @@ -70,10 +88,7 @@ func runTool(cmd *Command, args []string) { } } 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) + if toolPath == "" { return } if toolIsWindows && toolName == "pprof" { @@ -86,7 +101,6 @@ func runTool(cmd *Command, args []string) { return } } - if toolN { fmt.Printf("%s %s\n", toolPath, strings.Join(args[1:], " ")) return diff --git a/src/cmd/go/vcs.go b/src/cmd/go/vcs.go index 39881a6dc..22d5ebc24 100644 --- a/src/cmd/go/vcs.go +++ b/src/cmd/go/vcs.go @@ -91,7 +91,7 @@ var vcsGit = &vcsCmd{ cmd: "git", createCmd: "clone {repo} {dir}", - downloadCmd: "fetch", + downloadCmd: "pull --ff-only", tagCmd: []tagCmd{ // tags/xxx matches a git tag named xxx @@ -102,7 +102,7 @@ var vcsGit = &vcsCmd{ {"show-ref tags/{tag} origin/{tag}", `((?:tags|origin)/\S+)$`}, }, tagSyncCmd: "checkout {tag}", - tagSyncDefault: "checkout origin/master", + tagSyncDefault: "checkout master", scheme: []string{"git", "https", "http", "git+ssh"}, pingCmd: "ls-remote {scheme}://{repo}", @@ -223,9 +223,36 @@ func (v *vcsCmd) create(dir, repo string) error { // download downloads any new changes for the repo in dir. func (v *vcsCmd) download(dir string) error { + if err := v.fixDetachedHead(dir); err != nil { + return err + } return v.run(dir, v.downloadCmd) } +// fixDetachedHead switches a Git repository in dir from a detached head to the master branch. +// Go versions before 1.2 downloaded Git repositories in an unfortunate way +// that resulted in the working tree state being on a detached head. +// That meant the repository was not usable for normal Git operations. +// Go 1.2 fixed that, but we can't pull into a detached head, so if this is +// a Git repository we check for being on a detached head and switch to the +// real branch, almost always called "master". +// TODO(dsymonds): Consider removing this for Go 1.3. +func (v *vcsCmd) fixDetachedHead(dir string) error { + if v != vcsGit { + return nil + } + + // "git symbolic-ref HEAD" succeeds iff we are not on a detached head. + if err := v.runVerboseOnly(dir, "symbolic-ref HEAD"); err == nil { + // not on a detached head + return nil + } + if buildV { + log.Printf("%s on detached head; repairing", dir) + } + return v.run(dir, "checkout master") +} + // tags returns the list of available tags for the repo in dir. func (v *vcsCmd) tags(dir string) ([]string, error) { var tags []string @@ -294,6 +321,7 @@ func vcsForDir(p *Package) (vcs *vcsCmd, root string, err error) { return nil, "", fmt.Errorf("directory %q is outside source root %q", dir, srcRoot) } + origDir := dir for len(dir) > len(srcRoot) { for _, vcs := range vcsList { if fi, err := os.Stat(filepath.Join(dir, "."+vcs.cmd)); err == nil && fi.IsDir() { @@ -310,7 +338,7 @@ func vcsForDir(p *Package) (vcs *vcsCmd, root string, err error) { dir = ndir } - return nil, "", fmt.Errorf("directory %q is not using a known version control system", dir) + return nil, "", fmt.Errorf("directory %q is not using a known version control system", origDir) } // repoRoot represents a version control system, a repo, and a root of @@ -442,7 +470,11 @@ func repoRootForImportDynamic(importPath string) (*repoRoot, error) { return nil, fmt.Errorf("http/https fetch: %v", err) } defer body.Close() - metaImport, err := matchGoImport(parseMetaGoImports(body), importPath) + imports, err := parseMetaGoImports(body) + if err != nil { + return nil, fmt.Errorf("parsing %s: %v", importPath, err) + } + metaImport, err := matchGoImport(imports, importPath) if err != nil { if err != errNoMatch { return nil, fmt.Errorf("parse %s: %v", urlStr, err) @@ -467,7 +499,10 @@ func repoRootForImportDynamic(importPath string) (*repoRoot, error) { if err != nil { return nil, fmt.Errorf("fetch %s: %v", urlStr, err) } - imports := parseMetaGoImports(body) + imports, err := parseMetaGoImports(body) + if err != nil { + return nil, fmt.Errorf("parsing %s: %v", importPath, err) + } if len(imports) == 0 { return nil, fmt.Errorf("fetch %s: no go-import meta tag", urlStr) } diff --git a/src/cmd/go/vet.go b/src/cmd/go/vet.go index 503e16362..ffb431837 100644 --- a/src/cmd/go/vet.go +++ b/src/cmd/go/vet.go @@ -15,7 +15,7 @@ var cmdVet = &Command{ 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 vet, see 'godoc code.google.com/p/go.tools/cmd/vet'. For more about specifying packages, see 'go help packages'. To run the vet tool with specific options, run 'go tool vet'. |
