summaryrefslogtreecommitdiff
path: root/src/cmd/go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/go')
-rw-r--r--src/cmd/go/build.go226
-rw-r--r--src/cmd/go/doc.go206
-rw-r--r--src/cmd/go/generate.go398
-rw-r--r--src/cmd/go/generate_test.go48
-rw-r--r--src/cmd/go/get.go35
-rw-r--r--src/cmd/go/go_windows_test.go55
-rw-r--r--src/cmd/go/help.go29
-rw-r--r--src/cmd/go/list.go39
-rw-r--r--src/cmd/go/main.go5
-rwxr-xr-xsrc/cmd/go/mkdoc.sh2
-rw-r--r--src/cmd/go/pkg.go180
-rwxr-xr-xsrc/cmd/go/test.bash405
-rw-r--r--src/cmd/go/test.go178
-rw-r--r--src/cmd/go/testdata/generate/test1.go13
-rw-r--r--src/cmd/go/testdata/generate/test2.go10
-rw-r--r--src/cmd/go/testdata/generate/test3.go9
-rw-r--r--src/cmd/go/testdata/importcom/bad.go3
-rw-r--r--src/cmd/go/testdata/importcom/conflict.go3
-rw-r--r--src/cmd/go/testdata/importcom/src/bad/bad.go1
-rw-r--r--src/cmd/go/testdata/importcom/src/conflict/a.go1
-rw-r--r--src/cmd/go/testdata/importcom/src/conflict/b.go1
-rw-r--r--src/cmd/go/testdata/importcom/src/works/x/x.go1
-rw-r--r--src/cmd/go/testdata/importcom/src/works/x/x1.go1
-rw-r--r--src/cmd/go/testdata/importcom/src/wrongplace/x.go1
-rw-r--r--src/cmd/go/testdata/importcom/works.go3
-rw-r--r--src/cmd/go/testdata/importcom/wrongplace.go3
-rw-r--r--src/cmd/go/testdata/norunexample/example_test.go11
-rw-r--r--src/cmd/go/testdata/norunexample/test_test.go10
-rw-r--r--src/cmd/go/testdata/src/badc/x.c1
-rw-r--r--src/cmd/go/testdata/src/badc/x.go1
-rw-r--r--src/cmd/go/testdata/src/badtest/badexec/x_test.go5
-rw-r--r--src/cmd/go/testdata/src/badtest/badsyntax/x.go1
-rw-r--r--src/cmd/go/testdata/src/badtest/badsyntax/x_test.go3
-rw-r--r--src/cmd/go/testdata/src/badtest/badvar/x.go1
-rw-r--r--src/cmd/go/testdata/src/badtest/badvar/x_test.go5
-rw-r--r--src/cmd/go/testdata/src/vetpkg/a_test.go1
-rw-r--r--src/cmd/go/testdata/src/vetpkg/b.go7
-rw-r--r--src/cmd/go/testdata/testinternal/p.go3
-rw-r--r--src/cmd/go/testdata/testinternal2/p.go3
-rw-r--r--src/cmd/go/testdata/testinternal2/x/y/z/internal/w/w.go1
-rw-r--r--src/cmd/go/testflag.go7
-rw-r--r--src/cmd/go/testgo.go21
-rw-r--r--src/cmd/go/tool.go14
-rw-r--r--src/cmd/go/vcs.go149
-rw-r--r--src/cmd/go/vcs_test.go124
-rw-r--r--src/cmd/go/vet.go25
46 files changed, 1964 insertions, 285 deletions
diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go
index 3645f1c2d..1dd4314da 100644
--- a/src/cmd/go/build.go
+++ b/src/cmd/go/build.go
@@ -57,6 +57,7 @@ and test commands:
-a
force rebuilding of packages that are already up-to-date.
+ In Go releases, does not apply to the standard library.
-n
print the commands but do not run them.
-p n
@@ -64,7 +65,7 @@ and test commands:
The default is the number of CPUs available.
-race
enable data race detection.
- Supported only on linux/amd64, darwin/amd64 and windows/amd64.
+ Supported only on linux/amd64, freebsd/amd64, darwin/amd64 and windows/amd64.
-v
print the names of packages as they are compiled.
-work
@@ -284,23 +285,26 @@ func runBuild(cmd *Command, args []string) {
}
}
+ depMode := modeBuild
+ if buildI {
+ depMode = modeInstall
+ }
+
if *buildO != "" {
if len(pkgs) > 1 {
fatalf("go build: cannot use -o with multiple packages")
+ } else if len(pkgs) == 0 {
+ fatalf("no packages to build")
}
p := pkgs[0]
p.target = "" // must build - not up to date
- a := b.action(modeInstall, modeBuild, p)
+ a := b.action(modeInstall, depMode, p)
a.target = *buildO
b.do(a)
return
}
a := &action{}
- depMode := modeBuild
- if buildI {
- depMode = modeInstall
- }
for _, p := range packages(args) {
a.deps = append(a.deps, b.action(modeBuild, depMode, p))
}
@@ -431,12 +435,11 @@ const (
)
var (
- goroot = filepath.Clean(runtime.GOROOT())
- gobin = os.Getenv("GOBIN")
- gorootBin = filepath.Join(goroot, "bin")
- gorootSrcPkg = filepath.Join(goroot, "src/pkg")
- gorootPkg = filepath.Join(goroot, "pkg")
- gorootSrc = filepath.Join(goroot, "src")
+ goroot = filepath.Clean(runtime.GOROOT())
+ gobin = os.Getenv("GOBIN")
+ gorootBin = filepath.Join(goroot, "bin")
+ gorootPkg = filepath.Join(goroot, "pkg")
+ gorootSrc = filepath.Join(goroot, "src")
)
func (b *builder) init() {
@@ -503,8 +506,13 @@ func goFilesPackage(gofiles []string) *Package {
}
ctxt.ReadDir = func(string) ([]os.FileInfo, error) { return dirent, nil }
- if !filepath.IsAbs(dir) {
- dir = filepath.Join(cwd, dir)
+ var err error
+ if dir == "" {
+ dir = cwd
+ }
+ dir, err = filepath.Abs(dir)
+ if err != nil {
+ fatalf("%s", err)
}
bp, err := ctxt.ImportDir(dir, 0)
@@ -823,12 +831,17 @@ func (b *builder) build(a *action) (err error) {
}
}
- var gofiles, cfiles, sfiles, objects, cgoObjects []string
+ var gofiles, cfiles, sfiles, objects, cgoObjects, pcCFLAGS, pcLDFLAGS []string
gofiles = append(gofiles, a.p.GoFiles...)
cfiles = append(cfiles, a.p.CFiles...)
sfiles = append(sfiles, a.p.SFiles...)
+ if a.p.usesCgo() || a.p.usesSwig() {
+ if pcCFLAGS, pcLDFLAGS, err = b.getPkgConfigFlags(a.p); err != nil {
+ return
+ }
+ }
// Run cgo.
if a.p.usesCgo() {
// In a package using cgo, cgo compiles the C, C++ and assembly files with gcc.
@@ -859,7 +872,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, a.p.CXXFiles, a.p.MFiles)
+ outGo, outObj, err := b.cgo(a.p, cgoExe, obj, pcCFLAGS, pcLDFLAGS, gccfiles, a.p.CXXFiles, a.p.MFiles)
if err != nil {
return err
}
@@ -872,9 +885,18 @@ func (b *builder) build(a *action) (err error) {
// In a package using SWIG, any .c or .s files are
// compiled with gcc.
gccfiles := append(cfiles, sfiles...)
+ cxxfiles, mfiles := a.p.CXXFiles, a.p.MFiles
cfiles = nil
sfiles = nil
- outGo, outObj, err := b.swig(a.p, obj, gccfiles, a.p.CXXFiles, a.p.MFiles)
+
+ // Don't build c/c++ files twice if cgo is enabled (mainly for pkg-config).
+ if a.p.usesCgo() {
+ cxxfiles = nil
+ gccfiles = nil
+ mfiles = nil
+ }
+
+ outGo, outObj, err := b.swig(a.p, obj, pcCFLAGS, gccfiles, cxxfiles, mfiles)
if err != nil {
return err
}
@@ -883,7 +905,7 @@ func (b *builder) build(a *action) (err error) {
}
if len(gofiles) == 0 {
- return &build.NoGoError{a.p.Dir}
+ return &build.NoGoError{Dir: a.p.Dir}
}
// If we're doing coverage, preprocess the .go files and put them in the work directory
@@ -1018,6 +1040,34 @@ func (b *builder) build(a *action) (err error) {
return nil
}
+// Calls pkg-config if needed and returns the cflags/ldflags needed to build the package.
+func (b *builder) getPkgConfigFlags(p *Package) (cflags, ldflags []string, err error) {
+ if pkgs := p.CgoPkgConfig; len(pkgs) > 0 {
+ var out []byte
+ out, err = b.runOut(p.Dir, p.ImportPath, nil, "pkg-config", "--cflags", pkgs)
+ if err != nil {
+ b.showOutput(p.Dir, "pkg-config --cflags "+strings.Join(pkgs, " "), string(out))
+ b.print(err.Error() + "\n")
+ err = errPrintedOutput
+ return
+ }
+ if len(out) > 0 {
+ cflags = strings.Fields(string(out))
+ }
+ out, err = b.runOut(p.Dir, p.ImportPath, nil, "pkg-config", "--libs", pkgs)
+ if err != nil {
+ b.showOutput(p.Dir, "pkg-config --libs "+strings.Join(pkgs, " "), string(out))
+ b.print(err.Error() + "\n")
+ err = errPrintedOutput
+ return
+ }
+ if len(out) > 0 {
+ ldflags = strings.Fields(string(out))
+ }
+ }
+ return
+}
+
// install is the action for installing a single package or executable.
func (b *builder) install(a *action) (err error) {
defer func() {
@@ -1253,7 +1303,7 @@ func (b *builder) showcmd(dir string, format string, args ...interface{}) {
// the source directory for the package that has failed to build.
// showOutput rewrites mentions of dir with a relative path to dir
// when the relative path is shorter. This is usually more pleasant.
-// For example, if fmt doesn't compile and we are in src/pkg/html,
+// For example, if fmt doesn't compile and we are in src/html,
// the output is
//
// $ go build
@@ -1265,7 +1315,7 @@ func (b *builder) showcmd(dir string, format string, args ...interface{}) {
//
// $ go build
// # fmt
-// /usr/gopher/go/src/pkg/fmt/print.go:1090: undefined: asdf
+// /usr/gopher/go/src/fmt/print.go:1090: undefined: asdf
// $
//
// showOutput also replaces references to the work directory with $WORK.
@@ -1425,6 +1475,14 @@ func (b *builder) runOut(dir string, desc string, env []string, cmdargs ...inter
continue
}
+ // err can be something like 'exit status 1'.
+ // Add information about what program was running.
+ // Note that if buf.Bytes() is non-empty, the caller usually
+ // shows buf.Bytes() and does not print err at all, so the
+ // prefix here does not make most output any more verbose.
+ if err != nil {
+ err = errors.New(cmdline[0] + ": " + err.Error())
+ }
return buf.Bytes(), err
}
}
@@ -1587,7 +1645,7 @@ func (gcToolchain) gc(b *builder, p *Package, archive, obj string, importArgs []
extFiles := len(p.CgoFiles) + len(p.CFiles) + len(p.CXXFiles) + len(p.MFiles) + len(p.SFiles) + len(p.SysoFiles) + len(p.SwigFiles) + len(p.SwigCXXFiles)
if p.Standard {
switch p.ImportPath {
- case "os", "runtime/pprof", "sync", "time":
+ case "bytes", "net", "os", "runtime/pprof", "sync", "time":
extFiles++
}
}
@@ -1611,8 +1669,10 @@ func (gcToolchain) gc(b *builder, p *Package, archive, obj string, importArgs []
}
func (gcToolchain) asm(b *builder, p *Package, obj, ofile, sfile string) error {
+ // Add -I pkg/GOOS_GOARCH so #include "textflag.h" works in .s files.
+ inc := filepath.Join(goroot, "pkg", fmt.Sprintf("%s_%s", goos, goarch))
sfile = mkAbs(p.Dir, sfile)
- return b.run(p.Dir, p.ImportPath, nil, tool(archChar+"a"), "-trimpath", b.work, "-I", obj, "-o", ofile, "-D", "GOOS_"+goos, "-D", "GOARCH_"+goarch, sfile)
+ return b.run(p.Dir, p.ImportPath, nil, tool(archChar+"a"), "-trimpath", b.work, "-I", obj, "-I", inc, "-o", ofile, "-D", "GOOS_"+goos, "-D", "GOARCH_"+goarch, sfile)
}
func (gcToolchain) pkgpath(basedir string, p *Package) string {
@@ -1706,7 +1766,7 @@ func packInternal(b *builder, afile string, ofiles []string) error {
func (gcToolchain) ld(b *builder, p *Package, out string, allactions []*action, mainpkg string, ofiles []string) error {
importArgs := b.includeArgs("-L", allactions)
- cxx := false
+ cxx := len(p.CXXFiles) > 0
for _, a := range allactions {
if a.p != nil && len(a.p.CXXFiles) > 0 {
cxx = true
@@ -1766,14 +1826,30 @@ func (gcToolchain) ld(b *builder, p *Package, out string, allactions []*action,
func (gcToolchain) cc(b *builder, p *Package, objdir, ofile, cfile string) error {
inc := filepath.Join(goroot, "pkg", fmt.Sprintf("%s_%s", goos, goarch))
cfile = mkAbs(p.Dir, cfile)
- args := stringList(tool(archChar+"c"), "-F", "-V", "-w", "-trimpath", b.work, "-I", objdir, "-I", inc, "-o", ofile, buildCcflags, "-D", "GOOS_"+goos, "-D", "GOARCH_"+goarch, cfile)
+ warn := []string{"-w"}
+ if p.usesSwig() {
+ // When using SWIG, this compiler is only used to
+ // compile the C files generated by SWIG.
+ // We don't want warnings.
+ // See issue 9065 for details.
+ warn = nil
+ }
+ args := stringList(tool(archChar+"c"), "-F", "-V", warn, "-trimpath", b.work, "-I", objdir, "-I", inc, "-o", ofile, buildCcflags, "-D", "GOOS_"+goos, "-D", "GOARCH_"+goarch, cfile)
return b.run(p.Dir, p.ImportPath, nil, args)
}
// The Gccgo toolchain.
type gccgoToolchain struct{}
-var gccgoBin, _ = exec.LookPath("gccgo")
+var gccgoName, gccgoBin string
+
+func init() {
+ gccgoName = os.Getenv("GCCGO")
+ if gccgoName == "" {
+ gccgoName = "gccgo"
+ }
+ gccgoBin, _ = exec.LookPath(gccgoName)
+}
func (gccgoToolchain) compiler() string {
return gccgoBin
@@ -1784,7 +1860,7 @@ func (gccgoToolchain) linker() string {
}
func (gccgoToolchain) gc(b *builder, p *Package, archive, obj string, importArgs []string, gofiles []string) (ofile string, output []byte, err error) {
- out := p.Name + ".o"
+ out := "_go_.o"
ofile = obj + out
gcargs := []string{"-g"}
gcargs = append(gcargs, b.gccArchArgs()...)
@@ -1794,7 +1870,7 @@ func (gccgoToolchain) gc(b *builder, p *Package, archive, obj string, importArgs
if p.localPrefix != "" {
gcargs = append(gcargs, "-fgo-relative-import-path="+p.localPrefix)
}
- args := stringList("gccgo", importArgs, "-c", gcargs, "-o", ofile, buildGccgoflags)
+ args := stringList(gccgoName, importArgs, "-c", gcargs, "-o", ofile, buildGccgoflags)
for _, f := range gofiles {
args = append(args, mkAbs(p.Dir, f))
}
@@ -1810,7 +1886,7 @@ func (gccgoToolchain) asm(b *builder, p *Package, obj, ofile, sfile string) erro
defs = append(defs, `-D`, `GOPKGPATH="`+pkgpath+`"`)
}
defs = append(defs, b.gccArchArgs()...)
- return b.run(p.Dir, p.ImportPath, nil, "gccgo", "-I", obj, "-o", ofile, defs, sfile)
+ return b.run(p.Dir, p.ImportPath, nil, gccgoName, "-I", obj, "-o", ofile, defs, sfile)
}
func (gccgoToolchain) pkgpath(basedir string, p *Package) string {
@@ -1836,8 +1912,8 @@ func (tools gccgoToolchain) ld(b *builder, p *Package, out string, allactions []
ldflags := b.gccArchArgs()
cgoldflags := []string{}
usesCgo := false
- cxx := false
- objc := false
+ cxx := len(p.CXXFiles) > 0
+ objc := len(p.MFiles) > 0
// Prefer the output of an install action to the output of a build action,
// because the install action will delete the output of the build action.
@@ -1877,6 +1953,7 @@ func (tools gccgoToolchain) ld(b *builder, p *Package, out string, allactions []
}
ldflags = append(ldflags, afiles...)
ldflags = append(ldflags, cgoldflags...)
+ ldflags = append(ldflags, envList("CGO_LDFLAGS", "")...)
ldflags = append(ldflags, p.CgoLDFLAGS...)
if usesCgo && goos == "linux" {
ldflags = append(ldflags, "-Wl,-E")
@@ -1887,7 +1964,7 @@ func (tools gccgoToolchain) ld(b *builder, p *Package, out string, allactions []
if objc {
ldflags = append(ldflags, "-lobjc")
}
- return b.run(".", p.ImportPath, nil, "gccgo", "-o", out, ofiles, "-Wl,-(", ldflags, "-Wl,-)", buildGccgoflags)
+ return b.run(".", p.ImportPath, nil, gccgoName, "-o", out, ofiles, "-Wl,-(", ldflags, "-Wl,-)", buildGccgoflags)
}
func (gccgoToolchain) cc(b *builder, p *Package, objdir, ofile, cfile string) error {
@@ -1898,8 +1975,7 @@ func (gccgoToolchain) cc(b *builder, p *Package, objdir, ofile, cfile string) er
if pkgpath := gccgoCleanPkgpath(p); pkgpath != "" {
defs = append(defs, `-D`, `GOPKGPATH="`+pkgpath+`"`)
}
- // TODO: Support using clang here (during gccgo build)?
- return b.run(p.Dir, p.ImportPath, nil, "gcc", "-Wall", "-g",
+ return b.run(p.Dir, p.ImportPath, nil, envList("CC", defaultCC), "-Wall", "-g",
"-I", objdir, "-I", inc, "-o", ofile, defs, "-c", cfile)
}
@@ -1950,9 +2026,9 @@ func (b *builder) libgcc(p *Package) (string, error) {
return "$LIBGCC", nil
}
- // clang might not be able to find libgcc, and in that case,
+ // The compiler might not be able to find libgcc, and in that case,
// it will simply return "libgcc.a", which is of no use to us.
- if strings.Contains(gccCmd[0], "clang") && !filepath.IsAbs(string(f)) {
+ if !filepath.IsAbs(string(f)) {
return "", nil
}
@@ -2090,36 +2166,16 @@ var (
cgoLibGccFileOnce sync.Once
)
-func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles, gxxfiles, mfiles []string) (outGo, outObj []string, err error) {
+func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, gccfiles, gxxfiles, mfiles []string) (outGo, outObj []string, err error) {
cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, cgoLDFLAGS := b.cflags(p, true)
_, cgoexeCFLAGS, _, _ := b.cflags(p, false)
-
+ cgoCPPFLAGS = append(cgoCPPFLAGS, pcCFLAGS...)
+ cgoLDFLAGS = append(cgoLDFLAGS, pcLDFLAGS...)
// If we are compiling Objective-C code, then we need to link against libobjc
if len(mfiles) > 0 {
cgoLDFLAGS = append(cgoLDFLAGS, "-lobjc")
}
- if pkgs := p.CgoPkgConfig; len(pkgs) > 0 {
- out, err := b.runOut(p.Dir, p.ImportPath, nil, "pkg-config", "--cflags", pkgs)
- if err != nil {
- b.showOutput(p.Dir, "pkg-config --cflags "+strings.Join(pkgs, " "), string(out))
- b.print(err.Error() + "\n")
- return nil, nil, errPrintedOutput
- }
- if len(out) > 0 {
- cgoCPPFLAGS = append(cgoCPPFLAGS, strings.Fields(string(out))...)
- }
- out, err = b.runOut(p.Dir, p.ImportPath, nil, "pkg-config", "--libs", pkgs)
- if err != nil {
- b.showOutput(p.Dir, "pkg-config --libs "+strings.Join(pkgs, " "), string(out))
- b.print(err.Error() + "\n")
- return nil, nil, errPrintedOutput
- }
- if len(out) > 0 {
- cgoLDFLAGS = append(cgoLDFLAGS, strings.Fields(string(out))...)
- }
- }
-
// Allows including _cgo_export.h from .[ch] files in the package.
cgoCPPFLAGS = append(cgoCPPFLAGS, "-I", obj)
@@ -2196,6 +2252,14 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles, gxxfiles, mfiles
strings.HasSuffix(f, ".so"),
strings.HasSuffix(f, ".dll"):
continue
+ // Remove any -fsanitize=foo flags.
+ // Otherwise the compiler driver thinks that we are doing final link
+ // and links sanitizer runtime into the object file. But we are not doing
+ // the final link, we will link the resulting object file again. And
+ // so the program ends up with two copies of sanitizer runtime.
+ // See issue 8788 for details.
+ case strings.HasPrefix(f, "-fsanitize="):
+ continue
default:
bareLDFLAGS = append(bareLDFLAGS, f)
}
@@ -2262,13 +2326,14 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles, gxxfiles, mfiles
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
+ pie := goarch == "arm" && (goos == "linux" || goos == "android")
+ if pie { // we need to use -pie for Linux/ARM to get accurate imported sym
cgoLDFLAGS = append(cgoLDFLAGS, "-pie")
}
if err := b.gccld(p, dynobj, cgoLDFLAGS, linkobj); err != nil {
return nil, nil, err
}
- if goarch == "arm" && goos == "linux" { // but we don't need -pie for normal cgo programs
+ if pie { // but we don't need -pie for normal cgo programs
cgoLDFLAGS = cgoLDFLAGS[0 : len(cgoLDFLAGS)-1]
}
@@ -2302,7 +2367,23 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles, gxxfiles, mfiles
nonGccObjs = append(nonGccObjs, f)
}
}
- if err := b.gccld(p, ofile, stringList(bareLDFLAGS, "-Wl,-r", "-nostdlib", staticLibs), gccObjs); err != nil {
+ ldflags := stringList(bareLDFLAGS, "-Wl,-r", "-nostdlib", staticLibs)
+
+ // Some systems, such as Ubuntu, always add --build-id to
+ // every link, but we don't want a build ID since we are
+ // producing an object file. On some of those system a plain
+ // -r (not -Wl,-r) will turn off --build-id, but clang 3.0
+ // doesn't support a plain -r. I don't know how to turn off
+ // --build-id when using clang other than passing a trailing
+ // --build-id=none. So that is what we do, but only on
+ // systems likely to support it, which is to say, systems that
+ // normally use gold or the GNU linker.
+ switch goos {
+ case "android", "dragonfly", "linux", "netbsd":
+ ldflags = append(ldflags, "-Wl,--build-id=none")
+ }
+
+ if err := b.gccld(p, ofile, ldflags, gccObjs); err != nil {
return nil, nil, err
}
@@ -2317,7 +2398,7 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles, gxxfiles, mfiles
// Run SWIG on all SWIG input files.
// 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, mfiles []string) (outGo, outObj []string, err error) {
+func (b *builder) swig(p *Package, obj string, pcCFLAGS, gccfiles, gxxfiles, mfiles []string) (outGo, outObj []string, err error) {
cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, _ := b.cflags(p, true)
cflags := stringList(cgoCPPFLAGS, cgoCFLAGS)
cxxflags := stringList(cgoCPPFLAGS, cgoCXXFLAGS)
@@ -2358,7 +2439,7 @@ func (b *builder) swig(p *Package, obj string, gccfiles, gxxfiles, mfiles []stri
}
for _, f := range p.SwigFiles {
- goFile, objFile, gccObjFile, err := b.swigOne(p, f, obj, false, intgosize)
+ goFile, objFile, gccObjFile, err := b.swigOne(p, f, obj, pcCFLAGS, false, intgosize)
if err != nil {
return nil, nil, err
}
@@ -2373,7 +2454,7 @@ func (b *builder) swig(p *Package, obj string, gccfiles, gxxfiles, mfiles []stri
}
}
for _, f := range p.SwigCXXFiles {
- goFile, objFile, gccObjFile, err := b.swigOne(p, f, obj, true, intgosize)
+ goFile, objFile, gccObjFile, err := b.swigOne(p, f, obj, pcCFLAGS, true, intgosize)
if err != nil {
return nil, nil, err
}
@@ -2452,13 +2533,13 @@ 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, objGccObj string, err error) {
+func (b *builder) swigOne(p *Package, file, obj string, pcCFLAGS []string, cxx bool, intgosize string) (outGo, outObj, objGccObj string, err error) {
cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, _ := b.cflags(p, true)
var cflags []string
if cxx {
- cflags = stringList(cgoCPPFLAGS, cgoCXXFLAGS)
+ cflags = stringList(cgoCPPFLAGS, pcCFLAGS, cgoCXXFLAGS)
} else {
- cflags = stringList(cgoCPPFLAGS, cgoCFLAGS)
+ cflags = stringList(cgoCPPFLAGS, pcCFLAGS, cgoCFLAGS)
}
n := 5 // length of ".swig"
@@ -2484,6 +2565,13 @@ func (b *builder) swigOne(p *Package, file, obj string, cxx bool, intgosize stri
"-o", obj + gccBase + gccExt,
"-outdir", obj,
}
+
+ for _, f := range cflags {
+ if len(f) > 3 && f[:2] == "-I" {
+ args = append(args, f)
+ }
+ }
+
if gccgo {
args = append(args, "-gccgo")
if pkgpath := gccgoPkgpath(p); pkgpath != "" {
@@ -2556,8 +2644,8 @@ func raceInit() {
if !buildRace {
return
}
- if goarch != "amd64" || goos != "linux" && goos != "darwin" && goos != "windows" {
- fmt.Fprintf(os.Stderr, "go %s: -race is only supported on linux/amd64, darwin/amd64 and windows/amd64\n", flag.Args()[0])
+ if goarch != "amd64" || goos != "linux" && goos != "freebsd" && goos != "darwin" && goos != "windows" {
+ fmt.Fprintf(os.Stderr, "go %s: -race is only supported on linux/amd64, freebsd/amd64, darwin/amd64 and windows/amd64\n", flag.Args()[0])
os.Exit(2)
}
buildGcflags = append(buildGcflags, "-race")
diff --git a/src/cmd/go/doc.go b/src/cmd/go/doc.go
index 9840804ce..d0d8a8a5b 100644
--- a/src/cmd/go/doc.go
+++ b/src/cmd/go/doc.go
@@ -19,6 +19,7 @@ The commands are:
env print Go environment information
fix run go tool fix on packages
fmt run gofmt on package sources
+ generate generate Go files by processing source
get download and install packages and dependencies
install compile and install packages and dependencies
list list packages
@@ -75,6 +76,7 @@ and test commands:
-a
force rebuilding of packages that are already up-to-date.
+ In Go releases, does not apply to the standard library.
-n
print the commands but do not run them.
-p n
@@ -82,7 +84,7 @@ and test commands:
The default is the number of CPUs available.
-race
enable data race detection.
- Supported only on linux/amd64, darwin/amd64 and windows/amd64.
+ Supported only on linux/amd64, freebsd/amd64, darwin/amd64 and windows/amd64.
-v
print the names of packages as they are compiled.
-work
@@ -219,11 +221,110 @@ To run gofmt with specific options, run gofmt itself.
See also: go fix, go vet.
+Generate Go files by processing source
+
+Usage:
+
+ go generate [-run regexp] [file.go... | packages]
+
+Generate runs commands described by directives within existing
+files. Those commands can run any process but the intent is to
+create or update Go source files, for instance by running yacc.
+
+Go generate is never run automatically by go build, go get, go test,
+and so on. It must be run explicitly.
+
+Go generate scans the file for directives, which are lines of
+the form,
+
+ //go:generate command argument...
+
+(note: no leading spaces and no space in "//go") where command
+is the generator to be run, corresponding to an executable file
+that can be run locally. It must either be in the shell path
+(gofmt), a fully qualified path (/usr/you/bin/mytool), or a
+command alias, described below.
+
+Note that go generate does not parse the file, so lines that look
+like directives in comments or multiline strings will be treated
+as directives.
+
+The arguments to the directive are space-separated tokens or
+double-quoted strings passed to the generator as individual
+arguments when it is run.
+
+Quoted strings use Go syntax and are evaluated before execution; a
+quoted string appears as a single argument to the generator.
+
+Go generate sets several variables when it runs the generator:
+
+ $GOARCH
+ The execution architecture (arm, amd64, etc.)
+ $GOOS
+ The execution operating system (linux, windows, etc.)
+ $GOFILE
+ The base name of the file.
+ $GOPACKAGE
+ The name of the package of the file containing the directive.
+
+Other than variable substitution and quoted-string evaluation, no
+special processing such as "globbing" is performed on the command
+line.
+
+As a last step before running the command, any invocations of any
+environment variables with alphanumeric names, such as $GOFILE or
+$HOME, are expanded throughout the command line. The syntax for
+variable expansion is $NAME on all operating systems. Due to the
+order of evaluation, variables are expanded even inside quoted
+strings. If the variable NAME is not set, $NAME expands to the
+empty string.
+
+A directive of the form,
+
+ //go:generate -command xxx args...
+
+specifies, for the remainder of this source file only, that the
+string xxx represents the command identified by the arguments. This
+can be used to create aliases or to handle multiword generators.
+For example,
+
+ //go:generate -command yacc go tool yacc
+
+specifies that the command "yacc" represents the generator
+"go tool yacc".
+
+Generate processes packages in the order given on the command line,
+one at a time. If the command line lists .go files, they are treated
+as a single package. Within a package, generate processes the
+source files in a package in file name order, one at a time. Within
+a source file, generate runs generators in the order they appear
+in the file, one at a time.
+
+If any generator returns an error exit status, "go generate" skips
+all further processing for that package.
+
+The generator is run in the package's source directory.
+
+Go generate accepts one specific flag:
+
+ -run=""
+ if non-empty, specifies a regular expression to
+ select directives whose command matches the expression.
+
+It also accepts the standard build flags -v, -n, and -x.
+The -v flag prints the names of packages and files as they are
+processed.
+The -n flag prints commands that would be executed.
+The -x flag prints commands as they are executed.
+
+For more about specifying packages, see 'go help packages'.
+
+
Download and install packages and dependencies
Usage:
- go get [-d] [-fix] [-t] [-u] [build flags] [packages]
+ go get [-d] [-f] [-fix] [-t] [-u] [build flags] [packages]
Get downloads and installs the packages named by the import paths,
along with their dependencies.
@@ -231,6 +332,11 @@ along with their dependencies.
The -d flag instructs get to stop after downloading the packages; that is,
it instructs get not to install the packages.
+The -f flag, valid only when -u is set, forces get -u not to verify that
+each package has been checked out from the source control repository
+implied by its import path. This can be useful if the source is a local fork
+of the original.
+
The -fix flag instructs get to run the fix tool on the downloaded packages
before resolving dependencies or building the code.
@@ -291,28 +397,29 @@ syntax of package template. The default output is equivalent to -f
'{{.ImportPath}}'. The struct being passed to the template is:
type Package struct {
- Dir string // directory containing package sources
- ImportPath string // import path of package in dir
- Name string // package name
- Doc string // package documentation string
- Target string // install path
- Goroot bool // is this package in the Go root?
- Standard bool // is this package part of the standard Go library?
- Stale bool // would 'go install' do anything for this package?
- Root string // Go root or Go path dir containing this package
+ Dir string // directory containing package sources
+ ImportPath string // import path of package in dir
+ ImportComment string // path in import comment on package statement
+ Name string // package name
+ Doc string // package documentation string
+ Target string // install path
+ Goroot bool // is this package in the Go root?
+ Standard bool // is this package part of the standard Go library?
+ Stale bool // would 'go install' do anything for this package?
+ Root string // Go root or Go path dir containing this package
// Source files
- GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
- CgoFiles []string // .go sources files that import "C"
+ GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
+ CgoFiles []string // .go sources files that import "C"
IgnoredGoFiles []string // .go sources ignored due to build constraints
- CFiles []string // .c source files
- CXXFiles []string // .cc, .cxx and .cpp source files
- MFiles []string // .m source files
- HFiles []string // .h, .hh, .hpp and .hxx source files
- SFiles []string // .s source files
- SwigFiles []string // .swig files
- SwigCXXFiles []string // .swigcxx files
- SysoFiles []string // .syso object files to add to archive
+ CFiles []string // .c source files
+ CXXFiles []string // .cc, .cxx and .cpp source files
+ MFiles []string // .m source files
+ HFiles []string // .h, .hh, .hpp and .hxx source files
+ SFiles []string // .s source files
+ 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
@@ -431,16 +538,23 @@ non-test installation.
In addition to the build flags, the flags handled by 'go test' itself are:
- -c Compile the test binary to pkg.test but do not run it.
- (Where pkg is the last element of the package's import path.)
+ -c
+ Compile the test binary to pkg.test but do not run it
+ (where pkg is the last element of the package's import path).
+ The file name can be changed with the -o flag.
+
+ -exec xprog
+ Run the test binary using xprog. The behavior is the same as
+ in 'go run'. See 'go help run' for details.
-i
Install packages that are dependencies of the test.
Do not run the test.
- -exec xprog
- Run the test binary using xprog. The behavior is the same as
- in 'go run'. See 'go help run' for details.
+ -o file
+ Compile the test binary to the named file.
+ The test still runs (unless -c or -i is specified).
+
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.
@@ -488,7 +602,7 @@ Usage:
Vet runs the Go vet command on the packages named by the import paths.
-For more about vet, see 'godoc code.google.com/p/go.tools/cmd/vet'.
+For more about vet, see 'godoc golang.org/x/tools/cmd/vet'.
For more about specifying packages, see 'go help packages'.
To run the vet tool with specific options, run 'go tool vet'.
@@ -681,6 +795,11 @@ A few common code hosting sites have special syntax:
import "launchpad.net/~user/project/branch"
import "launchpad.net/~user/project/branch/sub/directory"
+ IBM DevOps Services (Git)
+
+ import "hub.jazz.net/git/user/project"
+ import "hub.jazz.net/git/user/project/sub/directory"
+
For code hosted on other servers, import paths may either be qualified
with the version control type, or the go tool can dynamically fetch
the import path over https/http and discover where the code resides
@@ -756,7 +875,26 @@ listed in the GOPATH environment variable (see 'go help gopath').
The go command attempts to download the version of the
package appropriate for the Go release being used.
-Run 'go help install' for more.
+Run 'go help get' for more.
+
+Import path checking
+
+When the custom import path feature described above redirects to a
+known code hosting site, each of the resulting packages has two possible
+import paths, using the custom domain or the known hosting site.
+
+A package statement is said to have an "import comment" if it is immediately
+followed (before the next newline) by a comment of one of these two forms:
+
+ package math // import "path"
+ package math /* import "path" * /
+
+The go command will refuse to install a package with an import comment
+unless it is being referred to by that import path. In this way, import comments
+let package authors make sure the custom import path is used and not a
+direct path to the underlying code hosting site.
+
+See https://golang.org/s/go14customimport for details.
Description of package lists
@@ -812,7 +950,8 @@ 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.
+Directory and file names that begin with "." or "_" are ignored
+by the go tool, as are directories named "testdata".
Description of testing flags
@@ -844,6 +983,7 @@ control the execution of any test:
-blockprofile block.out
Write a goroutine blocking profile to the specified file
when all tests are complete.
+ Writes test binary as -c would.
-blockprofilerate n
Control the detail provided in goroutine blocking profiles by
@@ -875,8 +1015,7 @@ control the execution of any test:
Sets -cover.
-coverprofile cover.out
- Write a coverage profile to the specified file after all tests
- have passed.
+ Write a coverage profile to the file after all tests have passed.
Sets -cover.
-cpu 1,2,4
@@ -886,10 +1025,11 @@ control the execution of any test:
-cpuprofile cpu.out
Write a CPU profile to the specified file before exiting.
+ Writes test binary as -c would.
-memprofile mem.out
- Write a memory profile to the specified file after all tests
- have passed.
+ Write a memory profile to the file after all tests have passed.
+ Writes test binary as -c would.
-memprofilerate n
Enable more precise (and expensive) memory profiles by setting
diff --git a/src/cmd/go/generate.go b/src/cmd/go/generate.go
new file mode 100644
index 000000000..baf4d2b55
--- /dev/null
+++ b/src/cmd/go/generate.go
@@ -0,0 +1,398 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "bufio"
+ "bytes"
+ "fmt"
+ "io"
+ "log"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "runtime"
+ "strconv"
+ "strings"
+ "unicode"
+ "unicode/utf8"
+)
+
+var cmdGenerate = &Command{
+ Run: runGenerate,
+ UsageLine: "generate [-run regexp] [file.go... | packages]",
+ Short: "generate Go files by processing source",
+ Long: `
+Generate runs commands described by directives within existing
+files. Those commands can run any process but the intent is to
+create or update Go source files, for instance by running yacc.
+
+Go generate is never run automatically by go build, go get, go test,
+and so on. It must be run explicitly.
+
+Go generate scans the file for directives, which are lines of
+the form,
+
+ //go:generate command argument...
+
+(note: no leading spaces and no space in "//go") where command
+is the generator to be run, corresponding to an executable file
+that can be run locally. It must either be in the shell path
+(gofmt), a fully qualified path (/usr/you/bin/mytool), or a
+command alias, described below.
+
+Note that go generate does not parse the file, so lines that look
+like directives in comments or multiline strings will be treated
+as directives.
+
+The arguments to the directive are space-separated tokens or
+double-quoted strings passed to the generator as individual
+arguments when it is run.
+
+Quoted strings use Go syntax and are evaluated before execution; a
+quoted string appears as a single argument to the generator.
+
+Go generate sets several variables when it runs the generator:
+
+ $GOARCH
+ The execution architecture (arm, amd64, etc.)
+ $GOOS
+ The execution operating system (linux, windows, etc.)
+ $GOFILE
+ The base name of the file.
+ $GOPACKAGE
+ The name of the package of the file containing the directive.
+
+Other than variable substitution and quoted-string evaluation, no
+special processing such as "globbing" is performed on the command
+line.
+
+As a last step before running the command, any invocations of any
+environment variables with alphanumeric names, such as $GOFILE or
+$HOME, are expanded throughout the command line. The syntax for
+variable expansion is $NAME on all operating systems. Due to the
+order of evaluation, variables are expanded even inside quoted
+strings. If the variable NAME is not set, $NAME expands to the
+empty string.
+
+A directive of the form,
+
+ //go:generate -command xxx args...
+
+specifies, for the remainder of this source file only, that the
+string xxx represents the command identified by the arguments. This
+can be used to create aliases or to handle multiword generators.
+For example,
+
+ //go:generate -command yacc go tool yacc
+
+specifies that the command "yacc" represents the generator
+"go tool yacc".
+
+Generate processes packages in the order given on the command line,
+one at a time. If the command line lists .go files, they are treated
+as a single package. Within a package, generate processes the
+source files in a package in file name order, one at a time. Within
+a source file, generate runs generators in the order they appear
+in the file, one at a time.
+
+If any generator returns an error exit status, "go generate" skips
+all further processing for that package.
+
+The generator is run in the package's source directory.
+
+Go generate accepts one specific flag:
+
+ -run=""
+ if non-empty, specifies a regular expression to
+ select directives whose command matches the expression.
+
+It also accepts the standard build flags -v, -n, and -x.
+The -v flag prints the names of packages and files as they are
+processed.
+The -n flag prints commands that would be executed.
+The -x flag prints commands as they are executed.
+
+For more about specifying packages, see 'go help packages'.
+ `,
+}
+
+var generateRunFlag string // generate -run flag
+
+func init() {
+ addBuildFlags(cmdGenerate)
+ cmdGenerate.Flag.StringVar(&generateRunFlag, "run", "", "")
+}
+
+func runGenerate(cmd *Command, args []string) {
+ // Even if the arguments are .go files, this loop suffices.
+ for _, pkg := range packages(args) {
+ for _, file := range pkg.gofiles {
+ if !generate(pkg.Name, file) {
+ break
+ }
+ }
+ }
+}
+
+// generate runs the generation directives for a single file.
+func generate(pkg, absFile string) bool {
+ fd, err := os.Open(absFile)
+ if err != nil {
+ log.Fatalf("generate: %s", err)
+ }
+ defer fd.Close()
+ g := &Generator{
+ r: fd,
+ path: absFile,
+ pkg: pkg,
+ commands: make(map[string][]string),
+ }
+ return g.run()
+}
+
+// A Generator represents the state of a single Go source file
+// being scanned for generator commands.
+type Generator struct {
+ r io.Reader
+ path string // full rooted path name.
+ dir string // full rooted directory of file.
+ file string // base name of file.
+ pkg string
+ commands map[string][]string
+ lineNum int
+}
+
+// run runs the generators in the current file.
+func (g *Generator) run() (ok bool) {
+ // Processing below here calls g.errorf on failure, which does panic(stop).
+ // If we encounter an error, we abort the package.
+ defer func() {
+ e := recover()
+ if e != nil {
+ ok = false
+ if e != stop {
+ panic(e)
+ }
+ setExitStatus(1)
+ }
+ }()
+ g.dir, g.file = filepath.Split(g.path)
+ g.dir = filepath.Clean(g.dir) // No final separator please.
+ if buildV {
+ fmt.Fprintf(os.Stderr, "%s\n", shortPath(g.path))
+ }
+
+ // Scan for lines that start "//go:generate".
+ // Can't use bufio.Scanner because it can't handle long lines,
+ // which are likely to appear when using generate.
+ input := bufio.NewReader(g.r)
+ var err error
+ // One line per loop.
+ for {
+ g.lineNum++ // 1-indexed.
+ var buf []byte
+ buf, err = input.ReadSlice('\n')
+ if err == bufio.ErrBufferFull {
+ // Line too long - consume and ignore.
+ if isGoGenerate(buf) {
+ g.errorf("directive too long")
+ }
+ for err == bufio.ErrBufferFull {
+ _, err = input.ReadSlice('\n')
+ }
+ if err != nil {
+ break
+ }
+ continue
+ }
+
+ if err != nil {
+ // Check for marker at EOF without final \n.
+ if err == io.EOF && isGoGenerate(buf) {
+ err = io.ErrUnexpectedEOF
+ }
+ break
+ }
+
+ if !isGoGenerate(buf) {
+ continue
+ }
+
+ words := g.split(string(buf))
+ if len(words) == 0 {
+ g.errorf("no arguments to directive")
+ }
+ if words[0] == "-command" {
+ g.setShorthand(words)
+ continue
+ }
+ // Run the command line.
+ if buildN || buildX {
+ fmt.Fprintf(os.Stderr, "%s\n", strings.Join(words, " "))
+ }
+ if buildN {
+ continue
+ }
+ g.exec(words)
+ }
+ if err != nil && err != io.EOF {
+ g.errorf("error reading %s: %s", shortPath(g.path), err)
+ }
+ return true
+}
+
+func isGoGenerate(buf []byte) bool {
+ return bytes.HasPrefix(buf, []byte("//go:generate ")) || bytes.HasPrefix(buf, []byte("//go:generate\t"))
+}
+
+// split breaks the line into words, evaluating quoted
+// strings and evaluating environment variables.
+// The initial //go:generate element is present in line.
+func (g *Generator) split(line string) []string {
+ // Parse line, obeying quoted strings.
+ var words []string
+ line = line[len("//go:generate ") : len(line)-1] // Drop preamble and final newline.
+ // One (possibly quoted) word per iteration.
+Words:
+ for {
+ line = strings.TrimLeft(line, " \t")
+ if len(line) == 0 {
+ break
+ }
+ if line[0] == '"' {
+ for i := 1; i < len(line); i++ {
+ c := line[i] // Only looking for ASCII so this is OK.
+ switch c {
+ case '\\':
+ if i+1 == len(line) {
+ g.errorf("bad backslash")
+ }
+ i++ // Absorb next byte (If it's a multibyte we'll get an error in Unquote).
+ case '"':
+ word, err := strconv.Unquote(line[0 : i+1])
+ if err != nil {
+ g.errorf("bad quoted string")
+ }
+ words = append(words, word)
+ line = line[i+1:]
+ // Check the next character is space or end of line.
+ if len(line) > 0 && line[0] != ' ' && line[0] != '\t' {
+ g.errorf("expect space after quoted argument")
+ }
+ continue Words
+ }
+ }
+ g.errorf("mismatched quoted string")
+ }
+ i := strings.IndexAny(line, " \t")
+ if i < 0 {
+ i = len(line)
+ }
+ words = append(words, line[0:i])
+ line = line[i:]
+ }
+ // Substitute command if required.
+ if len(words) > 0 && g.commands[words[0]] != nil {
+ // Replace 0th word by command substitution.
+ words = append(g.commands[words[0]], words[1:]...)
+ }
+ // Substitute environment variables.
+ for i, word := range words {
+ words[i] = g.expandEnv(word)
+ }
+ return words
+}
+
+var stop = fmt.Errorf("error in generation")
+
+// errorf logs an error message prefixed with the file and line number.
+// It then exits the program (with exit status 1) because generation stops
+// at the first error.
+func (g *Generator) errorf(format string, args ...interface{}) {
+ fmt.Fprintf(os.Stderr, "%s:%d: %s\n", shortPath(g.path), g.lineNum,
+ fmt.Sprintf(format, args...))
+ panic(stop)
+}
+
+// expandEnv expands any $XXX invocations in word.
+func (g *Generator) expandEnv(word string) string {
+ if !strings.ContainsRune(word, '$') {
+ return word
+ }
+ var buf bytes.Buffer
+ var w int
+ var r rune
+ for i := 0; i < len(word); i += w {
+ r, w = utf8.DecodeRuneInString(word[i:])
+ if r != '$' {
+ buf.WriteRune(r)
+ continue
+ }
+ w += g.identLength(word[i+w:])
+ envVar := word[i+1 : i+w]
+ var sub string
+ switch envVar {
+ case "GOARCH":
+ sub = runtime.GOARCH
+ case "GOOS":
+ sub = runtime.GOOS
+ case "GOFILE":
+ sub = g.file
+ case "GOPACKAGE":
+ sub = g.pkg
+ default:
+ sub = os.Getenv(envVar)
+ }
+ buf.WriteString(sub)
+ }
+ return buf.String()
+}
+
+// identLength returns the length of the identifier beginning the string.
+func (g *Generator) identLength(word string) int {
+ for i, r := range word {
+ if r == '_' || unicode.IsLetter(r) || unicode.IsDigit(r) {
+ continue
+ }
+ return i
+ }
+ return len(word)
+}
+
+// setShorthand installs a new shorthand as defined by a -command directive.
+func (g *Generator) setShorthand(words []string) {
+ // Create command shorthand.
+ if len(words) == 1 {
+ g.errorf("no command specified for -command")
+ }
+ command := words[1]
+ if g.commands[command] != nil {
+ g.errorf("command %q defined multiply defined", command)
+ }
+ g.commands[command] = words[2:len(words):len(words)] // force later append to make copy
+}
+
+// exec runs the command specified by the argument. The first word is
+// the command name itself.
+func (g *Generator) exec(words []string) {
+ cmd := exec.Command(words[0], words[1:]...)
+ // Standard in and out of generator should be the usual.
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ // Run the command in the package directory.
+ cmd.Dir = g.dir
+ env := []string{
+ "GOARCH=" + runtime.GOARCH,
+ "GOOS=" + runtime.GOOS,
+ "GOFILE=" + g.file,
+ "GOPACKAGE=" + g.pkg,
+ }
+ cmd.Env = mergeEnvLists(env, os.Environ())
+ err := cmd.Run()
+ if err != nil {
+ g.errorf("running %q: %s", words[0], err)
+ }
+}
diff --git a/src/cmd/go/generate_test.go b/src/cmd/go/generate_test.go
new file mode 100644
index 000000000..660ebabbe
--- /dev/null
+++ b/src/cmd/go/generate_test.go
@@ -0,0 +1,48 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "reflect"
+ "runtime"
+ "testing"
+)
+
+type splitTest struct {
+ in string
+ out []string
+}
+
+var splitTests = []splitTest{
+ {"", nil},
+ {"x", []string{"x"}},
+ {" a b\tc ", []string{"a", "b", "c"}},
+ {` " a " `, []string{" a "}},
+ {"$GOARCH", []string{runtime.GOARCH}},
+ {"$GOOS", []string{runtime.GOOS}},
+ {"$GOFILE", []string{"proc.go"}},
+ {"$GOPACKAGE", []string{"sys"}},
+ {"a $XXNOTDEFINEDXX b", []string{"a", "", "b"}},
+ {"/$XXNOTDEFINED/", []string{"//"}},
+ {"yacc -o $GOARCH/yacc_$GOFILE", []string{"go", "tool", "yacc", "-o", runtime.GOARCH + "/yacc_proc.go"}},
+}
+
+func TestGenerateCommandParse(t *testing.T) {
+ g := &Generator{
+ r: nil, // Unused here.
+ path: "/usr/ken/sys/proc.go",
+ dir: "/usr/ken/sys",
+ file: "proc.go",
+ pkg: "sys",
+ commands: make(map[string][]string),
+ }
+ g.setShorthand([]string{"-command", "yacc", "go", "tool", "yacc"})
+ for _, test := range splitTests {
+ got := g.split("//go:generate " + test.in + "\n")
+ if !reflect.DeepEqual(got, test.out) {
+ t.Errorf("split(%q): got %q expected %q", test.in, got, test.out)
+ }
+ }
+}
diff --git a/src/cmd/go/get.go b/src/cmd/go/get.go
index e708fcf77..86e169761 100644
--- a/src/cmd/go/get.go
+++ b/src/cmd/go/get.go
@@ -16,7 +16,7 @@ import (
)
var cmdGet = &Command{
- UsageLine: "get [-d] [-fix] [-t] [-u] [build flags] [packages]",
+ UsageLine: "get [-d] [-f] [-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,
@@ -25,6 +25,11 @@ along with their dependencies.
The -d flag instructs get to stop after downloading the packages; that is,
it instructs get not to install the packages.
+The -f flag, valid only when -u is set, forces get -u not to verify that
+each package has been checked out from the source control repository
+implied by its import path. This can be useful if the source is a local fork
+of the original.
+
The -fix flag instructs get to run the fix tool on the downloaded packages
before resolving dependencies or building the code.
@@ -53,6 +58,7 @@ See also: go build, go install, go clean.
}
var getD = cmdGet.Flag.Bool("d", false, "")
+var getF = cmdGet.Flag.Bool("f", false, "")
var getT = cmdGet.Flag.Bool("t", false, "")
var getU = cmdGet.Flag.Bool("u", false, "")
var getFix = cmdGet.Flag.Bool("fix", false, "")
@@ -63,6 +69,10 @@ func init() {
}
func runGet(cmd *Command, args []string) {
+ if *getF && !*getU {
+ fatalf("go get: cannot use -f flag without -u")
+ }
+
// Phase 1. Download/update.
var stk importStack
for _, arg := range downloadPaths(args) {
@@ -151,7 +161,9 @@ func download(arg string, stk *importStack, getTestDeps bool) {
}
// Only process each package once.
- if downloadCache[arg] {
+ // (Unless we're fetching test dependencies for this package,
+ // in which case we want to process it again.)
+ if downloadCache[arg] && !getTestDeps {
return
}
downloadCache[arg] = true
@@ -264,6 +276,25 @@ func downloadPackage(p *Package) error {
return err
}
repo = "<local>" // should be unused; make distinctive
+
+ // Double-check where it came from.
+ if *getU && vcs.remoteRepo != nil && !*getF {
+ dir := filepath.Join(p.build.SrcRoot, rootPath)
+ if remote, err := vcs.remoteRepo(vcs, dir); err == nil {
+ if rr, err := repoRootForImportPath(p.ImportPath); err == nil {
+ repo := rr.repo
+ if rr.vcs.resolveRepo != nil {
+ resolved, err := rr.vcs.resolveRepo(rr.vcs, dir, repo)
+ if err == nil {
+ repo = resolved
+ }
+ }
+ if remote != repo {
+ return fmt.Errorf("%s is from %s, should be from %s", dir, remote, repo)
+ }
+ }
+ }
+ }
} else {
// Analyze the import path to determine the version control system,
// repository, and the import path for the root of the repository.
diff --git a/src/cmd/go/go_windows_test.go b/src/cmd/go/go_windows_test.go
new file mode 100644
index 000000000..53d695ccc
--- /dev/null
+++ b/src/cmd/go/go_windows_test.go
@@ -0,0 +1,55 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strings"
+ "testing"
+)
+
+func TestAbsolutePath(t *testing.T) {
+ tmp, err := ioutil.TempDir("", "TestAbsolutePath")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(tmp)
+
+ file := filepath.Join(tmp, "a.go")
+ err = ioutil.WriteFile(file, []byte{}, 0644)
+ if err != nil {
+ t.Fatal(err)
+ }
+ dir := filepath.Join(tmp, "dir")
+ err = os.Mkdir(dir, 0777)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ wd, err := os.Getwd()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.Chdir(wd)
+
+ // Chdir so current directory and a.go reside on the same drive.
+ err = os.Chdir(dir)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ noVolume := file[len(filepath.VolumeName(file)):]
+ wrongPath := filepath.Join(dir, noVolume)
+ output, err := exec.Command("go", "build", noVolume).CombinedOutput()
+ if err == nil {
+ t.Fatal("build should fail")
+ }
+ if strings.Contains(string(output), wrongPath) {
+ t.Fatalf("wrong output found: %v %v", err, string(output))
+ }
+}
diff --git a/src/cmd/go/help.go b/src/cmd/go/help.go
index 40da7e1f5..c590fdb37 100644
--- a/src/cmd/go/help.go
+++ b/src/cmd/go/help.go
@@ -81,7 +81,8 @@ 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.
+Directory and file names that begin with "." or "_" are ignored
+by the go tool, as are directories named "testdata".
`,
}
@@ -154,6 +155,11 @@ A few common code hosting sites have special syntax:
import "launchpad.net/~user/project/branch"
import "launchpad.net/~user/project/branch/sub/directory"
+ IBM DevOps Services (Git)
+
+ import "hub.jazz.net/git/user/project"
+ import "hub.jazz.net/git/user/project/sub/directory"
+
For code hosted on other servers, import paths may either be qualified
with the version control type, or the go tool can dynamically fetch
the import path over https/http and discover where the code resides
@@ -229,7 +235,26 @@ listed in the GOPATH environment variable (see 'go help gopath').
The go command attempts to download the version of the
package appropriate for the Go release being used.
-Run 'go help install' for more.
+Run 'go help get' for more.
+
+Import path checking
+
+When the custom import path feature described above redirects to a
+known code hosting site, each of the resulting packages has two possible
+import paths, using the custom domain or the known hosting site.
+
+A package statement is said to have an "import comment" if it is immediately
+followed (before the next newline) by a comment of one of these two forms:
+
+ package math // import "path"
+ package math /* import "path" */
+
+The go command will refuse to install a package with an import comment
+unless it is being referred to by that import path. In this way, import comments
+let package authors make sure the custom import path is used and not a
+direct path to the underlying code hosting site.
+
+See https://golang.org/s/go14customimport for details.
`,
}
diff --git a/src/cmd/go/list.go b/src/cmd/go/list.go
index 0ead43502..fbf96167f 100644
--- a/src/cmd/go/list.go
+++ b/src/cmd/go/list.go
@@ -30,28 +30,29 @@ syntax of package template. The default output is equivalent to -f
'{{.ImportPath}}'. The struct being passed to the template is:
type Package struct {
- Dir string // directory containing package sources
- ImportPath string // import path of package in dir
- Name string // package name
- Doc string // package documentation string
- Target string // install path
- Goroot bool // is this package in the Go root?
- Standard bool // is this package part of the standard Go library?
- Stale bool // would 'go install' do anything for this package?
- Root string // Go root or Go path dir containing this package
+ Dir string // directory containing package sources
+ ImportPath string // import path of package in dir
+ ImportComment string // path in import comment on package statement
+ Name string // package name
+ Doc string // package documentation string
+ Target string // install path
+ Goroot bool // is this package in the Go root?
+ Standard bool // is this package part of the standard Go library?
+ Stale bool // would 'go install' do anything for this package?
+ Root string // Go root or Go path dir containing this package
// Source files
- GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
- CgoFiles []string // .go sources files that import "C"
+ GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
+ CgoFiles []string // .go sources files that import "C"
IgnoredGoFiles []string // .go sources ignored due to build constraints
- CFiles []string // .c source files
- CXXFiles []string // .cc, .cxx and .cpp source files
- MFiles []string // .m source files
- HFiles []string // .h, .hh, .hpp and .hxx source files
- SFiles []string // .s source files
- SwigFiles []string // .swig files
- SwigCXXFiles []string // .swigcxx files
- SysoFiles []string // .syso object files to add to archive
+ CFiles []string // .c source files
+ CXXFiles []string // .cc, .cxx and .cpp source files
+ MFiles []string // .m source files
+ HFiles []string // .h, .hh, .hpp and .hxx source files
+ SFiles []string // .s source files
+ 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
diff --git a/src/cmd/go/main.go b/src/cmd/go/main.go
index 5b1194aaa..9691f39c7 100644
--- a/src/cmd/go/main.go
+++ b/src/cmd/go/main.go
@@ -79,6 +79,7 @@ var commands = []*Command{
cmdEnv,
cmdFix,
cmdFmt,
+ cmdGenerate,
cmdGet,
cmdInstall,
cmdList,
@@ -536,7 +537,7 @@ func matchPackages(pattern string) []string {
})
for _, src := range buildContext.SrcDirs() {
- if pattern == "std" && src != gorootSrcPkg {
+ if pattern == "std" && src != gorootSrc {
continue
}
src = filepath.Clean(src) + string(filepath.Separator)
@@ -618,7 +619,7 @@ func matchPackagesInFS(pattern string) []string {
// The initial case is not Cleaned, though, so we do this explicitly.
//
// This converts a path like "./io/" to "io". Without this step, running
- // "cd $GOROOT/src/pkg; go list ./io/..." would incorrectly skip the io
+ // "cd $GOROOT/src; go list ./io/..." would incorrectly skip the io
// package, because prepending the prefix "./" to the unclean path would
// result in "././io", and match("././io") returns false.
path = filepath.Clean(path)
diff --git a/src/cmd/go/mkdoc.sh b/src/cmd/go/mkdoc.sh
index 12fd7ba3e..e15e8809c 100755
--- a/src/cmd/go/mkdoc.sh
+++ b/src/cmd/go/mkdoc.sh
@@ -4,6 +4,6 @@
# license that can be found in the LICENSE file.
go install # So the next line will produce updated documentation.
-go help documentation > doc.go
+go help documentation | sed 's; \*/; * /;' >doc.go
gofmt -w doc.go
diff --git a/src/cmd/go/pkg.go b/src/cmd/go/pkg.go
index d45df265b..b71feb7a6 100644
--- a/src/cmd/go/pkg.go
+++ b/src/cmd/go/pkg.go
@@ -14,6 +14,7 @@ import (
"os"
pathpkg "path"
"path/filepath"
+ "runtime"
"sort"
"strings"
"time"
@@ -25,16 +26,17 @@ 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
- ConflictDir string `json:",omitempty"` // Dir is hidden by this other directory
+ Dir string `json:",omitempty"` // directory containing package sources
+ ImportPath string `json:",omitempty"` // import path of package in dir
+ ImportComment string `json:",omitempty"` // path in import comment on package statement
+ 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)
@@ -103,6 +105,7 @@ func (p *Package) copyBuild(pp *build.Package) {
p.Dir = pp.Dir
p.ImportPath = pp.ImportPath
+ p.ImportComment = pp.ImportComment
p.Name = pp.Name
p.Doc = pp.Doc
p.Root = pp.Root
@@ -218,7 +221,7 @@ func dirToImportPath(dir string) string {
}
func makeImportValid(r rune) rune {
- // Should match Go spec, compilers, and ../../pkg/go/parser/parser.go:/isValidImport.
+ // Should match Go spec, compilers, and ../../go/parser/parser.go:/isValidImport.
const illegalChars = `!"#$%&'()*,:;<=>?[\]^{|}` + "`\uFFFD"
if !unicode.IsGraphic(r) || unicode.IsSpace(r) || strings.ContainsRune(illegalChars, r) {
return '_'
@@ -244,6 +247,9 @@ func loadImport(path string, srcDir string, stk *importStack, importPos []token.
importPath = dirToImportPath(filepath.Join(srcDir, path))
}
if p := packageCache[importPath]; p != nil {
+ if perr := disallowInternal(srcDir, p, stk); perr != p {
+ return perr
+ }
return reusePackage(p, stk)
}
@@ -258,11 +264,14 @@ func loadImport(path string, srcDir string, stk *importStack, importPos []token.
//
// TODO: After Go 1, decide when to pass build.AllowBinary here.
// See issue 3268 for mistakes to avoid.
- bp, err := buildContext.Import(path, srcDir, 0)
+ bp, err := buildContext.Import(path, srcDir, build.ImportComment)
bp.ImportPath = importPath
if gobin != "" {
bp.BinDir = gobin
}
+ if err == nil && !isLocal && bp.ImportComment != "" && bp.ImportComment != path {
+ err = fmt.Errorf("code in directory %s expects import %q", bp.Dir, bp.ImportComment)
+ }
p.load(stk, bp, err)
if p.Error != nil && len(importPos) > 0 {
pos := importPos[0]
@@ -270,6 +279,10 @@ func loadImport(path string, srcDir string, stk *importStack, importPos []token.
p.Error.Pos = pos.String()
}
+ if perr := disallowInternal(srcDir, p, stk); perr != p {
+ return perr
+ }
+
return p
}
@@ -298,12 +311,82 @@ func reusePackage(p *Package, stk *importStack) *Package {
return p
}
+// disallowInternal checks that srcDir is allowed to import p.
+// If the import is allowed, disallowInternal returns the original package p.
+// If not, it returns a new package containing just an appropriate error.
+func disallowInternal(srcDir string, p *Package, stk *importStack) *Package {
+ // golang.org/s/go14internal:
+ // An import of a path containing the element “internal”
+ // is disallowed if the importing code is outside the tree
+ // rooted at the parent of the “internal” directory.
+ //
+ // ... For Go 1.4, we will implement the rule first for $GOROOT, but not $GOPATH.
+
+ // Only applies to $GOROOT.
+ if !p.Standard {
+ return p
+ }
+
+ // The stack includes p.ImportPath.
+ // If that's the only thing on the stack, we started
+ // with a name given on the command line, not an
+ // import. Anything listed on the command line is fine.
+ if len(*stk) == 1 {
+ return p
+ }
+
+ // Check for "internal" element: four cases depending on begin of string and/or end of string.
+ i, ok := findInternal(p.ImportPath)
+ if !ok {
+ return p
+ }
+
+ // Internal is present.
+ // Map import path back to directory corresponding to parent of internal.
+ if i > 0 {
+ i-- // rewind over slash in ".../internal"
+ }
+ parent := p.Dir[:i+len(p.Dir)-len(p.ImportPath)]
+ if hasPathPrefix(filepath.ToSlash(srcDir), filepath.ToSlash(parent)) {
+ return p
+ }
+
+ // Internal is present, and srcDir is outside parent's tree. Not allowed.
+ perr := *p
+ perr.Error = &PackageError{
+ ImportStack: stk.copy(),
+ Err: "use of internal package not allowed",
+ }
+ perr.Incomplete = true
+ return &perr
+}
+
+// findInternal looks for the final "internal" path element in the given import path.
+// If there isn't one, findInternal returns ok=false.
+// Otherwise, findInternal returns ok=true and the index of the "internal".
+func findInternal(path string) (index int, ok bool) {
+ // Four cases, depending on internal at start/end of string or not.
+ // The order matters: we must return the index of the final element,
+ // because the final one produces the most restrictive requirement
+ // on the importer.
+ switch {
+ case strings.HasSuffix(path, "/internal"):
+ return len(path) - len("internal"), true
+ case strings.Contains(path, "/internal/"):
+ return strings.LastIndex(path, "/internal/") + 1, true
+ case path == "internal", strings.HasPrefix(path, "internal/"):
+ return 0, true
+ }
+ return 0, false
+}
+
type targetDir int
const (
- toRoot targetDir = iota // to bin dir inside package root (default)
- toTool // GOROOT/pkg/tool
- toBin // GOROOT/bin
+ toRoot targetDir = iota // to bin dir inside package root (default)
+ toTool // GOROOT/pkg/tool
+ toBin // GOROOT/bin
+ stalePath // the old import path; fail to build
)
// goTools is a map of Go program import path to install target directory.
@@ -316,10 +399,14 @@ var goTools = map[string]targetDir{
"cmd/nm": toTool,
"cmd/objdump": toTool,
"cmd/pack": toTool,
+ "cmd/pprof": 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,
+ "golang.org/x/tools/cmd/cover": toTool,
+ "golang.org/x/tools/cmd/godoc": toBin,
+ "golang.org/x/tools/cmd/vet": toTool,
+ "code.google.com/p/go.tools/cmd/cover": stalePath,
+ "code.google.com/p/go.tools/cmd/godoc": stalePath,
+ "code.google.com/p/go.tools/cmd/vet": stalePath,
}
// expandScanner expands a scanner.List error into all the errors in the list.
@@ -380,6 +467,13 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package
}
if p.Name == "main" {
+ // Report an error when the old code.google.com/p/go.tools paths are used.
+ if goTools[p.ImportPath] == stalePath {
+ newPath := strings.Replace(p.ImportPath, "code.google.com/p/go.", "golang.org/x/", 1)
+ e := fmt.Sprintf("the %v command has moved; use %v instead.", p.ImportPath, newPath)
+ p.Error = &PackageError{Err: e}
+ return p
+ }
_, elem := filepath.Split(p.Dir)
full := buildContext.GOOS + "_" + buildContext.GOARCH + "/" + elem
if buildContext.GOOS != toolGOOS || buildContext.GOARCH != toolGOARCH {
@@ -482,7 +576,7 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package
// Build list of imported packages and full dependency list.
imports := make([]*Package, 0, len(p.Imports))
- deps := make(map[string]bool)
+ deps := make(map[string]*Package)
for i, path := range importPaths {
if path == "C" {
continue
@@ -502,10 +596,10 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package
path = p1.ImportPath
importPaths[i] = path
}
- deps[path] = true
+ deps[path] = p1
imports = append(imports, p1)
- for _, dep := range p1.Deps {
- deps[dep] = true
+ for _, dep := range p1.deps {
+ deps[dep.ImportPath] = dep
}
if p1.Incomplete {
p.Incomplete = true
@@ -519,7 +613,7 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package
}
sort.Strings(p.Deps)
for _, dep := range p.Deps {
- p1 := packageCache[dep]
+ p1 := deps[dep]
if p1 == nil {
panic("impossible: missing entry in package cache for " + dep + " imported by " + p.ImportPath)
}
@@ -535,6 +629,16 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package
}
p.Target = p.target
+ // Check for C code compiled with Plan 9 C compiler.
+ // No longer allowed except in runtime and runtime/cgo, for now.
+ if len(p.CFiles) > 0 && !p.usesCgo() && (!p.Standard || p.ImportPath != "runtime") {
+ p.Error = &PackageError{
+ ImportStack: stk.copy(),
+ Err: fmt.Sprintf("C source files not allowed when not using cgo: %s", strings.Join(p.CFiles, " ")),
+ }
+ return p
+ }
+
// In the absence of errors lower in the dependency tree,
// check for case-insensitive collisions of import paths.
if len(p.DepsErrors) == 0 {
@@ -596,6 +700,12 @@ func computeStale(pkgs ...*Package) {
}
}
+// The runtime version string takes one of two forms:
+// "go1.X[.Y]" for Go releases, and "devel +hash" at tip.
+// Determine whether we are in a released copy by
+// inspecting the version.
+var isGoRelease = strings.HasPrefix(runtime.Version(), "go1")
+
// isStale reports whether package p needs to be rebuilt.
func isStale(p *Package, topRoot map[string]bool) bool {
if p.Standard && (p.ImportPath == "unsafe" || buildContext.Compiler == "gccgo") {
@@ -616,7 +726,16 @@ func isStale(p *Package, topRoot map[string]bool) bool {
return false
}
- if buildA || p.target == "" || p.Stale {
+ // If we are running a release copy of Go, do not rebuild the standard packages.
+ // They may not be writable anyway, but they are certainly not changing.
+ // This makes 'go build -a' skip the standard packages when using an official release.
+ // See issue 4106 and issue 8290.
+ pkgBuildA := buildA
+ if p.Standard && isGoRelease {
+ pkgBuildA = false
+ }
+
+ if pkgBuildA || p.target == "" || p.Stale {
return true
}
@@ -704,24 +823,13 @@ func loadPackage(arg string, stk *importStack) *Package {
arg = sub
}
}
- if strings.HasPrefix(arg, "cmd/") {
+ if strings.HasPrefix(arg, "cmd/") && !strings.Contains(arg[4:], "/") {
if p := cmdCache[arg]; p != nil {
return p
}
stk.push(arg)
defer stk.pop()
- if strings.Contains(arg[4:], "/") {
- p := &Package{
- Error: &PackageError{
- ImportStack: stk.copy(),
- Err: fmt.Sprintf("invalid import path: cmd/... is reserved for Go commands"),
- hard: true,
- },
- }
- return p
- }
-
bp, err := buildContext.ImportDir(filepath.Join(gorootSrc, arg), 0)
bp.ImportPath = arg
bp.Goroot = true
diff --git a/src/cmd/go/test.bash b/src/cmd/go/test.bash
index 0060ce218..e0f066f18 100755
--- a/src/cmd/go/test.bash
+++ b/src/cmd/go/test.bash
@@ -4,18 +4,22 @@
# license that can be found in the LICENSE file.
set -e
-go build -o testgo
+go build -tags testgo -o testgo
go() {
echo TEST ERROR: ran go, not testgo: go "$@" >&2
exit 2
}
started=false
+testdesc=""
+nl="
+"
TEST() {
if $started; then
stop
fi
echo TEST: "$@"
+ testdesc="$@"
started=true
ok=true
}
@@ -29,6 +33,7 @@ stop() {
echo PASS
else
echo FAIL
+ testfail="$testfail $testdesc$nl"
allok=false
fi
}
@@ -55,12 +60,63 @@ if ! grep -q "^$fn:" $d/err.out; then
fi
rm -r $d
+TEST 'program name in crash messages'
+linker=$(./testgo env GOCHAR)l
+d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX)
+./testgo build -ldflags -crash_for_testing $(./testgo env GOROOT)/test/helloworld.go 2>$d/err.out || true
+if ! grep -q "/tool/.*/$linker" $d/err.out; then
+ echo "missing linker name in error message"
+ cat $d/err.out
+ ok=false
+fi
+rm -r $d
+
+TEST broken tests without Test functions all fail
+d=$(mktemp -d -t testgoXXX)
+./testgo test ./testdata/src/badtest/... >$d/err 2>&1 || true
+if grep -q '^ok' $d/err; then
+ echo test passed unexpectedly:
+ grep '^ok' $d/err
+ ok=false
+elif ! grep -q 'FAIL.*badtest/badexec' $d/err || ! grep -q 'FAIL.*badtest/badsyntax' $d/err || ! grep -q 'FAIL.*badtest/badvar' $d/err; then
+ echo test did not run everything
+ cat $d/err
+ ok=false
+fi
+rm -rf $d
+
+TEST 'go build -a in dev branch'
+./testgo install math || ok=false # should be up to date already but just in case
+d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX)
+if ! TESTGO_IS_GO_RELEASE=0 ./testgo build -v -a math 2>$d/err.out; then
+ cat $d/err.out
+ ok=false
+elif ! grep -q runtime $d/err.out; then
+ echo "testgo build -a math in dev branch DID NOT build runtime, but should have"
+ cat $d/err.out
+ ok=false
+fi
+rm -r $d
+
+TEST 'go build -a in release branch'
+./testgo install math || ok=false # should be up to date already but just in case
+d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX)
+if ! TESTGO_IS_GO_RELEASE=1 ./testgo build -v -a math 2>$d/err.out; then
+ cat $d/err.out
+ ok=false
+elif grep -q runtime $d/err.out; then
+ echo "testgo build -a math in dev branch DID build runtime, but should NOT have"
+ cat $d/err.out
+ ok=false
+fi
+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
+ ./testgo build -o hello "testdata/$local/easy.go" || ok=false
+ ./hello >hello.out || ok=false
if ! grep -q '^easysub\.Hello' hello.out; then
echo "testdata/$local/easy.go did not generate expected output"
cat hello.out
@@ -68,8 +124,8 @@ testlocal() {
fi
TEST local imports $2 '(easysub)'
- ./testgo build -o hello "testdata/$local/easysub/main.go"
- ./hello >hello.out
+ ./testgo build -o hello "testdata/$local/easysub/main.go" || ok=false
+ ./hello >hello.out || ok=false
if ! grep -q '^easysub\.Hello' hello.out; then
echo "testdata/$local/easysub/main.go did not generate expected output"
cat hello.out
@@ -77,8 +133,8 @@ testlocal() {
fi
TEST local imports $2 '(hard)'
- ./testgo build -o hello "testdata/$local/hard.go"
- ./hello >hello.out
+ ./testgo build -o hello "testdata/$local/hard.go" || ok=false
+ ./hello >hello.out || ok=false
if ! grep -q '^sub\.Hello' hello.out || ! grep -q '^subsub\.Hello' hello.out ; then
echo "testdata/$local/hard.go did not generate expected output"
cat hello.out
@@ -105,6 +161,132 @@ cp -R testdata/local "testdata/$bad"
testlocal "$bad" 'with bad characters in path'
rm -rf "testdata/$bad"
+TEST 'internal packages in $GOROOT are respected'
+if ./testgo build -v ./testdata/testinternal >testdata/std.out 2>&1; then
+ echo "go build ./testdata/testinternal succeeded incorrectly"
+ ok=false
+elif ! grep 'use of internal package not allowed' testdata/std.out >/dev/null; then
+ echo "wrong error message for testdata/testinternal"
+ cat std.out
+ ok=false
+fi
+
+TEST 'internal packages outside $GOROOT are not respected'
+if ! ./testgo build -v ./testdata/testinternal2; then
+ echo "go build ./testdata/testinternal2 failed"
+ ok=false
+fi
+
+# Test that 'go get -u' reports moved packages.
+testmove() {
+ vcs=$1
+ url=$2
+ base=$3
+ config=$4
+
+ TEST go get -u notices $vcs package that moved
+ d=$(mktemp -d -t testgoXXX)
+ mkdir -p $d/src
+ if ! GOPATH=$d ./testgo get -d $url; then
+ echo 'go get -d $url failed'
+ ok=false
+ elif ! GOPATH=$d ./testgo get -d -u $url; then
+ echo 'go get -d -u $url failed'
+ ok=false
+ else
+ set +e
+ case "$vcs" in
+ svn)
+ # SVN doesn't believe in text files so we can't just edit the config.
+ # Check out a different repo into the wrong place.
+ rm -rf $d/src/code.google.com/p/rsc-svn
+ GOPATH=$d ./testgo get -d -u code.google.com/p/rsc-svn2/trunk
+ mv $d/src/code.google.com/p/rsc-svn2 $d/src/code.google.com/p/rsc-svn
+ ;;
+ *)
+ echo '1,$s;'"$base"';'"$base"'XXX;
+w
+q' | ed $d/src/$config >/dev/null 2>&1
+ esac
+ set -e
+
+ if GOPATH=$d ./testgo get -d -u $url 2>$d/err; then
+ echo "go get -d -u $url succeeded with wrong remote repo"
+ cat $d/err
+ ok=false
+ elif ! grep 'should be from' $d/err >/dev/null; then
+ echo "go get -d -u $url failed for wrong reason"
+ cat $d/err
+ ok=false
+ fi
+
+ if GOPATH=$d ./testgo get -d -f -u $url 2>$d/err; then
+ echo "go get -d -u $url succeeded with wrong remote repo"
+ cat $d/err
+ ok=false
+ elif ! egrep -i 'validating server certificate|not found' $d/err >/dev/null; then
+ echo "go get -d -f -u $url failed for wrong reason"
+ cat $d/err
+ ok=false
+ fi
+ fi
+ rm -rf $d
+}
+
+testmove hg rsc.io/x86/x86asm x86 rsc.io/x86/.hg/hgrc
+testmove git rsc.io/pdf pdf rsc.io/pdf/.git/config
+testmove svn code.google.com/p/rsc-svn/trunk - -
+
+export GOPATH=$(pwd)/testdata/importcom
+TEST 'import comment - match'
+if ! ./testgo build ./testdata/importcom/works.go; then
+ echo 'go build ./testdata/importcom/works.go failed'
+ ok=false
+fi
+TEST 'import comment - mismatch'
+if ./testgo build ./testdata/importcom/wrongplace.go 2>testdata/err; then
+ echo 'go build ./testdata/importcom/wrongplace.go suceeded'
+ ok=false
+elif ! grep 'wrongplace expects import "my/x"' testdata/err >/dev/null; then
+ echo 'go build did not mention incorrect import:'
+ cat testdata/err
+ ok=false
+fi
+TEST 'import comment - syntax error'
+if ./testgo build ./testdata/importcom/bad.go 2>testdata/err; then
+ echo 'go build ./testdata/importcom/bad.go suceeded'
+ ok=false
+elif ! grep 'cannot parse import comment' testdata/err >/dev/null; then
+ echo 'go build did not mention syntax error:'
+ cat testdata/err
+ ok=false
+fi
+TEST 'import comment - conflict'
+if ./testgo build ./testdata/importcom/conflict.go 2>testdata/err; then
+ echo 'go build ./testdata/importcom/conflict.go suceeded'
+ ok=false
+elif ! grep 'found import comments' testdata/err >/dev/null; then
+ echo 'go build did not mention comment conflict:'
+ cat testdata/err
+ ok=false
+fi
+rm -f ./testdata/err
+unset GOPATH
+
+export GOPATH=$(pwd)/testdata/src
+TEST disallowed C source files
+export GOPATH=$(pwd)/testdata
+if ./testgo build badc 2>testdata/err; then
+ echo 'go build badc succeeded'
+ ok=false
+elif ! grep 'C source files not allowed' testdata/err >/dev/null; then
+ echo 'go test did not say C source files not allowed:'
+ cat testdata/err
+ ok=false
+fi
+rm -f ./testdata/err
+unset GOPATH
+
TEST error message for syntax error in test go file says FAIL
export GOPATH=$(pwd)/testdata
if ./testgo test syntaxerror 2>testdata/err; then
@@ -251,20 +433,20 @@ 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
+GOBIN=$d/gobin ./testgo get golang.org/x/tools/cmd/godoc || ok=false
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
+ GOBIN=$d/gobin ./testgo list -f 'Target: {{.Target}}' golang.org/x/tools/cmd/godoc || true
ok=false
fi
TEST godoc installs into GOROOT
GOROOT=$(./testgo env GOROOT)
rm -f $GOROOT/bin/godoc
-./testgo install code.google.com/p/go.tools/cmd/godoc
+./testgo install golang.org/x/tools/cmd/godoc || ok=false
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
+ ./testgo list -f 'Target: {{.Target}}' golang.org/x/tools/cmd/godoc || true
ok=false
fi
@@ -272,36 +454,36 @@ 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
+./testgo install cmd/fix || ok=false
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
+ GOBIN=$d/gobin ./testgo list -f 'Target: {{.Target}}' cmd/fix || true
ok=false
fi
rm -f $GOROOT/pkg/tool/${GOOS}_${GOARCH}/fix
-GOBIN=$d/gobin ./testgo install cmd/fix
+GOBIN=$d/gobin ./testgo install cmd/fix || ok=false
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
+ GOBIN=$d/gobin ./testgo list -f 'Target: {{.Target}}' cmd/fix || true
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
+GOBIN=$d/gobin ./testgo install progname || ok=false
if [ ! -x $d/gobin/progname ]; then
echo 'did not install progname to $GOBIN/progname'
- ./testgo list -f 'Target: {{.Target}}' cmd/api
+ ./testgo list -f 'Target: {{.Target}}' cmd/api || true
ok=false
fi
rm -f $d/gobin/progname $d/bin/progname
TEST gopath program installs into GOPATH/bin
-./testgo install progname
+./testgo install progname || ok=false
if [ ! -x $d/bin/progname ]; then
echo 'did not install progname to $GOPATH/bin/progname'
- ./testgo list -f 'Target: {{.Target}}' progname
+ ./testgo list -f 'Target: {{.Target}}' progname || true
ok=false
fi
@@ -330,7 +512,7 @@ fi
# ensure that output of 'go list' is consistent between runs
TEST go list is consistent
-./testgo list std > test_std.list
+./testgo list std > test_std.list || ok=false
if ! ./testgo list std | cmp -s test_std.list - ; then
echo "go list std ordering is inconsistent"
ok=false
@@ -378,9 +560,9 @@ fi
# 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
- echo 'go get code.google.com/p/go.codereview/cmd/hgpatch should not succeed with $GOPATH unset'
+mkdir -p $d/src
+if GOPATH= GOROOT=$d ./testgo get -d golang.org/x/codereview/cmd/hgpatch ; then
+ echo 'go get golang.org/x/codereview/cmd/hgpatch should not succeed with $GOPATH unset'
ok=false
fi
rm -rf $d
@@ -388,9 +570,9 @@ 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
- echo 'go get code.google.com/p/go.codereview/cmd/hgpatch should not succeed with GOPATH=$GOROOT'
+mkdir -p $d/src
+if GOPATH=$d GOROOT=$d ./testgo get -d golang.org/x/codereview/cmd/hgpatch ; then
+ echo 'go get golang.org/x/codereview/cmd/hgpatch should not succeed with GOPATH=$GOROOT'
ok=false
fi
rm -rf $d
@@ -404,7 +586,7 @@ func main() {
println(extern)
}
EOF
-./testgo run -ldflags '-X main.extern "hello world"' $d/main.go 2>hello.out
+./testgo run -ldflags '-X main.extern "hello world"' $d/main.go 2>hello.out || ok=false
if ! grep -q '^hello world' hello.out; then
echo "ldflags -X main.extern 'hello world' failed. Output:"
cat hello.out
@@ -420,6 +602,30 @@ if [ ! -x strings.test ]; then
fi
rm -f strings.prof strings.test
+TEST go test -cpuprofile -o controls binary location
+./testgo test -cpuprofile strings.prof -o mystrings.test strings || ok=false
+if [ ! -x mystrings.test ]; then
+ echo "go test -cpuprofile -o mystrings.test did not create mystrings.test"
+ ok=false
+fi
+rm -f strings.prof mystrings.test
+
+TEST go test -c -o controls binary location
+./testgo test -c -o mystrings.test strings || ok=false
+if [ ! -x mystrings.test ]; then
+ echo "go test -c -o mystrings.test did not create mystrings.test"
+ ok=false
+fi
+rm -f mystrings.test
+
+TEST go test -o writes binary
+./testgo test -o mystrings.test strings || ok=false
+if [ ! -x mystrings.test ]; then
+ echo "go test -o mystrings.test did not create mystrings.test"
+ ok=false
+fi
+rm -f mystrings.test
+
TEST symlinks do not confuse go list '(issue 4568)'
old=$(pwd)
tmp=$(cd /tmp && pwd -P)
@@ -522,37 +728,56 @@ elif ! grep "case-insensitive file name collision" $d/out >/dev/null; then
fi
TEST go get cover
-./testgo get code.google.com/p/go.tools/cmd/cover || ok=false
+./testgo get golang.org/x/tools/cmd/cover || ok=false
unset GOPATH
rm -rf $d
+TEST go get -t "code.google.com/p/go-get-issue-8181/{a,b}"
+d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX)
+export GOPATH=$d
+if ./testgo get -t code.google.com/p/go-get-issue-8181/{a,b}; then
+ ./testgo list ... | grep go.tools/godoc > /dev/null || ok=false
+else
+ ok=false
+fi
+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.
+set +e
cdir=$(./testgo list -f '({{.ImportPath}}) ({{.ConflictDir}})' ./testdata/shadow/root1/src/math)
-if [ "$cdir" != "(_$(pwd)/testdata/shadow/root1/src/math) ($GOROOT/src/pkg/math)" ]; then
+set -e
+if [ "$cdir" != "(_$(pwd)/testdata/shadow/root1/src/math) ($GOROOT/src/math)" ]; then
echo shadowed math is not shadowed: "$cdir"
ok=false
fi
# The foo in root1 is "foo".
+set +e
cdir=$(./testgo list -f '({{.ImportPath}}) ({{.ConflictDir}})' ./testdata/shadow/root1/src/foo)
+set -e
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.
+set +e
cdir=$(./testgo list -f '({{.ImportPath}}) ({{.ConflictDir}})' ./testdata/shadow/root2/src/foo)
+set -e
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)
+set +e
+err=$(./testgo install ./testdata/shadow/root2/src/foo 2>&1)
+set -e
if [ "$err" != "go install: no install location for $(pwd)/testdata/shadow/root2/src/foo: hidden by $(pwd)/testdata/shadow/root1/src/foo" ]; then
echo wrong shadowed install error: "$err"
ok=false
@@ -741,30 +966,46 @@ echo '
package foo
func F() {}
' >$d/src/x/y/foo/foo.go
+checkbar() {
+ desc="$1"
+ sleep 1
+ touch $d/src/x/y/foo/foo.go
+ if ! ./testgo build -v -i x/y/bar &> $d/err; then
+ echo build -i "$1" failed
+ cat $d/err
+ ok=false
+ elif ! grep x/y/foo $d/err >/dev/null; then
+ echo first build -i "$1" did not build x/y/foo
+ cat $d/err
+ ok=false
+ fi
+ if ! ./testgo build -v -i x/y/bar &> $d/err; then
+ echo second build -i "$1" failed
+ cat $d/err
+ ok=false
+ elif grep x/y/foo $d/err >/dev/null; then
+ echo second build -i "$1" built x/y/foo
+ cat $d/err
+ ok=false
+ fi
+}
+
echo '
package bar
import "x/y/foo"
func F() { foo.F() }
' >$d/src/x/y/bar/bar.go
-if ! ./testgo build -v -i x/y/bar &> $d/err; then
- echo build -i failed
- cat $d/err
- ok=false
-elif ! grep x/y/foo $d/err >/dev/null; then
- echo first build -i did not build x/y/foo
- cat $d/err
- ok=false
-fi
-if ! ./testgo build -v -i x/y/bar &> $d/err; then
- echo second build -i failed
- cat $d/err
- ok=false
-elif grep x/y/foo $d/err >/dev/null; then
- echo second build -i built x/y/foo
- cat $d/err
- ok=false
-fi
-rm -rf $d
+checkbar pkg
+
+TEST build -i installs dependencies for command
+echo '
+package main
+import "x/y/foo"
+func main() { foo.F() }
+' >$d/src/x/y/bar/bar.go
+checkbar cmd
+
+rm -rf $d bar
unset GOPATH
TEST 'go build in test-only directory fails with a good error'
@@ -799,13 +1040,74 @@ fi
TEST 'go test xtestonly works'
export GOPATH=$(pwd)/testdata
-./testgo clean -i xtestonly
+./testgo clean -i xtestonly || ok=false
if ! ./testgo test xtestonly >/dev/null; then
echo "go test xtestonly failed"
ok=false
fi
unset GOPATH
+TEST 'go test builds an xtest containing only non-runnable examples'
+if ! ./testgo test -v ./testdata/norunexample > testdata/std.out; then
+ echo "go test ./testdata/norunexample failed"
+ ok=false
+elif ! grep 'File with non-runnable example was built.' testdata/std.out > /dev/null; then
+ echo "file with non-runnable example was not built"
+ ok=false
+fi
+
+TEST 'go generate handles simple command'
+if ! ./testgo generate ./testdata/generate/test1.go > testdata/std.out; then
+ echo "go test ./testdata/generate/test1.go failed to run"
+ ok=false
+elif ! grep 'Success' testdata/std.out > /dev/null; then
+ echo "go test ./testdata/generate/test1.go generated wrong output"
+ ok=false
+fi
+
+TEST 'go generate handles command alias'
+if ! ./testgo generate ./testdata/generate/test2.go > testdata/std.out; then
+ echo "go test ./testdata/generate/test2.go failed to run"
+ ok=false
+elif ! grep 'Now is the time for all good men' testdata/std.out > /dev/null; then
+ echo "go test ./testdata/generate/test2.go generated wrong output"
+ ok=false
+fi
+
+TEST 'go generate variable substitution'
+if ! ./testgo generate ./testdata/generate/test3.go > testdata/std.out; then
+ echo "go test ./testdata/generate/test3.go failed to run"
+ ok=false
+elif ! grep "$GOARCH test3.go p xyzp/test3.go/123" testdata/std.out > /dev/null; then
+ echo "go test ./testdata/generate/test3.go generated wrong output"
+ ok=false
+fi
+
+TEST go get works with vanity wildcards
+d=$(mktemp -d -t testgoXXX)
+export GOPATH=$d
+if ! ./testgo get -u rsc.io/pdf/...; then
+ ok=false
+elif [ ! -x $d/bin/pdfpasswd ]; then
+ echo did not build rsc.io/pdf/pdfpasswd
+ ok=false
+fi
+unset GOPATH
+rm -rf $d
+
+TEST go vet with external tests
+d=$(mktemp -d -t testgoXXX)
+export GOPATH=$(pwd)/testdata
+if ./testgo vet vetpkg >$d/err 2>&1; then
+ echo "go vet vetpkg passes incorrectly"
+ ok=false
+elif ! grep -q 'missing argument for Printf' $d/err; then
+ echo "go vet vetpkg did not find missing argument for Printf"
+ cat $d/err
+ ok=false
+fi
+unset GOPATH
+rm -rf $d
# clean up
if $started; then stop; fi
@@ -815,6 +1117,7 @@ rm -f testgo
if $allok; then
echo PASS
else
- echo FAIL
+ echo FAIL:
+ echo "$testfail"
exit 1
fi
diff --git a/src/cmd/go/test.go b/src/cmd/go/test.go
index 5935c98db..c81e40639 100644
--- a/src/cmd/go/test.go
+++ b/src/cmd/go/test.go
@@ -6,6 +6,7 @@ package main
import (
"bytes"
+ "errors"
"fmt"
"go/ast"
"go/build"
@@ -48,7 +49,7 @@ 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".
+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.
@@ -65,16 +66,23 @@ non-test installation.
In addition to the build flags, the flags handled by 'go test' itself are:
- -c Compile the test binary to pkg.test but do not run it.
- (Where pkg is the last element of the package's import path.)
+ -c
+ Compile the test binary to pkg.test but do not run it
+ (where pkg is the last element of the package's import path).
+ The file name can be changed with the -o flag.
+
+ -exec xprog
+ Run the test binary using xprog. The behavior is the same as
+ in 'go run'. See 'go help run' for details.
-i
Install packages that are dependencies of the test.
Do not run the test.
- -exec xprog
- Run the test binary using xprog. The behavior is the same as
- in 'go run'. See 'go help run' for details.
+ -o file
+ Compile the test binary to the named file.
+ The test still runs (unless -c or -i is specified).
+
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.
@@ -122,6 +130,7 @@ control the execution of any test:
-blockprofile block.out
Write a goroutine blocking profile to the specified file
when all tests are complete.
+ Writes test binary as -c would.
-blockprofilerate n
Control the detail provided in goroutine blocking profiles by
@@ -153,8 +162,7 @@ control the execution of any test:
Sets -cover.
-coverprofile cover.out
- Write a coverage profile to the specified file after all tests
- have passed.
+ Write a coverage profile to the file after all tests have passed.
Sets -cover.
-cpu 1,2,4
@@ -164,10 +172,11 @@ control the execution of any test:
-cpuprofile cpu.out
Write a CPU profile to the specified file before exiting.
+ Writes test binary as -c would.
-memprofile mem.out
- Write a memory profile to the specified file after all tests
- have passed.
+ Write a memory profile to the file after all tests have passed.
+ Writes test binary as -c would.
-memprofilerate n
Enable more precise (and expensive) memory profiles by setting
@@ -274,10 +283,10 @@ var (
testCoverMode string // -covermode flag
testCoverPaths []string // -coverpkg flag
testCoverPkgs []*Package // -coverpkg flag
+ testO string // -o flag
testProfile bool // some profiling flag
testNeedBinary bool // profile needs to keep binary around
testV bool // -v flag
- testFiles []string // -file flag(s) TODO: not respected
testTimeout string // -timeout flag
testArgs []string
testBench bool
@@ -291,6 +300,7 @@ var testMainDeps = map[string]bool{
// Dependencies for testmain.
"testing": true,
"regexp": true,
+ "os": true,
}
func runTest(cmd *Command, args []string) {
@@ -308,6 +318,9 @@ func runTest(cmd *Command, args []string) {
if testC && len(pkgs) != 1 {
fatalf("cannot use -c flag with multiple packages")
}
+ if testO != "" && len(pkgs) != 1 {
+ fatalf("cannot use -o flag with multiple packages")
+ }
if testProfile && len(pkgs) != 1 {
fatalf("cannot use test profile flag with multiple packages")
}
@@ -522,6 +535,13 @@ func contains(x []string, s string) bool {
return false
}
+var windowsBadWords = []string{
+ "install",
+ "patch",
+ "setup",
+ "update",
+}
+
func (b *builder) test(p *Package) (buildAction, runAction, printAction *action, err error) {
if len(p.TestGoFiles)+len(p.XTestGoFiles) == 0 {
build := b.action(modeBuild, modeBuild, p)
@@ -687,7 +707,7 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,
omitDWARF: !testC && !testNeedBinary,
}
- // The generated main also imports testing and regexp.
+ // The generated main also imports testing, regexp, and os.
stk.push("testmain")
for dep := range testMainDeps {
if dep == ptest.ImportPath {
@@ -723,11 +743,13 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,
if err != nil {
return nil, nil, nil, err
}
- if t.NeedTest || ptest.coverMode != "" {
+ if len(ptest.GoFiles) > 0 {
pmain.imports = append(pmain.imports, ptest)
+ t.ImportTest = true
}
- if t.NeedXtest {
+ if pxtest != nil {
pmain.imports = append(pmain.imports, pxtest)
+ t.ImportXtest = true
}
if ptest != p && localCover {
@@ -779,17 +801,54 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,
a.objdir = testDir + string(filepath.Separator)
a.objpkg = filepath.Join(testDir, "main.a")
a.target = filepath.Join(testDir, testBinary) + exeSuffix
- pmainAction := a
+ if goos == "windows" {
+ // There are many reserved words on Windows that,
+ // if used in the name of an executable, cause Windows
+ // to try to ask for extra permissions.
+ // The word list includes setup, install, update, and patch,
+ // but it does not appear to be defined anywhere.
+ // We have run into this trying to run the
+ // go.codereview/patch tests.
+ // For package names containing those words, use test.test.exe
+ // instead of pkgname.test.exe.
+ // Note that this file name is only used in the Go command's
+ // temporary directory. If the -c or other flags are
+ // given, the code below will still use pkgname.test.exe.
+ // There are two user-visible effects of this change.
+ // First, you can actually run 'go test' in directories that
+ // have names that Windows thinks are installer-like,
+ // without getting a dialog box asking for more permissions.
+ // Second, in the Windows process listing during go test,
+ // the test shows up as test.test.exe, not pkgname.test.exe.
+ // That second one is a drawback, but it seems a small
+ // price to pay for the test running at all.
+ // If maintaining the list of bad words is too onerous,
+ // we could just do this always on Windows.
+ for _, bad := range windowsBadWords {
+ if strings.Contains(testBinary, bad) {
+ a.target = filepath.Join(testDir, "test.test") + exeSuffix
+ break
+ }
+ }
+ }
+ buildAction = a
if testC || testNeedBinary {
// -c or profiling flag: create action to copy binary to ./test.out.
- runAction = &action{
+ target := filepath.Join(cwd, testBinary+exeSuffix)
+ if testO != "" {
+ target = testO
+ if !filepath.IsAbs(target) {
+ target = filepath.Join(cwd, target)
+ }
+ }
+ buildAction = &action{
f: (*builder).install,
- deps: []*action{pmainAction},
+ deps: []*action{buildAction},
p: pmain,
- target: filepath.Join(cwd, testBinary+exeSuffix),
+ target: target,
}
- pmainAction = runAction // in case we are running the test
+ runAction = buildAction // make sure runAction != nil even if not running test
}
if testC {
printAction = &action{p: p, deps: []*action{runAction}} // nop
@@ -797,7 +856,7 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,
// run test
runAction = &action{
f: (*builder).runTest,
- deps: []*action{pmainAction},
+ deps: []*action{buildAction},
p: p,
ignoreFail: true,
}
@@ -813,7 +872,7 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,
}
}
- return pmainAction, runAction, printAction, nil
+ return buildAction, runAction, printAction, nil
}
func testImportStack(top string, p *Package, target string) []string {
@@ -1057,6 +1116,31 @@ func (b *builder) notest(a *action) error {
return nil
}
+// isTestMain tells whether fn is a TestMain(m *testing.M) function.
+func isTestMain(fn *ast.FuncDecl) bool {
+ if fn.Name.String() != "TestMain" ||
+ fn.Type.Results != nil && len(fn.Type.Results.List) > 0 ||
+ fn.Type.Params == nil ||
+ len(fn.Type.Params.List) != 1 ||
+ len(fn.Type.Params.List[0].Names) > 1 {
+ return false
+ }
+ ptr, ok := fn.Type.Params.List[0].Type.(*ast.StarExpr)
+ if !ok {
+ return false
+ }
+ // We can't easily check that the type is *testing.M
+ // because we don't know how testing has been imported,
+ // but at least check that it's *M or *something.M.
+ if name, ok := ptr.X.(*ast.Ident); ok && name.Name == "M" {
+ return true
+ }
+ if sel, ok := ptr.X.(*ast.SelectorExpr); ok && sel.Sel.Name == "M" {
+ return true
+ }
+ return false
+}
+
// isTest tells whether name looks like a test (or benchmark, according to prefix).
// It is a Test (say) if there is a character after Test that is not a lower-case letter.
// We don't want TesticularCancer.
@@ -1082,12 +1166,12 @@ func loadTestFuncs(ptest *Package) (*testFuncs, error) {
Package: ptest,
}
for _, file := range ptest.TestGoFiles {
- if err := t.load(filepath.Join(ptest.Dir, file), "_test", &t.NeedTest); err != nil {
+ if err := t.load(filepath.Join(ptest.Dir, file), "_test", &t.ImportTest, &t.NeedTest); err != nil {
return nil, err
}
}
for _, file := range ptest.XTestGoFiles {
- if err := t.load(filepath.Join(ptest.Dir, file), "_xtest", &t.NeedXtest); err != nil {
+ if err := t.load(filepath.Join(ptest.Dir, file), "_xtest", &t.ImportXtest, &t.NeedXtest); err != nil {
return nil, err
}
}
@@ -1110,13 +1194,16 @@ func writeTestmain(out string, t *testFuncs) error {
}
type testFuncs struct {
- Tests []testFunc
- Benchmarks []testFunc
- Examples []testFunc
- Package *Package
- NeedTest bool
- NeedXtest bool
- Cover []coverInfo
+ Tests []testFunc
+ Benchmarks []testFunc
+ Examples []testFunc
+ TestMain *testFunc
+ Package *Package
+ ImportTest bool
+ NeedTest bool
+ ImportXtest bool
+ NeedXtest bool
+ Cover []coverInfo
}
func (t *testFuncs) CoverMode() string {
@@ -1151,7 +1238,7 @@ type testFunc struct {
var testFileSet = token.NewFileSet()
-func (t *testFuncs) load(filename, pkg string, seen *bool) error {
+func (t *testFuncs) load(filename, pkg string, doImport, seen *bool) error {
f, err := parser.ParseFile(testFileSet, filename, nil, parser.ParseComments)
if err != nil {
return expandScanner(err)
@@ -1166,17 +1253,24 @@ func (t *testFuncs) load(filename, pkg string, seen *bool) error {
}
name := n.Name.String()
switch {
+ case isTestMain(n):
+ if t.TestMain != nil {
+ return errors.New("multiple definitions of TestMain")
+ }
+ t.TestMain = &testFunc{pkg, name, ""}
+ *doImport, *seen = true, true
case isTest(name, "Test"):
t.Tests = append(t.Tests, testFunc{pkg, name, ""})
- *seen = true
+ *doImport, *seen = true, true
case isTest(name, "Benchmark"):
t.Benchmarks = append(t.Benchmarks, testFunc{pkg, name, ""})
- *seen = true
+ *doImport, *seen = true, true
}
}
ex := doc.Examples(f)
sort.Sort(byOrder(ex))
for _, e := range ex {
+ *doImport = true // import test file whether executed or not
if e.Output == "" && !e.EmptyOutput {
// Don't run examples with no output.
continue
@@ -1197,14 +1291,17 @@ var testmainTmpl = template.Must(template.New("main").Parse(`
package main
import (
+{{if not .TestMain}}
+ "os"
+{{end}}
"regexp"
"testing"
-{{if .NeedTest}}
- _test {{.Package.ImportPath | printf "%q"}}
+{{if .ImportTest}}
+ {{if .NeedTest}}_test{{else}}_{{end}} {{.Package.ImportPath | printf "%q"}}
{{end}}
-{{if .NeedXtest}}
- _xtest {{.Package.ImportPath | printf "%s_test" | printf "%q"}}
+{{if .ImportXtest}}
+ {{if .NeedXtest}}_xtest{{else}}_{{end}} {{.Package.ImportPath | printf "%s_test" | printf "%q"}}
{{end}}
{{range $i, $p := .Cover}}
_cover{{$i}} {{$p.Package.ImportPath | printf "%q"}}
@@ -1291,7 +1388,12 @@ func main() {
CoveredPackages: {{printf "%q" .Covered}},
})
{{end}}
- testing.Main(matchString, tests, benchmarks, examples)
+ m := testing.MainStart(matchString, tests, benchmarks, examples)
+{{with .TestMain}}
+ {{.Package}}.{{.Name}}(m)
+{{else}}
+ os.Exit(m.Run())
+{{end}}
}
`))
diff --git a/src/cmd/go/testdata/generate/test1.go b/src/cmd/go/testdata/generate/test1.go
new file mode 100644
index 000000000..1f05734f0
--- /dev/null
+++ b/src/cmd/go/testdata/generate/test1.go
@@ -0,0 +1,13 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Simple test for go generate.
+
+// We include a build tag that go generate should ignore.
+
+// +build ignore
+
+//go:generate echo Success
+
+package p
diff --git a/src/cmd/go/testdata/generate/test2.go b/src/cmd/go/testdata/generate/test2.go
new file mode 100644
index 000000000..ef1a3d951
--- /dev/null
+++ b/src/cmd/go/testdata/generate/test2.go
@@ -0,0 +1,10 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Test that go generate handles command aliases.
+
+//go:generate -command run echo Now is the time
+//go:generate run for all good men
+
+package p
diff --git a/src/cmd/go/testdata/generate/test3.go b/src/cmd/go/testdata/generate/test3.go
new file mode 100644
index 000000000..41ffb7ea8
--- /dev/null
+++ b/src/cmd/go/testdata/generate/test3.go
@@ -0,0 +1,9 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Test go generate variable substitution.
+
+//go:generate echo $GOARCH $GOFILE $GOPACKAGE xyz$GOPACKAGE/$GOFILE/123
+
+package p
diff --git a/src/cmd/go/testdata/importcom/bad.go b/src/cmd/go/testdata/importcom/bad.go
new file mode 100644
index 000000000..e104c2e99
--- /dev/null
+++ b/src/cmd/go/testdata/importcom/bad.go
@@ -0,0 +1,3 @@
+package p
+
+import "bad"
diff --git a/src/cmd/go/testdata/importcom/conflict.go b/src/cmd/go/testdata/importcom/conflict.go
new file mode 100644
index 000000000..995556c51
--- /dev/null
+++ b/src/cmd/go/testdata/importcom/conflict.go
@@ -0,0 +1,3 @@
+package p
+
+import "conflict"
diff --git a/src/cmd/go/testdata/importcom/src/bad/bad.go b/src/cmd/go/testdata/importcom/src/bad/bad.go
new file mode 100644
index 000000000..bc51fd3fd
--- /dev/null
+++ b/src/cmd/go/testdata/importcom/src/bad/bad.go
@@ -0,0 +1 @@
+package bad // import
diff --git a/src/cmd/go/testdata/importcom/src/conflict/a.go b/src/cmd/go/testdata/importcom/src/conflict/a.go
new file mode 100644
index 000000000..2d6770351
--- /dev/null
+++ b/src/cmd/go/testdata/importcom/src/conflict/a.go
@@ -0,0 +1 @@
+package conflict // import "a"
diff --git a/src/cmd/go/testdata/importcom/src/conflict/b.go b/src/cmd/go/testdata/importcom/src/conflict/b.go
new file mode 100644
index 000000000..8fcfb3c8b
--- /dev/null
+++ b/src/cmd/go/testdata/importcom/src/conflict/b.go
@@ -0,0 +1 @@
+package conflict /* import "b" */
diff --git a/src/cmd/go/testdata/importcom/src/works/x/x.go b/src/cmd/go/testdata/importcom/src/works/x/x.go
new file mode 100644
index 000000000..044c6eca8
--- /dev/null
+++ b/src/cmd/go/testdata/importcom/src/works/x/x.go
@@ -0,0 +1 @@
+package x // import "works/x"
diff --git a/src/cmd/go/testdata/importcom/src/works/x/x1.go b/src/cmd/go/testdata/importcom/src/works/x/x1.go
new file mode 100644
index 000000000..2449b29df
--- /dev/null
+++ b/src/cmd/go/testdata/importcom/src/works/x/x1.go
@@ -0,0 +1 @@
+package x // important! not an import comment
diff --git a/src/cmd/go/testdata/importcom/src/wrongplace/x.go b/src/cmd/go/testdata/importcom/src/wrongplace/x.go
new file mode 100644
index 000000000..b89849da7
--- /dev/null
+++ b/src/cmd/go/testdata/importcom/src/wrongplace/x.go
@@ -0,0 +1 @@
+package x // import "my/x"
diff --git a/src/cmd/go/testdata/importcom/works.go b/src/cmd/go/testdata/importcom/works.go
new file mode 100644
index 000000000..31b55d08a
--- /dev/null
+++ b/src/cmd/go/testdata/importcom/works.go
@@ -0,0 +1,3 @@
+package p
+
+import _ "works/x"
diff --git a/src/cmd/go/testdata/importcom/wrongplace.go b/src/cmd/go/testdata/importcom/wrongplace.go
new file mode 100644
index 000000000..e2535e01a
--- /dev/null
+++ b/src/cmd/go/testdata/importcom/wrongplace.go
@@ -0,0 +1,3 @@
+package p
+
+import "wrongplace"
diff --git a/src/cmd/go/testdata/norunexample/example_test.go b/src/cmd/go/testdata/norunexample/example_test.go
new file mode 100644
index 000000000..e158305a6
--- /dev/null
+++ b/src/cmd/go/testdata/norunexample/example_test.go
@@ -0,0 +1,11 @@
+package pkg_test
+
+import "os"
+
+func init() {
+ os.Stdout.Write([]byte("File with non-runnable example was built.\n"))
+}
+
+func Example_test() {
+ // This test will not be run, it has no "Output:" comment.
+}
diff --git a/src/cmd/go/testdata/norunexample/test_test.go b/src/cmd/go/testdata/norunexample/test_test.go
new file mode 100644
index 000000000..d2e919838
--- /dev/null
+++ b/src/cmd/go/testdata/norunexample/test_test.go
@@ -0,0 +1,10 @@
+package pkg
+
+import (
+ "os"
+ "testing"
+)
+
+func TestBuilt(t *testing.T) {
+ os.Stdout.Write([]byte("A normal test was executed.\n"))
+}
diff --git a/src/cmd/go/testdata/src/badc/x.c b/src/cmd/go/testdata/src/badc/x.c
new file mode 100644
index 000000000..f6cbf6924
--- /dev/null
+++ b/src/cmd/go/testdata/src/badc/x.c
@@ -0,0 +1 @@
+// C code!
diff --git a/src/cmd/go/testdata/src/badc/x.go b/src/cmd/go/testdata/src/badc/x.go
new file mode 100644
index 000000000..bfa1de28b
--- /dev/null
+++ b/src/cmd/go/testdata/src/badc/x.go
@@ -0,0 +1 @@
+package badc
diff --git a/src/cmd/go/testdata/src/badtest/badexec/x_test.go b/src/cmd/go/testdata/src/badtest/badexec/x_test.go
new file mode 100644
index 000000000..12f505171
--- /dev/null
+++ b/src/cmd/go/testdata/src/badtest/badexec/x_test.go
@@ -0,0 +1,5 @@
+package badexec
+
+func init() {
+ panic("badexec")
+}
diff --git a/src/cmd/go/testdata/src/badtest/badsyntax/x.go b/src/cmd/go/testdata/src/badtest/badsyntax/x.go
new file mode 100644
index 000000000..c8a5407a5
--- /dev/null
+++ b/src/cmd/go/testdata/src/badtest/badsyntax/x.go
@@ -0,0 +1 @@
+package badsyntax
diff --git a/src/cmd/go/testdata/src/badtest/badsyntax/x_test.go b/src/cmd/go/testdata/src/badtest/badsyntax/x_test.go
new file mode 100644
index 000000000..5be10745d
--- /dev/null
+++ b/src/cmd/go/testdata/src/badtest/badsyntax/x_test.go
@@ -0,0 +1,3 @@
+package badsyntax
+
+func func func func func!
diff --git a/src/cmd/go/testdata/src/badtest/badvar/x.go b/src/cmd/go/testdata/src/badtest/badvar/x.go
new file mode 100644
index 000000000..fdd46c4c7
--- /dev/null
+++ b/src/cmd/go/testdata/src/badtest/badvar/x.go
@@ -0,0 +1 @@
+package badvar
diff --git a/src/cmd/go/testdata/src/badtest/badvar/x_test.go b/src/cmd/go/testdata/src/badtest/badvar/x_test.go
new file mode 100644
index 000000000..c67df01c5
--- /dev/null
+++ b/src/cmd/go/testdata/src/badtest/badvar/x_test.go
@@ -0,0 +1,5 @@
+package badvar_test
+
+func f() {
+ _ = notdefined
+}
diff --git a/src/cmd/go/testdata/src/vetpkg/a_test.go b/src/cmd/go/testdata/src/vetpkg/a_test.go
new file mode 100644
index 000000000..9b64e8e1a
--- /dev/null
+++ b/src/cmd/go/testdata/src/vetpkg/a_test.go
@@ -0,0 +1 @@
+package p_test
diff --git a/src/cmd/go/testdata/src/vetpkg/b.go b/src/cmd/go/testdata/src/vetpkg/b.go
new file mode 100644
index 000000000..99e18f63d
--- /dev/null
+++ b/src/cmd/go/testdata/src/vetpkg/b.go
@@ -0,0 +1,7 @@
+package p
+
+import "fmt"
+
+func f() {
+ fmt.Printf("%d")
+}
diff --git a/src/cmd/go/testdata/testinternal/p.go b/src/cmd/go/testdata/testinternal/p.go
new file mode 100644
index 000000000..e3558a53b
--- /dev/null
+++ b/src/cmd/go/testdata/testinternal/p.go
@@ -0,0 +1,3 @@
+package p
+
+import _ "net/http/internal"
diff --git a/src/cmd/go/testdata/testinternal2/p.go b/src/cmd/go/testdata/testinternal2/p.go
new file mode 100644
index 000000000..c594f5c5e
--- /dev/null
+++ b/src/cmd/go/testdata/testinternal2/p.go
@@ -0,0 +1,3 @@
+package p
+
+import _ "./x/y/z/internal/w"
diff --git a/src/cmd/go/testdata/testinternal2/x/y/z/internal/w/w.go b/src/cmd/go/testdata/testinternal2/x/y/z/internal/w/w.go
new file mode 100644
index 000000000..a796c0b5f
--- /dev/null
+++ b/src/cmd/go/testdata/testinternal2/x/y/z/internal/w/w.go
@@ -0,0 +1 @@
+package w
diff --git a/src/cmd/go/testflag.go b/src/cmd/go/testflag.go
index 73f311e5f..6da74b996 100644
--- a/src/cmd/go/testflag.go
+++ b/src/cmd/go/testflag.go
@@ -65,9 +65,9 @@ type testFlagSpec struct {
var testFlagDefn = []*testFlagSpec{
// local.
{name: "c", boolVar: &testC},
- {name: "file", multiOK: true},
{name: "cover", boolVar: &testCover},
{name: "coverpkg"},
+ {name: "o"},
// build flags.
{name: "a", boolVar: &buildA},
@@ -153,6 +153,9 @@ func testFlags(args []string) (packageNames, passToTest []string) {
// bool flags.
case "a", "c", "i", "n", "x", "v", "race", "cover", "work":
setBoolFlag(f.boolVar, value)
+ case "o":
+ testO = value
+ testNeedBinary = true
case "p":
setIntFlag(&buildP, value)
case "exec":
@@ -184,8 +187,6 @@ func testFlags(args []string) (packageNames, passToTest []string) {
buildContext.BuildTags = strings.Fields(value)
case "compiler":
buildCompiler{}.Set(value)
- case "file":
- testFiles = append(testFiles, value)
case "bench":
// record that we saw the flag; don't care about the value
testBench = true
diff --git a/src/cmd/go/testgo.go b/src/cmd/go/testgo.go
new file mode 100644
index 000000000..01923f74b
--- /dev/null
+++ b/src/cmd/go/testgo.go
@@ -0,0 +1,21 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file contains extra hooks for testing the go command.
+// It is compiled into the Go binary only when building the
+// test copy; it does not get compiled into the standard go
+// command, so these testing hooks are not present in the
+// go command that everyone uses.
+
+// +build testgo
+
+package main
+
+import "os"
+
+func init() {
+ if v := os.Getenv("TESTGO_IS_GO_RELEASE"); v != "" {
+ isGoRelease = v == "1"
+ }
+}
diff --git a/src/cmd/go/tool.go b/src/cmd/go/tool.go
index 6d26f7a4b..3f11c3e3d 100644
--- a/src/cmd/go/tool.go
+++ b/src/cmd/go/tool.go
@@ -47,13 +47,13 @@ const toolWindowsExtension = ".exe"
func tool(toolName string) string {
toolPath := filepath.Join(toolDir, toolName)
- if toolIsWindows && toolName != "pprof" {
+ if toolIsWindows {
toolPath += toolWindowsExtension
}
// 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)
+ fmt.Fprintf(os.Stderr, "go tool: no such tool %q; to install:\n\tgo get golang.org/x/tools/cmd/%s\n", toolName, toolName)
} else {
fmt.Fprintf(os.Stderr, "go tool: no such tool %q\n", toolName)
}
@@ -91,16 +91,6 @@ func runTool(cmd *Command, args []string) {
if toolPath == "" {
return
}
- if toolIsWindows && toolName == "pprof" {
- args = append([]string{"perl", toolPath}, args[1:]...)
- var err error
- toolPath, err = exec.LookPath("perl")
- if err != nil {
- fmt.Fprintf(os.Stderr, "go tool: perl not found\n")
- setExitStatus(3)
- return
- }
- }
if toolN {
fmt.Printf("%s %s\n", toolPath, strings.Join(args[1:], " "))
return
diff --git a/src/cmd/go/vcs.go b/src/cmd/go/vcs.go
index 8f0bae0b7..1cac61338 100644
--- a/src/cmd/go/vcs.go
+++ b/src/cmd/go/vcs.go
@@ -33,6 +33,9 @@ type vcsCmd struct {
scheme []string
pingCmd string
+
+ remoteRepo func(v *vcsCmd, rootDir string) (remoteRepo string, err error)
+ resolveRepo func(v *vcsCmd, rootDir, remoteRepo string) (realRepo string, err error)
}
// A tagCmd describes a command to list available tags
@@ -81,8 +84,17 @@ var vcsHg = &vcsCmd{
tagSyncCmd: "update -r {tag}",
tagSyncDefault: "update default",
- scheme: []string{"https", "http", "ssh"},
- pingCmd: "identify {scheme}://{repo}",
+ scheme: []string{"https", "http", "ssh"},
+ pingCmd: "identify {scheme}://{repo}",
+ remoteRepo: hgRemoteRepo,
+}
+
+func hgRemoteRepo(vcsHg *vcsCmd, rootDir string) (remoteRepo string, err error) {
+ out, err := vcsHg.runOutput(rootDir, "paths default")
+ if err != nil {
+ return "", err
+ }
+ return strings.TrimSpace(string(out)), nil
}
// vcsGit describes how to use Git.
@@ -104,8 +116,38 @@ var vcsGit = &vcsCmd{
tagSyncCmd: "checkout {tag}",
tagSyncDefault: "checkout master",
- scheme: []string{"git", "https", "http", "git+ssh"},
- pingCmd: "ls-remote {scheme}://{repo}",
+ scheme: []string{"git", "https", "http", "git+ssh"},
+ pingCmd: "ls-remote {scheme}://{repo}",
+ remoteRepo: gitRemoteRepo,
+}
+
+func gitRemoteRepo(vcsGit *vcsCmd, rootDir string) (remoteRepo string, err error) {
+ outb, err := vcsGit.runOutput(rootDir, "remote -v")
+ if err != nil {
+ return "", err
+ }
+ out := string(outb)
+
+ // Expect:
+ // origin https://github.com/rsc/pdf (fetch)
+ // origin https://github.com/rsc/pdf (push)
+ // use first line only.
+
+ if !strings.HasPrefix(out, "origin\t") {
+ return "", fmt.Errorf("unable to parse output of git remote -v")
+ }
+ out = strings.TrimPrefix(out, "origin\t")
+ i := strings.Index(out, "\n")
+ if i < 0 {
+ return "", fmt.Errorf("unable to parse output of git remote -v")
+ }
+ out = out[:i]
+ i = strings.LastIndex(out, " ")
+ if i < 0 {
+ return "", fmt.Errorf("unable to parse output of git remote -v")
+ }
+ out = out[:i]
+ return strings.TrimSpace(string(out)), nil
}
// vcsBzr describes how to use Bazaar.
@@ -123,8 +165,51 @@ var vcsBzr = &vcsCmd{
tagSyncCmd: "update -r {tag}",
tagSyncDefault: "update -r revno:-1",
- scheme: []string{"https", "http", "bzr", "bzr+ssh"},
- pingCmd: "info {scheme}://{repo}",
+ scheme: []string{"https", "http", "bzr", "bzr+ssh"},
+ pingCmd: "info {scheme}://{repo}",
+ remoteRepo: bzrRemoteRepo,
+ resolveRepo: bzrResolveRepo,
+}
+
+func bzrRemoteRepo(vcsBzr *vcsCmd, rootDir string) (remoteRepo string, err error) {
+ outb, err := vcsBzr.runOutput(rootDir, "config parent_location")
+ if err != nil {
+ return "", err
+ }
+ return strings.TrimSpace(string(outb)), nil
+}
+
+func bzrResolveRepo(vcsBzr *vcsCmd, rootDir, remoteRepo string) (realRepo string, err error) {
+ outb, err := vcsBzr.runOutput(rootDir, "info "+remoteRepo)
+ if err != nil {
+ return "", err
+ }
+ out := string(outb)
+
+ // Expect:
+ // ...
+ // (branch root|repository branch): <URL>
+ // ...
+
+ found := false
+ for _, prefix := range []string{"\n branch root: ", "\n repository branch: "} {
+ i := strings.Index(out, prefix)
+ if i >= 0 {
+ out = out[i+len(prefix):]
+ found = true
+ break
+ }
+ }
+ if !found {
+ return "", fmt.Errorf("unable to parse output of bzr info")
+ }
+
+ i := strings.Index(out, "\n")
+ if i < 0 {
+ return "", fmt.Errorf("unable to parse output of bzr info")
+ }
+ out = out[:i]
+ return strings.TrimSpace(string(out)), nil
}
// vcsSvn describes how to use Subversion.
@@ -138,8 +223,34 @@ var vcsSvn = &vcsCmd{
// There is no tag command in subversion.
// The branch information is all in the path names.
- scheme: []string{"https", "http", "svn", "svn+ssh"},
- pingCmd: "info {scheme}://{repo}",
+ scheme: []string{"https", "http", "svn", "svn+ssh"},
+ pingCmd: "info {scheme}://{repo}",
+ remoteRepo: svnRemoteRepo,
+}
+
+func svnRemoteRepo(vcsSvn *vcsCmd, rootDir string) (remoteRepo string, err error) {
+ outb, err := vcsSvn.runOutput(rootDir, "info")
+ if err != nil {
+ return "", err
+ }
+ out := string(outb)
+
+ // Expect:
+ // ...
+ // Repository Root: <URL>
+ // ...
+
+ i := strings.Index(out, "\nRepository Root: ")
+ if i < 0 {
+ return "", fmt.Errorf("unable to parse output of svn info")
+ }
+ out = out[i+len("\nRepository Root: "):]
+ i = strings.Index(out, "\n")
+ if i < 0 {
+ return "", fmt.Errorf("unable to parse output of svn info")
+ }
+ out = out[:i]
+ return strings.TrimSpace(string(out)), nil
}
func (v *vcsCmd) String() string {
@@ -361,7 +472,14 @@ var httpPrefixRE = regexp.MustCompile(`^https?:`)
func repoRootForImportPath(importPath string) (*repoRoot, error) {
rr, err := repoRootForImportPathStatic(importPath, "")
if err == errUnknownSite {
- rr, err = repoRootForImportDynamic(importPath)
+ // If there are wildcards, look up the thing before the wildcard,
+ // hoping it applies to the wildcarded parts too.
+ // This makes 'go get rsc.io/pdf/...' work in a fresh GOPATH.
+ lookup := strings.TrimSuffix(importPath, "/...")
+ if i := strings.Index(lookup, "/.../"); i >= 0 {
+ lookup = lookup[:i]
+ }
+ rr, err = repoRootForImportDynamic(lookup)
// repoRootForImportDynamic returns error detail
// that is irrelevant if the user didn't intend to use a
@@ -465,11 +583,11 @@ func repoRootForImportPathStatic(importPath, scheme string) (*repoRoot, error) {
func repoRootForImportDynamic(importPath string) (*repoRoot, error) {
slash := strings.Index(importPath, "/")
if slash < 0 {
- return nil, errors.New("import path doesn't contain a slash")
+ return nil, errors.New("import path does not contain a slash")
}
host := importPath[:slash]
if !strings.Contains(host, ".") {
- return nil, errors.New("import path doesn't contain a hostname")
+ return nil, errors.New("import path does not begin with hostname")
}
urlStr, body, err := httpsOrHTTP(importPath)
if err != nil {
@@ -613,6 +731,15 @@ var vcsPaths = []*vcsPath{
check: launchpadVCS,
},
+ // IBM DevOps Services (JazzHub)
+ {
+ prefix: "hub.jazz.net/git",
+ re: `^(?P<root>hub.jazz.net/git/[a-z0-9]+/[A-Za-z0-9_.\-]+)(/[A-Za-z0-9_.\-]+)*$`,
+ vcs: "git",
+ repo: "https://{root}",
+ check: noVCSSuffix,
+ },
+
// General syntax for any server.
{
re: `^(?P<root>(?P<repo>([a-z0-9.\-]+\.)+[a-z0-9.\-]+(:[0-9]+)?/[A-Za-z0-9_.\-/]*?)\.(?P<vcs>bzr|git|hg|svn))(/[A-Za-z0-9_.\-]+)*$`,
diff --git a/src/cmd/go/vcs_test.go b/src/cmd/go/vcs_test.go
new file mode 100644
index 000000000..14d681ba6
--- /dev/null
+++ b/src/cmd/go/vcs_test.go
@@ -0,0 +1,124 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "runtime"
+ "testing"
+)
+
+// Test that RepoRootForImportPath creates the correct RepoRoot for a given importPath.
+// TODO(cmang): Add tests for SVN and BZR.
+func TestRepoRootForImportPath(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping test to avoid external network")
+ }
+ switch runtime.GOOS {
+ case "nacl", "android":
+ t.Skipf("no networking available on %s", runtime.GOOS)
+ }
+ tests := []struct {
+ path string
+ want *repoRoot
+ }{
+ {
+ "code.google.com/p/go",
+ &repoRoot{
+ vcs: vcsHg,
+ repo: "https://code.google.com/p/go",
+ },
+ },
+ /*{
+ "code.google.com/r/go",
+ &repoRoot{
+ vcs: vcsHg,
+ repo: "https://code.google.com/r/go",
+ },
+ },*/
+ {
+ "github.com/golang/groupcache",
+ &repoRoot{
+ vcs: vcsGit,
+ repo: "https://github.com/golang/groupcache",
+ },
+ },
+ // IBM DevOps Services tests
+ {
+ "hub.jazz.net/git/user1/pkgname",
+ &repoRoot{
+ vcs: vcsGit,
+ repo: "https://hub.jazz.net/git/user1/pkgname",
+ },
+ },
+ {
+ "hub.jazz.net/git/user1/pkgname/submodule/submodule/submodule",
+ &repoRoot{
+ vcs: vcsGit,
+ repo: "https://hub.jazz.net/git/user1/pkgname",
+ },
+ },
+ {
+ "hub.jazz.net",
+ nil,
+ },
+ {
+ "hub2.jazz.net",
+ nil,
+ },
+ {
+ "hub.jazz.net/someotherprefix",
+ nil,
+ },
+ {
+ "hub.jazz.net/someotherprefix/user1/pkgname",
+ nil,
+ },
+ // Spaces are not valid in user names or package names
+ {
+ "hub.jazz.net/git/User 1/pkgname",
+ nil,
+ },
+ {
+ "hub.jazz.net/git/user1/pkg name",
+ nil,
+ },
+ // Dots are not valid in user names
+ {
+ "hub.jazz.net/git/user.1/pkgname",
+ nil,
+ },
+ {
+ "hub.jazz.net/git/user/pkg.name",
+ &repoRoot{
+ vcs: vcsGit,
+ repo: "https://hub.jazz.net/git/user/pkg.name",
+ },
+ },
+ // User names cannot have uppercase letters
+ {
+ "hub.jazz.net/git/USER/pkgname",
+ nil,
+ },
+ }
+
+ for _, test := range tests {
+ got, err := repoRootForImportPath(test.path)
+ want := test.want
+
+ if want == nil {
+ if err == nil {
+ t.Errorf("RepoRootForImport(%q): Error expected but not received", test.path)
+ }
+ continue
+ }
+ if err != nil {
+ t.Errorf("RepoRootForImport(%q): %v", test.path, err)
+ continue
+ }
+ if got.vcs.name != want.vcs.name || got.repo != want.repo {
+ t.Errorf("RepoRootForImport(%q) = VCS(%s) Repo(%s), want VCS(%s) Repo(%s)", test.path, got.vcs, got.repo, want.vcs, want.repo)
+ }
+ }
+}
diff --git a/src/cmd/go/vet.go b/src/cmd/go/vet.go
index ffb431837..02ff54b2a 100644
--- a/src/cmd/go/vet.go
+++ b/src/cmd/go/vet.go
@@ -4,6 +4,8 @@
package main
+import "path/filepath"
+
func init() {
addBuildFlagsNX(cmdVet)
}
@@ -15,7 +17,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 code.google.com/p/go.tools/cmd/vet'.
+For more about vet, see 'godoc golang.org/x/tools/cmd/vet'.
For more about specifying packages, see 'go help packages'.
To run the vet tool with specific options, run 'go tool vet'.
@@ -28,10 +30,21 @@ See also: go fmt, go fix.
}
func runVet(cmd *Command, args []string) {
- for _, pkg := range packages(args) {
- // Use pkg.gofiles instead of pkg.Dir so that
- // the command only applies to this package,
- // not to packages in subdirectories.
- run(tool("vet"), relPaths(stringList(pkg.gofiles, pkg.sfiles)))
+ for _, p := range packages(args) {
+ // Vet expects to be given a set of files all from the same package.
+ // Run once for package p and once for package p_test.
+ if len(p.GoFiles)+len(p.CgoFiles)+len(p.TestGoFiles) > 0 {
+ runVetFiles(p, stringList(p.GoFiles, p.CgoFiles, p.TestGoFiles, p.SFiles))
+ }
+ if len(p.XTestGoFiles) > 0 {
+ runVetFiles(p, stringList(p.XTestGoFiles))
+ }
+ }
+}
+
+func runVetFiles(p *Package, files []string) {
+ for i := range files {
+ files[i] = filepath.Join(p.Dir, files[i])
}
+ run(tool("vet"), relPaths(files))
}