diff --git a/src/cmd/gc/runtime.go b/src/cmd/gc/runtime.go index 2d9e423214..000b2328ff 100644 --- a/src/cmd/gc/runtime.go +++ b/src/cmd/gc/runtime.go @@ -6,6 +6,8 @@ // to update builtin.c.boot. This is not done automatically // to avoid depending on having a working compiler binary. +// +build ignore + package PACKAGE // emitted by compiler, not referred to by go programs diff --git a/src/cmd/gc/unsafe.go b/src/cmd/gc/unsafe.go index db27d7425f..c7b48a8b06 100644 --- a/src/cmd/gc/unsafe.go +++ b/src/cmd/gc/unsafe.go @@ -6,6 +6,8 @@ // to update builtin.c.boot. This is not done automatically // to avoid depending on having a working compiler binary. +// +build ignore + package PACKAGE type Pointer uintptr // not really; filled in by compiler diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go index c434dc0db5..f53efc8c1f 100644 --- a/src/cmd/go/build.go +++ b/src/cmd/go/build.go @@ -6,6 +6,7 @@ package main import ( "bytes" + "errors" "fmt" "go/build" "io" @@ -14,8 +15,8 @@ import ( "os/exec" "path/filepath" "regexp" - "runtime" "strings" + "sync" ) // Break init cycles @@ -108,7 +109,11 @@ type builder struct { goroot string // the $GOROOT goarch string // the $GOARCH goos string // the $GOOS + gobin string // the $GOBIN actionCache map[cacheKey]*action // a cache of already-constructed actions + + output sync.Mutex + scriptDir string // current directory in printed script } // An action represents a single action in the action graph. @@ -148,9 +153,10 @@ func (b *builder) init(aflag, nflag, xflag bool) { b.nflag = nflag b.xflag = xflag b.actionCache = make(map[cacheKey]*action) - b.goroot = runtime.GOROOT() b.goarch = build.DefaultContext.GOARCH b.goos = build.DefaultContext.GOOS + b.goroot = build.Path[0].Path + b.gobin = build.Path[0].BinDir() b.arch, err = build.ArchChar(b.goarch) if err != nil { @@ -312,7 +318,11 @@ func (b *builder) do(a *action) { } if a.f != nil { if err := a.f(b, a); err != nil { - errorf("%s", err) + if err == errPrintedOutput { + exitStatus = 2 + } else { + errorf("%s", err) + } a.failed = true } } @@ -321,6 +331,14 @@ func (b *builder) do(a *action) { // build is the action for building a single package or command. func (b *builder) build(a *action) error { + if b.nflag { + // In -n mode, print a banner between packages. + // The banner is five lines so that when changes to + // different sections of the bootstrap script have to + // be merged, the banners give patch something + // to use to find its context. + fmt.Printf("\n#\n# %s\n#\n\n", a.p.ImportPath) + } obj := filepath.Join(b.work, filepath.FromSlash(a.p.ImportPath+"/_obj")) + string(filepath.Separator) if a.pkgobj == "" { a.pkgobj = filepath.Join(b.work, filepath.FromSlash(a.p.ImportPath+".a")) @@ -362,7 +380,7 @@ func (b *builder) build(a *action) error { sfiles = nil } - outGo, outObj, err := b.cgo(a.p.Dir, obj, gccfiles, a.p) + outGo, outObj, err := b.cgo(a.p, obj, gccfiles) if err != nil { return err } @@ -405,7 +423,7 @@ func (b *builder) build(a *action) error { // additional reflect type data. gcargs = append(gcargs, "-+") } - if err := b.gc(a.p.Dir, obj+out, gcargs, inc, gofiles); err != nil { + if err := b.gc(a.p, obj+out, gcargs, inc, gofiles); err != nil { return err } objects = append(objects, out) @@ -439,7 +457,7 @@ func (b *builder) build(a *action) error { for _, file := range cfiles { out := file[:len(file)-len(".c")] + "." + b.arch - if err := b.cc(a.p.Dir, obj, obj+out, file); err != nil { + if err := b.cc(a.p, obj, obj+out, file); err != nil { return err } objects = append(objects, out) @@ -448,14 +466,14 @@ func (b *builder) build(a *action) error { // assemble .s files for _, file := range sfiles { out := file[:len(file)-len(".s")] + "." + b.arch - if err := b.asm(a.p.Dir, obj, obj+out, file); err != nil { + if err := b.asm(a.p, obj, obj+out, file); err != nil { return err } objects = append(objects, out) } // pack into archive - if err := b.gopack(obj, a.pkgobj, objects); err != nil { + if err := b.gopack(a.p, obj, a.pkgobj, objects); err != nil { return err } @@ -467,7 +485,7 @@ func (b *builder) build(a *action) error { inc[i] = "-L" } a.pkgbin = obj + "a.out" - if err := b.ld(a.p.Dir, a.pkgbin, inc, a.pkgobj); err != nil { + if err := b.ld(a.p, a.pkgbin, inc, a.pkgobj); err != nil { return err } } @@ -503,7 +521,7 @@ func (b *builder) install(a *action) error { // copyFile is like 'cp src dst'. func (b *builder) copyFile(dst, src string, perm uint32) error { if b.nflag || b.xflag { - b.showcmd("cp %s %s", src, dst) + b.showcmd("", "cp %s %s", src, dst) if b.nflag { return nil } @@ -528,27 +546,91 @@ func (b *builder) copyFile(dst, src string, perm uint32) error { return nil } -// fmtcmd is like fmt.Sprintf but replaces references to the -// work directory (a temporary directory with a clumsy name) -// with $WORK. -func (b *builder) fmtcmd(format string, args ...interface{}) string { - s := fmt.Sprintf(format, args...) - s = strings.Replace(s, b.work, "$WORK", -1) - return s +// fmtcmd formats a command in the manner of fmt.Sprintf but also: +// +// If dir is non-empty and the script is not in dir right now, +// fmtcmd inserts "cd dir\n" before the command. +// +// fmtcmd replaces the value of b.work with $WORK. +// fmtcmd replaces the value of b.goroot with $GOROOT. +// fmtcmd replaces the value of b.gobin with $GOBIN. +// +// fmtcmd replaces the name of the current directory with dot (.) +// but only when it is at the beginning of a space-separated token. +// +func (b *builder) fmtcmd(dir string, format string, args ...interface{}) string { + cmd := fmt.Sprintf(format, args...) + if dir != "" { + cmd = strings.Replace(" "+cmd, " "+dir, " .", -1)[1:] + if b.scriptDir != dir { + b.scriptDir = dir + cmd = " cd " + dir + "\n" + cmd + } + } + cmd = strings.Replace(cmd, b.work, "$WORK", -1) + cmd = strings.Replace(cmd, b.gobin, "$GOBIN", -1) + cmd = strings.Replace(cmd, b.goroot, "$GOROOT", -1) + return cmd } // showcmd prints the given command to standard output // for the implementation of -n or -x. -func (b *builder) showcmd(format string, args ...interface{}) { - fmt.Println(b.fmtcmd(format, args...)) +func (b *builder) showcmd(dir string, format string, args ...interface{}) { + b.output.Lock() + defer b.output.Unlock() + fmt.Println(b.fmtcmd(dir, format, args...)) } +// showOutput prints "# desc" followed by the given output. +// The output is expected to contain references to 'dir', usually +// the source directory for the package that has failed to build. +// showOutput rewrites mentions of dir with a relative path to dir. +// This is usually shorter and more pleasant than the absolute path. +// For example, if fmt doesn't compile and we are in src/pkg/html, +// the output is +// +// $ go build +// # fmt +// ../fmt/print.go:1090: undefined: asdf +// $ +// +// instead of +// +// $ go build +// # fmt +// /usr/gopher/go/src/pkg/fmt/print.go:1090: undefined: asdf +// $ +// +// showOutput also replaces references to the work directory with $WORK. +// +func (b *builder) showOutput(dir, desc, out string) { + prefix := "# " + desc + suffix := "\n" + out + pwd, _ := os.Getwd() + if reldir, err := filepath.Rel(pwd, dir); err == nil { + suffix = strings.Replace(suffix, " "+dir, " "+reldir, -1) + suffix = strings.Replace(suffix, "\n"+dir, "\n"+reldir, -1) + } + suffix = strings.Replace(suffix, " "+b.work, " $WORK", -1) + + b.output.Lock() + defer b.output.Unlock() + fmt.Print(prefix, suffix) +} + +// errPrintedOutput is a special error indicating that a command failed +// but that it generated output as well, and that output has already +// been printed, so there's no point showing 'exit status 1' or whatever +// the wait status was. The main executor, builder.do, knows not to +// print this error. +var errPrintedOutput = errors.New("already printed output - no need to show error") + // run runs the command given by cmdline in the directory dir. // If the commnd fails, run prints information about the failure // and returns a non-nil error. -func (b *builder) run(dir string, cmdline ...string) error { +func (b *builder) run(dir string, desc string, cmdline ...string) error { if b.nflag || b.xflag { - b.showcmd("cd %s; %s", dir, strings.Join(cmdline, " ")) + b.showcmd(dir, "%s", strings.Join(cmdline, " ")) if b.nflag { return nil } @@ -562,8 +644,17 @@ func (b *builder) run(dir string, cmdline ...string) error { // TODO: cmd.Env err := cmd.Run() if buf.Len() > 0 { - fmt.Fprintf(os.Stderr, "# cd %s; %s\n", dir, strings.Join(cmdline, " ")) - fmt.Fprintf(os.Stderr, "%s\n", buf.Bytes()) + out := buf.Bytes() + if out[len(out)-1] != '\n' { + out = append(out, '\n') + } + if desc == "" { + desc = b.fmtcmd(dir, "%s", strings.Join(cmdline, " ")) + } + b.showOutput(dir, desc, string(out)) + if err != nil { + err = errPrintedOutput + } } return err } @@ -571,7 +662,7 @@ func (b *builder) run(dir string, cmdline ...string) error { // mkdir makes the named directory. func (b *builder) mkdir(dir string) error { if b.nflag || b.xflag { - b.showcmd("mkdir -p %s", dir) + b.showcmd("", "mkdir -p %s", dir) if b.nflag { return nil } @@ -583,50 +674,75 @@ func (b *builder) mkdir(dir string) error { return nil } +// mkAbs returns an absolute path corresponding to +// evaluating f in the directory dir. +// We always pass absolute paths of source files so that +// the error messages will include the full path to a file +// in need of attention. +func mkAbs(dir, f string) string { + // Leave absolute paths alone. + // Also, during -n mode we use the pseudo-directory $WORK + // instead of creating an actual work directory that won't be used. + // Leave paths beginning with $WORK alone too. + if filepath.IsAbs(f) || strings.HasPrefix(f, "$WORK") { + return f + } + return filepath.Join(dir, f) +} + // gc runs the Go compiler in a specific directory on a set of files // to generate the named output file. -func (b *builder) gc(dir, ofile string, gcargs, importArgs []string, gofiles []string) error { +func (b *builder) gc(p *Package, ofile string, gcargs, importArgs []string, gofiles []string) error { args := []string{b.arch + "g", "-o", ofile} args = append(args, gcargs...) args = append(args, importArgs...) - args = append(args, gofiles...) - return b.run(dir, args...) + for _, f := range gofiles { + args = append(args, mkAbs(p.Dir, f)) + } + return b.run(p.Dir, p.ImportPath, args...) } // asm runs the assembler in a specific directory on a specific file // to generate the named output file. -func (b *builder) asm(dir, obj, ofile, sfile string) error { - return b.run(dir, b.arch+"a", "-I", obj, "-o", ofile, "-DGOOS_"+b.goos, "-DGOARCH_"+b.goarch, sfile) +func (b *builder) asm(p *Package, obj, ofile, sfile string) error { + sfile = mkAbs(p.Dir, sfile) + return b.run(p.Dir, p.ImportPath, b.arch+"a", "-I", obj, "-o", ofile, "-DGOOS_"+b.goos, "-DGOARCH_"+b.goarch, sfile) } // gopack runs the assembler in a specific directory to create // an archive from a set of object files. // typically it is run in the object directory. -func (b *builder) gopack(objDir, afile string, ofiles []string) error { - return b.run(objDir, append([]string{"gopack", "grc", afile}, ofiles...)...) +func (b *builder) gopack(p *Package, objDir, afile string, ofiles []string) error { + cmd := []string{"gopack", "grc"} + cmd = append(cmd, mkAbs(objDir, afile)) + for _, f := range ofiles { + cmd = append(cmd, mkAbs(objDir, f)) + } + return b.run(p.Dir, p.ImportPath, cmd...) } // ld runs the linker to create a package starting at mainpkg. -func (b *builder) ld(dir, out string, importArgs []string, mainpkg string) error { - return b.run(dir, append(append([]string{b.arch + "l", "-o", out}, importArgs...), mainpkg)...) +func (b *builder) ld(p *Package, out string, importArgs []string, mainpkg string) error { + return b.run(p.Dir, p.ImportPath, append(append([]string{b.arch + "l", "-o", out}, importArgs...), mainpkg)...) } // cc runs the gc-toolchain C compiler in a directory on a C file // to produce an output file. -func (b *builder) cc(dir, objdir, ofile, cfile string) error { - inc := filepath.Join(runtime.GOROOT(), "pkg", - fmt.Sprintf("%s_%s", b.goos, b.goarch)) - return b.run(dir, b.arch+"c", "-FVw", "-I", objdir, "-I", inc, "-o", ofile, "-DGOOS_"+b.goos, "-DGOARCH_"+b.goarch, cfile) +func (b *builder) cc(p *Package, objdir, ofile, cfile string) error { + inc := filepath.Join(b.goroot, "pkg", fmt.Sprintf("%s_%s", b.goos, b.goarch)) + cfile = mkAbs(p.Dir, cfile) + return b.run(p.Dir, p.ImportPath, b.arch+"c", "-FVw", "-I", objdir, "-I", inc, "-o", ofile, "-DGOOS_"+b.goos, "-DGOARCH_"+b.goarch, cfile) } // gcc runs the gcc C compiler to create an object from a single C file. -func (b *builder) gcc(dir, out string, flags []string, cfile string) error { - return b.run(dir, b.gccCmd(dir, flags, "-o", out, "-c", cfile)...) +func (b *builder) gcc(p *Package, out string, flags []string, cfile string) error { + cfile = mkAbs(p.Dir, cfile) + return b.run(p.Dir, p.ImportPath, b.gccCmd(p.Dir, flags, "-o", out, "-c", cfile)...) } // gccld runs the gcc linker to create an executable from a set of object files -func (b *builder) gccld(dir, out string, flags []string, obj []string) error { - return b.run(dir, append(b.gccCmd(dir, flags, "-o", out), obj...)...) +func (b *builder) gccld(p *Package, out string, flags []string, obj []string) error { + return b.run(p.Dir, p.ImportPath, append(b.gccCmd(p.Dir, flags, "-o", out), obj...)...) } // gccCmd returns a gcc command line ending with args @@ -645,7 +761,7 @@ func (b *builder) gccCmd(objdir string, flags []string, args ...string) []string var cgoRe = regexp.MustCompile(`[/\\:]`) -func (b *builder) cgo(dir, obj string, csfiles []string, p *Package) (outGo, outObj []string, err error) { +func (b *builder) cgo(p *Package, obj string, gccfiles []string) (outGo, outObj []string, err error) { // cgo // TODO: CGOPKGPATH, CGO_FLAGS? gofiles := []string{obj + "_cgo_gotypes.go"} @@ -664,14 +780,14 @@ func (b *builder) cgo(dir, obj string, csfiles []string, p *Package) (outGo, out } cgoArgs = append(cgoArgs, "--") cgoArgs = append(cgoArgs, p.CgoFiles...) - if err := b.run(dir, cgoArgs...); err != nil { + if err := b.run(p.Dir, p.ImportPath, cgoArgs...); err != nil { return nil, nil, err } outGo = append(outGo, gofiles...) // cc _cgo_defun.c defunObj := obj + "_cgo_defun." + b.arch - if err := b.cc(dir, obj, defunObj, defunC); err != nil { + if err := b.cc(p, obj, defunObj, defunC); err != nil { return nil, nil, err } outObj = append(outObj, defunObj) @@ -680,7 +796,7 @@ func (b *builder) cgo(dir, obj string, csfiles []string, p *Package) (outGo, out var linkobj []string for _, cfile := range cfiles { ofile := obj + cfile[:len(cfile)-1] + "o" - if err := b.gcc(dir, ofile, p.info.CgoCFLAGS, obj+cfile); err != nil { + if err := b.gcc(p, ofile, p.info.CgoCFLAGS, obj+cfile); err != nil { return nil, nil, err } linkobj = append(linkobj, ofile) @@ -688,28 +804,28 @@ func (b *builder) cgo(dir, obj string, csfiles []string, p *Package) (outGo, out outObj = append(outObj, ofile) } } - for _, file := range csfiles { + for _, file := range gccfiles { ofile := obj + cgoRe.ReplaceAllString(file[:len(file)-1], "_") + "o" - if err := b.gcc(dir, ofile, p.info.CgoCFLAGS, file); err != nil { + if err := b.gcc(p, ofile, p.info.CgoCFLAGS, file); err != nil { return nil, nil, err } linkobj = append(linkobj, ofile) outObj = append(outObj, ofile) } dynobj := obj + "_cgo_.o" - if err := b.gccld(dir, dynobj, p.info.CgoLDFLAGS, linkobj); err != nil { + if err := b.gccld(p, dynobj, p.info.CgoLDFLAGS, linkobj); err != nil { return nil, nil, err } // cgo -dynimport importC := obj + "_cgo_import.c" - if err := b.run(dir, "cgo", "-objdir", obj, "-dynimport", dynobj, "-dynout", importC); err != nil { + if err := b.run(p.Dir, p.ImportPath, "cgo", "-objdir", obj, "-dynimport", dynobj, "-dynout", importC); err != nil { return nil, nil, err } // cc _cgo_import.ARCH importObj := obj + "_cgo_import." + b.arch - if err := b.cc(dir, obj, importObj, importC); err != nil { + if err := b.cc(p, obj, importObj, importC); err != nil { return nil, nil, err } outObj = append(outObj, importObj) diff --git a/src/cmd/go/list.go b/src/cmd/go/list.go index bd75fd6cc4..df18df17a7 100644 --- a/src/cmd/go/list.go +++ b/src/cmd/go/list.go @@ -14,7 +14,7 @@ var cmdList = &Command{ UsageLine: "list [-f format] [-json] [importpath...]", Short: "list packages", Long: ` -List lists the packages named by the import paths. +List lists the packages named by the import paths, one per line. The default output shows the package name and file system location: @@ -24,7 +24,7 @@ The default output shows the package name and file system location: The -f flag specifies an alternate format for the list, using the syntax of package template. The default output -is equivalent to -f '{{.Name}} {{.Dir}}'. The struct +is equivalent to -f '{{.ImportPath}}'. The struct being passed to the template is: type Package struct { @@ -57,7 +57,7 @@ func init() { cmdList.Run = runList // break init cycle } -var listFmt = cmdList.Flag.String("f", "{{.Name}} {{.Dir}}", "") +var listFmt = cmdList.Flag.String("f", "{{.ImportPath}}", "") var listJson = cmdList.Flag.Bool("json", false, "") var nl = []byte{'\n'} diff --git a/src/cmd/go/main.go b/src/cmd/go/main.go index 79bcd66746..6d70da824f 100644 --- a/src/cmd/go/main.go +++ b/src/cmd/go/main.go @@ -237,8 +237,38 @@ func run(cmdline ...string) { // allPackages returns all the packages that can be found // under the $GOPATH directories and $GOROOT. func allPackages() []string { - have := make(map[string]bool) + have := map[string]bool{ + "builtin": true, // ignore pseudo-package that exists only for documentation + } var pkgs []string + + // Commands + goroot := build.Path[0].Path + cmd := filepath.Join(goroot, "src/cmd") + string(filepath.Separator) + filepath.Walk(cmd, func(path string, fi os.FileInfo, err error) error { + if err != nil || !fi.IsDir() { + return nil + } + name := path[len(cmd):] + // Commands are all in cmd/, not in subdirectories. + if strings.Contains(name, string(filepath.Separator)) { + return filepath.SkipDir + } + + _, err = build.ScanDir(path) + if err != nil { + return nil + } + + // We use, e.g., cmd/gofmt as the pseudo import path for gofmt. + name = "cmd/" + name + if !have[name] { + have[name] = true + pkgs = append(pkgs, name) + } + return nil + }) + for _, t := range build.Path { src := t.SrcDir() + string(filepath.Separator) filepath.Walk(src, func(path string, fi os.FileInfo, err error) error { @@ -256,21 +286,19 @@ func allPackages() []string { return nil } name := path[len(src):] - if have[name] { - return nil + if !have[name] { + pkgs = append(pkgs, name) + have[name] = true } - pkgs = append(pkgs, name) - have[name] = true // Avoid go/build test data. + // TODO: Move it into a testdata directory. if path == filepath.Join(build.Path[0].SrcDir(), "go/build") { return filepath.SkipDir } return nil }) - - // TODO: Commands. } return pkgs } diff --git a/src/cmd/go/pkg.go b/src/cmd/go/pkg.go index 503b098ce8..599c4e0779 100644 --- a/src/cmd/go/pkg.go +++ b/src/cmd/go/pkg.go @@ -70,13 +70,15 @@ func loadPackage(arg string) (*Package, error) { // Find basic information about package path. t, importPath, err := build.FindTree(arg) + dir := "" // Maybe it is a standard command. - if err != nil && !filepath.IsAbs(arg) && !strings.HasPrefix(arg, ".") { + if err != nil && !filepath.IsAbs(arg) && strings.HasPrefix(arg, "cmd/") { goroot := build.Path[0] - p := filepath.Join(goroot.Path, "src/cmd", arg) + p := filepath.Join(goroot.Path, "src", arg) if st, err1 := os.Stat(p); err1 == nil && st.IsDir() { t = goroot - importPath = "../cmd/" + arg + importPath = arg + dir = p err = nil } } @@ -84,7 +86,9 @@ func loadPackage(arg string) (*Package, error) { return nil, err } - dir := filepath.Join(t.SrcDir(), filepath.FromSlash(importPath)) + if dir == "" { + dir = filepath.Join(t.SrcDir(), filepath.FromSlash(importPath)) + } // Maybe we know the package by its directory. if p := packageCache[dir]; p != nil { @@ -140,6 +144,13 @@ func scanPackage(ctxt *build.Context, t *build.Tree, arg, importPath, dir string } sort.Strings(p.gofiles) + // Packages that use cgo import runtime/cgo implicitly, + // except runtime/cgo itself. + if len(info.CgoFiles) > 0 && (!p.Standard || p.ImportPath != "runtime/cgo") { + p.Imports = append(p.Imports, "runtime/cgo") + sort.Strings(p.Imports) + } + // Record package under both import path and full directory name. packageCache[dir] = p packageCache[importPath] = p