1
0
mirror of https://github.com/golang/go synced 2024-11-25 11:07:59 -07:00

cmd/go: work toward build script

The commands in the standard tree are now named
by the pseudo-import paths cmd/gofmt etc.
This avoids ambiguity between cmd/go's directory
and go/token's parent directory.

R=golang-dev, r
CC=golang-dev
https://golang.org/cl/5503050
This commit is contained in:
Russ Cox 2011-12-20 16:42:44 -05:00
parent 8720105776
commit fd1c1b9679
6 changed files with 223 additions and 64 deletions

View File

@ -6,6 +6,8 @@
// to update builtin.c.boot. This is not done automatically // to update builtin.c.boot. This is not done automatically
// to avoid depending on having a working compiler binary. // to avoid depending on having a working compiler binary.
// +build ignore
package PACKAGE package PACKAGE
// emitted by compiler, not referred to by go programs // emitted by compiler, not referred to by go programs

View File

@ -6,6 +6,8 @@
// to update builtin.c.boot. This is not done automatically // to update builtin.c.boot. This is not done automatically
// to avoid depending on having a working compiler binary. // to avoid depending on having a working compiler binary.
// +build ignore
package PACKAGE package PACKAGE
type Pointer uintptr // not really; filled in by compiler type Pointer uintptr // not really; filled in by compiler

View File

@ -6,6 +6,7 @@ package main
import ( import (
"bytes" "bytes"
"errors"
"fmt" "fmt"
"go/build" "go/build"
"io" "io"
@ -14,8 +15,8 @@ import (
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"regexp" "regexp"
"runtime"
"strings" "strings"
"sync"
) )
// Break init cycles // Break init cycles
@ -108,7 +109,11 @@ type builder struct {
goroot string // the $GOROOT goroot string // the $GOROOT
goarch string // the $GOARCH goarch string // the $GOARCH
goos string // the $GOOS goos string // the $GOOS
gobin string // the $GOBIN
actionCache map[cacheKey]*action // a cache of already-constructed actions 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. // 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.nflag = nflag
b.xflag = xflag b.xflag = xflag
b.actionCache = make(map[cacheKey]*action) b.actionCache = make(map[cacheKey]*action)
b.goroot = runtime.GOROOT()
b.goarch = build.DefaultContext.GOARCH b.goarch = build.DefaultContext.GOARCH
b.goos = build.DefaultContext.GOOS b.goos = build.DefaultContext.GOOS
b.goroot = build.Path[0].Path
b.gobin = build.Path[0].BinDir()
b.arch, err = build.ArchChar(b.goarch) b.arch, err = build.ArchChar(b.goarch)
if err != nil { if err != nil {
@ -312,7 +318,11 @@ func (b *builder) do(a *action) {
} }
if a.f != nil { if a.f != nil {
if err := a.f(b, a); err != nil { if err := a.f(b, a); err != nil {
errorf("%s", err) if err == errPrintedOutput {
exitStatus = 2
} else {
errorf("%s", err)
}
a.failed = true a.failed = true
} }
} }
@ -321,6 +331,14 @@ func (b *builder) do(a *action) {
// build is the action for building a single package or command. // build is the action for building a single package or command.
func (b *builder) build(a *action) error { 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) obj := filepath.Join(b.work, filepath.FromSlash(a.p.ImportPath+"/_obj")) + string(filepath.Separator)
if a.pkgobj == "" { if a.pkgobj == "" {
a.pkgobj = filepath.Join(b.work, filepath.FromSlash(a.p.ImportPath+".a")) 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 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 { if err != nil {
return err return err
} }
@ -405,7 +423,7 @@ func (b *builder) build(a *action) error {
// additional reflect type data. // additional reflect type data.
gcargs = append(gcargs, "-+") 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 return err
} }
objects = append(objects, out) objects = append(objects, out)
@ -439,7 +457,7 @@ func (b *builder) build(a *action) error {
for _, file := range cfiles { for _, file := range cfiles {
out := file[:len(file)-len(".c")] + "." + b.arch 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 return err
} }
objects = append(objects, out) objects = append(objects, out)
@ -448,14 +466,14 @@ func (b *builder) build(a *action) error {
// assemble .s files // assemble .s files
for _, file := range sfiles { for _, file := range sfiles {
out := file[:len(file)-len(".s")] + "." + b.arch 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 return err
} }
objects = append(objects, out) objects = append(objects, out)
} }
// pack into archive // 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 return err
} }
@ -467,7 +485,7 @@ func (b *builder) build(a *action) error {
inc[i] = "-L" inc[i] = "-L"
} }
a.pkgbin = obj + "a.out" 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 return err
} }
} }
@ -503,7 +521,7 @@ func (b *builder) install(a *action) error {
// copyFile is like 'cp src dst'. // copyFile is like 'cp src dst'.
func (b *builder) copyFile(dst, src string, perm uint32) error { func (b *builder) copyFile(dst, src string, perm uint32) error {
if b.nflag || b.xflag { if b.nflag || b.xflag {
b.showcmd("cp %s %s", src, dst) b.showcmd("", "cp %s %s", src, dst)
if b.nflag { if b.nflag {
return nil return nil
} }
@ -528,27 +546,91 @@ func (b *builder) copyFile(dst, src string, perm uint32) error {
return nil return nil
} }
// fmtcmd is like fmt.Sprintf but replaces references to the // fmtcmd formats a command in the manner of fmt.Sprintf but also:
// work directory (a temporary directory with a clumsy name) //
// with $WORK. // If dir is non-empty and the script is not in dir right now,
func (b *builder) fmtcmd(format string, args ...interface{}) string { // fmtcmd inserts "cd dir\n" before the command.
s := fmt.Sprintf(format, args...) //
s = strings.Replace(s, b.work, "$WORK", -1) // fmtcmd replaces the value of b.work with $WORK.
return s // 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 // showcmd prints the given command to standard output
// for the implementation of -n or -x. // for the implementation of -n or -x.
func (b *builder) showcmd(format string, args ...interface{}) { func (b *builder) showcmd(dir string, format string, args ...interface{}) {
fmt.Println(b.fmtcmd(format, args...)) 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. // run runs the command given by cmdline in the directory dir.
// If the commnd fails, run prints information about the failure // If the commnd fails, run prints information about the failure
// and returns a non-nil error. // 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 { if b.nflag || b.xflag {
b.showcmd("cd %s; %s", dir, strings.Join(cmdline, " ")) b.showcmd(dir, "%s", strings.Join(cmdline, " "))
if b.nflag { if b.nflag {
return nil return nil
} }
@ -562,8 +644,17 @@ func (b *builder) run(dir string, cmdline ...string) error {
// TODO: cmd.Env // TODO: cmd.Env
err := cmd.Run() err := cmd.Run()
if buf.Len() > 0 { if buf.Len() > 0 {
fmt.Fprintf(os.Stderr, "# cd %s; %s\n", dir, strings.Join(cmdline, " ")) out := buf.Bytes()
fmt.Fprintf(os.Stderr, "%s\n", 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 return err
} }
@ -571,7 +662,7 @@ func (b *builder) run(dir string, cmdline ...string) error {
// mkdir makes the named directory. // mkdir makes the named directory.
func (b *builder) mkdir(dir string) error { func (b *builder) mkdir(dir string) error {
if b.nflag || b.xflag { if b.nflag || b.xflag {
b.showcmd("mkdir -p %s", dir) b.showcmd("", "mkdir -p %s", dir)
if b.nflag { if b.nflag {
return nil return nil
} }
@ -583,50 +674,75 @@ func (b *builder) mkdir(dir string) error {
return nil 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 // gc runs the Go compiler in a specific directory on a set of files
// to generate the named output file. // 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 := []string{b.arch + "g", "-o", ofile}
args = append(args, gcargs...) args = append(args, gcargs...)
args = append(args, importArgs...) args = append(args, importArgs...)
args = append(args, gofiles...) for _, f := range gofiles {
return b.run(dir, args...) 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 // asm runs the assembler in a specific directory on a specific file
// to generate the named output file. // to generate the named output file.
func (b *builder) asm(dir, obj, ofile, sfile string) error { func (b *builder) asm(p *Package, obj, ofile, sfile string) error {
return b.run(dir, b.arch+"a", "-I", obj, "-o", ofile, "-DGOOS_"+b.goos, "-DGOARCH_"+b.goarch, sfile) 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 // gopack runs the assembler in a specific directory to create
// an archive from a set of object files. // an archive from a set of object files.
// typically it is run in the object directory. // typically it is run in the object directory.
func (b *builder) gopack(objDir, afile string, ofiles []string) error { func (b *builder) gopack(p *Package, objDir, afile string, ofiles []string) error {
return b.run(objDir, append([]string{"gopack", "grc", afile}, ofiles...)...) 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. // ld runs the linker to create a package starting at mainpkg.
func (b *builder) ld(dir, out string, importArgs []string, mainpkg string) error { func (b *builder) ld(p *Package, out string, importArgs []string, mainpkg string) error {
return b.run(dir, append(append([]string{b.arch + "l", "-o", out}, importArgs...), mainpkg)...) 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 // cc runs the gc-toolchain C compiler in a directory on a C file
// to produce an output file. // to produce an output file.
func (b *builder) cc(dir, objdir, ofile, cfile string) error { func (b *builder) cc(p *Package, objdir, ofile, cfile string) error {
inc := filepath.Join(runtime.GOROOT(), "pkg", inc := filepath.Join(b.goroot, "pkg", fmt.Sprintf("%s_%s", b.goos, b.goarch))
fmt.Sprintf("%s_%s", b.goos, b.goarch)) cfile = mkAbs(p.Dir, cfile)
return b.run(dir, b.arch+"c", "-FVw", "-I", objdir, "-I", inc, "-o", ofile, "-DGOOS_"+b.goos, "-DGOARCH_"+b.goarch, 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. // 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 { func (b *builder) gcc(p *Package, out string, flags []string, cfile string) error {
return b.run(dir, b.gccCmd(dir, flags, "-o", out, "-c", cfile)...) 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 // 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 { func (b *builder) gccld(p *Package, out string, flags []string, obj []string) error {
return b.run(dir, append(b.gccCmd(dir, flags, "-o", out), obj...)...) 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 // 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(`[/\\:]`) 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 // cgo
// TODO: CGOPKGPATH, CGO_FLAGS? // TODO: CGOPKGPATH, CGO_FLAGS?
gofiles := []string{obj + "_cgo_gotypes.go"} 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, "--")
cgoArgs = append(cgoArgs, p.CgoFiles...) 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 return nil, nil, err
} }
outGo = append(outGo, gofiles...) outGo = append(outGo, gofiles...)
// cc _cgo_defun.c // cc _cgo_defun.c
defunObj := obj + "_cgo_defun." + b.arch 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 return nil, nil, err
} }
outObj = append(outObj, defunObj) outObj = append(outObj, defunObj)
@ -680,7 +796,7 @@ func (b *builder) cgo(dir, obj string, csfiles []string, p *Package) (outGo, out
var linkobj []string var linkobj []string
for _, cfile := range cfiles { for _, cfile := range cfiles {
ofile := obj + cfile[:len(cfile)-1] + "o" 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 return nil, nil, err
} }
linkobj = append(linkobj, ofile) 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) outObj = append(outObj, ofile)
} }
} }
for _, file := range csfiles { for _, file := range gccfiles {
ofile := obj + cgoRe.ReplaceAllString(file[:len(file)-1], "_") + "o" 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 return nil, nil, err
} }
linkobj = append(linkobj, ofile) linkobj = append(linkobj, ofile)
outObj = append(outObj, ofile) outObj = append(outObj, ofile)
} }
dynobj := obj + "_cgo_.o" 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 return nil, nil, err
} }
// cgo -dynimport // cgo -dynimport
importC := obj + "_cgo_import.c" 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 return nil, nil, err
} }
// cc _cgo_import.ARCH // cc _cgo_import.ARCH
importObj := obj + "_cgo_import." + b.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 return nil, nil, err
} }
outObj = append(outObj, importObj) outObj = append(outObj, importObj)

View File

@ -14,7 +14,7 @@ var cmdList = &Command{
UsageLine: "list [-f format] [-json] [importpath...]", UsageLine: "list [-f format] [-json] [importpath...]",
Short: "list packages", Short: "list packages",
Long: ` 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: 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, The -f flag specifies an alternate format for the list,
using the syntax of package template. The default output 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: being passed to the template is:
type Package struct { type Package struct {
@ -57,7 +57,7 @@ func init() {
cmdList.Run = runList // break init cycle 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 listJson = cmdList.Flag.Bool("json", false, "")
var nl = []byte{'\n'} var nl = []byte{'\n'}

View File

@ -237,8 +237,38 @@ func run(cmdline ...string) {
// allPackages returns all the packages that can be found // allPackages returns all the packages that can be found
// under the $GOPATH directories and $GOROOT. // under the $GOPATH directories and $GOROOT.
func allPackages() []string { 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 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 { for _, t := range build.Path {
src := t.SrcDir() + string(filepath.Separator) src := t.SrcDir() + string(filepath.Separator)
filepath.Walk(src, func(path string, fi os.FileInfo, err error) error { filepath.Walk(src, func(path string, fi os.FileInfo, err error) error {
@ -256,21 +286,19 @@ func allPackages() []string {
return nil return nil
} }
name := path[len(src):] name := path[len(src):]
if have[name] { if !have[name] {
return nil pkgs = append(pkgs, name)
have[name] = true
} }
pkgs = append(pkgs, name)
have[name] = true
// Avoid go/build test data. // Avoid go/build test data.
// TODO: Move it into a testdata directory.
if path == filepath.Join(build.Path[0].SrcDir(), "go/build") { if path == filepath.Join(build.Path[0].SrcDir(), "go/build") {
return filepath.SkipDir return filepath.SkipDir
} }
return nil return nil
}) })
// TODO: Commands.
} }
return pkgs return pkgs
} }

View File

@ -70,13 +70,15 @@ func loadPackage(arg string) (*Package, error) {
// Find basic information about package path. // Find basic information about package path.
t, importPath, err := build.FindTree(arg) t, importPath, err := build.FindTree(arg)
dir := ""
// Maybe it is a standard command. // 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] 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() { if st, err1 := os.Stat(p); err1 == nil && st.IsDir() {
t = goroot t = goroot
importPath = "../cmd/" + arg importPath = arg
dir = p
err = nil err = nil
} }
} }
@ -84,7 +86,9 @@ func loadPackage(arg string) (*Package, error) {
return nil, err 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. // Maybe we know the package by its directory.
if p := packageCache[dir]; p != nil { 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) 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. // Record package under both import path and full directory name.
packageCache[dir] = p packageCache[dir] = p
packageCache[importPath] = p packageCache[importPath] = p