1
0
mirror of https://github.com/golang/go synced 2024-11-21 17:14:45 -07:00

go: implement build, install, run

clean is gone; all the intermediate files are created
in a temporary tree that is wiped when the command ends.

Not using go/build's Script because it is not well aligned
with this API.  The various builder methods are copied from
go/build and adapted.  Probably once we delete goinstall
we can delete the Script API too.

R=rogpeppe, adg, adg
CC=golang-dev
https://golang.org/cl/5483069
This commit is contained in:
Russ Cox 2011-12-14 22:42:42 -05:00
parent 24e9683ae6
commit 2ad8a9c507
10 changed files with 778 additions and 82 deletions

View File

@ -729,7 +729,9 @@ func (p *Package) gccMachine() []string {
return nil return nil
} }
var gccTmp = objDir + "_cgo_.o" func gccTmp() string {
return *objDir + "_cgo_.o"
}
// gccCmd returns the gcc command line to use for compiling // gccCmd returns the gcc command line to use for compiling
// the input. // the input.
@ -738,7 +740,7 @@ func (p *Package) gccCmd() []string {
p.gccName(), p.gccName(),
"-Wall", // many warnings "-Wall", // many warnings
"-Werror", // warnings are errors "-Werror", // warnings are errors
"-o" + gccTmp, // write object to tmp "-o" + gccTmp(), // write object to tmp
"-gdwarf-2", // generate DWARF v2 debugging symbols "-gdwarf-2", // generate DWARF v2 debugging symbols
"-fno-eliminate-unused-debug-types", // gets rid of e.g. untyped enum otherwise "-fno-eliminate-unused-debug-types", // gets rid of e.g. untyped enum otherwise
"-c", // do not link "-c", // do not link
@ -755,10 +757,10 @@ func (p *Package) gccCmd() []string {
func (p *Package) gccDebug(stdin []byte) (*dwarf.Data, binary.ByteOrder, []byte) { func (p *Package) gccDebug(stdin []byte) (*dwarf.Data, binary.ByteOrder, []byte) {
runGcc(stdin, p.gccCmd()) runGcc(stdin, p.gccCmd())
if f, err := macho.Open(gccTmp); err == nil { if f, err := macho.Open(gccTmp()); err == nil {
d, err := f.DWARF() d, err := f.DWARF()
if err != nil { if err != nil {
fatalf("cannot load DWARF output from %s: %v", gccTmp, err) fatalf("cannot load DWARF output from %s: %v", gccTmp(), err)
} }
var data []byte var data []byte
if f.Symtab != nil { if f.Symtab != nil {
@ -784,23 +786,23 @@ func (p *Package) gccDebug(stdin []byte) (*dwarf.Data, binary.ByteOrder, []byte)
// Can skip debug data block in ELF and PE for now. // Can skip debug data block in ELF and PE for now.
// The DWARF information is complete. // The DWARF information is complete.
if f, err := elf.Open(gccTmp); err == nil { if f, err := elf.Open(gccTmp()); err == nil {
d, err := f.DWARF() d, err := f.DWARF()
if err != nil { if err != nil {
fatalf("cannot load DWARF output from %s: %v", gccTmp, err) fatalf("cannot load DWARF output from %s: %v", gccTmp(), err)
} }
return d, f.ByteOrder, nil return d, f.ByteOrder, nil
} }
if f, err := pe.Open(gccTmp); err == nil { if f, err := pe.Open(gccTmp()); err == nil {
d, err := f.DWARF() d, err := f.DWARF()
if err != nil { if err != nil {
fatalf("cannot load DWARF output from %s: %v", gccTmp, err) fatalf("cannot load DWARF output from %s: %v", gccTmp(), err)
} }
return d, binary.LittleEndian, nil return d, binary.LittleEndian, nil
} }
fatalf("cannot parse gcc output %s as ELF, Mach-O, PE object", gccTmp) fatalf("cannot parse gcc output %s as ELF, Mach-O, PE object", gccTmp())
panic("not reached") panic("not reached")
} }

View File

@ -123,12 +123,14 @@ var cPrefix string
var fset = token.NewFileSet() var fset = token.NewFileSet()
var dynobj = flag.String("dynimport", "", "if non-empty, print dynamic import data for that file") var dynobj = flag.String("dynimport", "", "if non-empty, print dynamic import data for that file")
var dynout = flag.String("dynout", "", "write -dynobj output to this file")
// These flags are for bootstrapping a new Go implementation, // These flags are for bootstrapping a new Go implementation,
// to generate Go and C headers that match the data layout and // to generate Go and C headers that match the data layout and
// constant values used in the host's C libraries and system calls. // constant values used in the host's C libraries and system calls.
var godefs = flag.Bool("godefs", false, "for bootstrap: write Go definitions for C file to standard output") var godefs = flag.Bool("godefs", false, "for bootstrap: write Go definitions for C file to standard output")
var cdefs = flag.Bool("cdefs", false, "for bootstrap: write C definitions for C file to standard output") var cdefs = flag.Bool("cdefs", false, "for bootstrap: write C definitions for C file to standard output")
var objDir = flag.String("objdir", "", "object directory")
var gccgo = flag.Bool("gccgo", false, "generate files for use with gccgo") var gccgo = flag.Bool("gccgo", false, "generate files for use with gccgo")
@ -202,9 +204,13 @@ func main() {
fs[i] = f fs[i] = f
} }
// make sure that _obj directory exists, so that we can write if *objDir == "" {
// all the output files there. // make sure that _obj directory exists, so that we can write
os.Mkdir("_obj", 0777) // all the output files there.
os.Mkdir("_obj", 0777)
*objDir = "_obj"
}
*objDir += string(filepath.Separator)
for i, input := range goFiles { for i, input := range goFiles {
f := fs[i] f := fs[i]

View File

@ -14,20 +14,17 @@ import (
"go/printer" "go/printer"
"go/token" "go/token"
"os" "os"
"path/filepath"
"strings" "strings"
) )
var objDir = "_obj" + string(filepath.Separator)
// writeDefs creates output files to be compiled by 6g, 6c, and gcc. // writeDefs creates output files to be compiled by 6g, 6c, and gcc.
// (The comments here say 6g and 6c but the code applies to the 8 and 5 tools too.) // (The comments here say 6g and 6c but the code applies to the 8 and 5 tools too.)
func (p *Package) writeDefs() { func (p *Package) writeDefs() {
fgo2 := creat(objDir + "_cgo_gotypes.go") fgo2 := creat(*objDir + "_cgo_gotypes.go")
fc := creat(objDir + "_cgo_defun.c") fc := creat(*objDir + "_cgo_defun.c")
fm := creat(objDir + "_cgo_main.c") fm := creat(*objDir + "_cgo_main.c")
fflg := creat(objDir + "_cgo_flags") fflg := creat(*objDir + "_cgo_flags")
for k, v := range p.CgoFlags { for k, v := range p.CgoFlags {
fmt.Fprintf(fflg, "_CGO_%s=%s\n", k, v) fmt.Fprintf(fflg, "_CGO_%s=%s\n", k, v)
} }
@ -109,6 +106,15 @@ func (p *Package) writeDefs() {
} }
func dynimport(obj string) { func dynimport(obj string) {
stdout := os.Stdout
if *dynout != "" {
f, err := os.Create(*dynout)
if err != nil {
fatalf("%s", err)
}
stdout = f
}
if f, err := elf.Open(obj); err == nil { if f, err := elf.Open(obj); err == nil {
sym, err := f.ImportedSymbols() sym, err := f.ImportedSymbols()
if err != nil { if err != nil {
@ -119,14 +125,14 @@ func dynimport(obj string) {
if s.Version != "" { if s.Version != "" {
targ += "@" + s.Version targ += "@" + s.Version
} }
fmt.Printf("#pragma dynimport %s %s %q\n", s.Name, targ, s.Library) fmt.Fprintf(stdout, "#pragma dynimport %s %s %q\n", s.Name, targ, s.Library)
} }
lib, err := f.ImportedLibraries() lib, err := f.ImportedLibraries()
if err != nil { if err != nil {
fatalf("cannot load imported libraries from ELF file %s: %v", obj, err) fatalf("cannot load imported libraries from ELF file %s: %v", obj, err)
} }
for _, l := range lib { for _, l := range lib {
fmt.Printf("#pragma dynimport _ _ %q\n", l) fmt.Fprintf(stdout, "#pragma dynimport _ _ %q\n", l)
} }
return return
} }
@ -140,14 +146,14 @@ func dynimport(obj string) {
if len(s) > 0 && s[0] == '_' { if len(s) > 0 && s[0] == '_' {
s = s[1:] s = s[1:]
} }
fmt.Printf("#pragma dynimport %s %s %q\n", s, s, "") fmt.Fprintf(stdout, "#pragma dynimport %s %s %q\n", s, s, "")
} }
lib, err := f.ImportedLibraries() lib, err := f.ImportedLibraries()
if err != nil { if err != nil {
fatalf("cannot load imported libraries from Mach-O file %s: %v", obj, err) fatalf("cannot load imported libraries from Mach-O file %s: %v", obj, err)
} }
for _, l := range lib { for _, l := range lib {
fmt.Printf("#pragma dynimport _ _ %q\n", l) fmt.Fprintf(stdout, "#pragma dynimport _ _ %q\n", l)
} }
return return
} }
@ -159,7 +165,7 @@ func dynimport(obj string) {
} }
for _, s := range sym { for _, s := range sym {
ss := strings.Split(s, ":") ss := strings.Split(s, ":")
fmt.Printf("#pragma dynimport %s %s %q\n", ss[0], ss[0], strings.ToLower(ss[1])) fmt.Fprintf(stdout, "#pragma dynimport %s %s %q\n", ss[0], ss[0], strings.ToLower(ss[1]))
} }
return return
} }
@ -307,8 +313,8 @@ func (p *Package) writeOutput(f *File, srcfile string) {
base = base[0 : len(base)-3] base = base[0 : len(base)-3]
} }
base = strings.Map(slashToUnderscore, base) base = strings.Map(slashToUnderscore, base)
fgo1 := creat(objDir + base + ".cgo1.go") fgo1 := creat(*objDir + base + ".cgo1.go")
fgcc := creat(objDir + base + ".cgo2.c") fgcc := creat(*objDir + base + ".cgo2.c")
p.GoFiles = append(p.GoFiles, base+".cgo1.go") p.GoFiles = append(p.GoFiles, base+".cgo1.go")
p.GccFiles = append(p.GccFiles, base+".cgo2.c") p.GccFiles = append(p.GccFiles, base+".cgo2.c")
@ -383,7 +389,7 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) {
// Write out the various stubs we need to support functions exported // Write out the various stubs we need to support functions exported
// from Go so that they are callable from C. // from Go so that they are callable from C.
func (p *Package) writeExports(fgo2, fc, fm *os.File) { func (p *Package) writeExports(fgo2, fc, fm *os.File) {
fgcc := creat(objDir + "_cgo_export.c") fgcc := creat(*objDir + "_cgo_export.c")
fgcch := creat("_cgo_export.h") fgcch := creat("_cgo_export.h")
fmt.Fprintf(fgcch, "/* Created by cgo - DO NOT EDIT. */\n") fmt.Fprintf(fgcch, "/* Created by cgo - DO NOT EDIT. */\n")

View File

@ -7,7 +7,6 @@ include ../../Make.inc
TARG=go TARG=go
GOFILES=\ GOFILES=\
build.go\ build.go\
clean.go\
fix.go\ fix.go\
get.go\ get.go\
fmt.go\ fmt.go\
@ -15,6 +14,7 @@ GOFILES=\
list.go\ list.go\
main.go\ main.go\
pkg.go\ pkg.go\
run.go\
test.go\ test.go\
version.go\ version.go\
vet.go\ vet.go\

View File

@ -4,14 +4,38 @@
package main package main
import (
"bytes"
"fmt"
"go/build"
"io"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"regexp"
"runtime"
"strings"
)
// Break init cycles
func init() {
cmdBuild.Run = runBuild
cmdInstall.Run = runInstall
}
var cmdBuild = &Command{ var cmdBuild = &Command{
Run: runBuild, UsageLine: "build [-a] [-n] [-v] [importpath... | gofiles...]",
UsageLine: "build [-n] [-v] [importpath...]", Short: "compile packages and dependencies",
Short: "compile and install packages and dependencies",
Long: ` Long: `
Build compiles the packages named by the import paths, Build compiles the packages named by the import paths,
along with their dependencies, but it does not install the results. along with their dependencies, but it does not install the results.
If the arguments are a list of .go files, build compiles them into
a package object or command executable named for the first
source file.
The -a flag forces rebuilding of packages that are already up-to-date.
The -n flag prints the commands but does not run them. The -n flag prints the commands but does not run them.
The -v flag prints the commands. The -v flag prints the commands.
@ -21,23 +45,34 @@ See also: go install, go get, go clean.
`, `,
} }
var buildA = cmdBuild.Flag.Bool("a", false, "")
var buildN = cmdBuild.Flag.Bool("n", false, "") var buildN = cmdBuild.Flag.Bool("n", false, "")
var buildV = cmdBuild.Flag.Bool("v", false, "") var buildV = cmdBuild.Flag.Bool("v", false, "")
func runBuild(cmd *Command, args []string) { func runBuild(cmd *Command, args []string) {
args = importPaths(args) var b builder
_ = args b.init(*buildA, *buildN, *buildV)
panic("build not implemented")
if len(args) > 0 && strings.HasSuffix(args[0], ".go") {
b.do(b.action(modeInstall, modeBuild, goFilesPackage(args, "")))
return
}
a := &action{f: (*builder).nop}
for _, p := range packages(args) {
a.deps = append(a.deps, b.action(modeBuild, modeBuild, p))
}
b.do(a)
} }
var cmdInstall = &Command{ var cmdInstall = &Command{
Run: runInstall, UsageLine: "install [-a] [-n] [-v] [importpath...]",
UsageLine: "install [-n] [-v] [importpath...]", Short: "compile and install packages and dependencies",
Short: "install packages and dependencies",
Long: ` Long: `
Install compiles and installs the packages named by the import paths, Install compiles and installs the packages named by the import paths,
along with their dependencies. along with their dependencies.
The -a flag forces reinstallation of packages that are already up-to-date.
The -n flag prints the commands but does not run them. The -n flag prints the commands but does not run them.
The -v flag prints the commands. The -v flag prints the commands.
@ -47,11 +82,541 @@ See also: go build, go get, go clean.
`, `,
} }
var installA = cmdInstall.Flag.Bool("a", false, "")
var installN = cmdInstall.Flag.Bool("n", false, "") var installN = cmdInstall.Flag.Bool("n", false, "")
var installV = cmdInstall.Flag.Bool("v", false, "") var installV = cmdInstall.Flag.Bool("v", false, "")
func runInstall(cmd *Command, args []string) { func runInstall(cmd *Command, args []string) {
args = importPaths(args) var b builder
_ = args b.init(*installA, *installN, *installV)
panic("install not implemented") a := &action{f: (*builder).nop}
for _, p := range packages(args) {
a.deps = append(a.deps, b.action(modeInstall, modeInstall, p))
}
b.do(a)
}
// A builder holds global state about a build.
// It does not hold per-package state, because eventually we will
// build packages in parallel, and the builder will be shared.
type builder struct {
work string // the temporary work directory (ends in filepath.Separator)
aflag bool // the -a flag
nflag bool // the -n flag
vflag bool // the -v flag
arch string // e.g., "6"
actionCache map[cacheKey]*action // a cache of already-constructed actions
}
// An action represents a single action in the action graph.
type action struct {
f func(*builder, *action) error // the action itself
p *Package // the package this action works on
deps []*action // actions that must happen before this one
done bool // whether the action is done (might have failed)
failed bool // whether the action failed
// Results left for communication with other code.
pkgobj string // the built .a file
pkgbin string // the built a.out file, if one exists
}
// cacheKey is the key for the action cache.
type cacheKey struct {
mode buildMode
p *Package
}
// buildMode specifies the build mode:
// are we just building things or also installing the results?
type buildMode int
const (
modeBuild buildMode = iota
modeInstall
)
func (b *builder) init(aflag, nflag, vflag bool) {
var err error
b.aflag = aflag
b.nflag = nflag
b.vflag = vflag
b.actionCache = make(map[cacheKey]*action)
b.arch, err = build.ArchChar(build.DefaultContext.GOARCH)
if err != nil {
fatalf("%s", err)
}
if nflag {
b.work = "$WORK"
} else {
b.work, err = ioutil.TempDir("", "go-build")
if err != nil {
fatalf("%s", err)
}
if vflag {
fmt.Printf("WORK=%s\n", b.work)
}
atexit(func() { os.RemoveAll(b.work) })
}
}
// goFilesPackage creates a package for building a collection of Go files
// (typically named on the command line). If target is given, the package
// target is target. Otherwise, the target is named p.a for
// package p or named after the first Go file for package main.
func goFilesPackage(gofiles []string, target string) *Package {
// Synthesize fake "directory" that only shows those two files,
// to make it look like this is a standard package or
// command directory.
var dir []os.FileInfo
for _, file := range gofiles {
fi, err := os.Stat(file)
if err != nil {
fatalf("%s", err)
}
if fi.IsDir() {
fatalf("%s is a directory, should be a Go file", file)
}
dir = append(dir, fi)
}
ctxt := build.DefaultContext
ctxt.ReadDir = func(string) ([]os.FileInfo, error) { return dir, nil }
pwd, _ := os.Getwd()
pkg, err := scanPackage(&ctxt, &build.Tree{Path: "."}, "<command line>", "<command line>", pwd)
if err != nil {
fatalf("%s", err)
}
if target != "" {
pkg.targ = target
} else if pkg.Name == "main" {
pkg.targ = gofiles[0][:len(gofiles[0])-len(".go")]
} else {
pkg.targ = pkg.Name + ".a"
}
pkg.ImportPath = "_/" + pkg.targ
return pkg
}
// action returns the action for applying the given operation (mode) to the package.
// depMode is the action to use when building dependencies.
func (b *builder) action(mode buildMode, depMode buildMode, p *Package) *action {
key := cacheKey{mode, p}
a := b.actionCache[key]
if a != nil {
return a
}
a = &action{p: p}
b.actionCache[key] = a
switch mode {
case modeBuild, modeInstall:
if !needInstall(p) && !b.aflag {
a.f = (*builder).nop
return a
}
if p.Standard {
switch p.ImportPath {
case "runtime", "runtime/cgo":
// Too complex - can't build.
a.f = (*builder).nop
return a
case "builtin", "unsafe":
// Fake packages - nothing to build.
a.f = (*builder).nop
return a
}
}
if mode == modeInstall {
a.f = (*builder).install
a.deps = []*action{b.action(modeBuild, depMode, p)}
return a
}
a.f = (*builder).build
for _, p1 := range p.imports {
a.deps = append(a.deps, b.action(depMode, depMode, p1))
}
}
return a
}
// needInstall reports whether p needs to be built and installed.
// That is only true if some source file is newer than the installed package binary.
func needInstall(p *Package) bool {
if p.targ == "" {
return true
}
fi, err := os.Stat(p.targ)
if err != nil {
return true
}
t := fi.ModTime()
srcss := [][]string{
p.GoFiles,
p.CFiles,
p.SFiles,
p.CgoFiles,
}
for _, srcs := range srcss {
for _, src := range srcs {
fi, err := os.Stat(filepath.Join(p.Dir, src))
if err != nil {
return true
}
if fi.ModTime().After(t) {
return true
}
}
}
return false
}
// do runs the action graph rooted at a.
func (b *builder) do(a *action) {
if a.done {
return
}
for _, a1 := range a.deps {
b.do(a1)
if a1.failed {
a.failed = true
a.done = true
return
}
}
if err := a.f(b, a); err != nil {
errorf("%s", err)
a.failed = true
}
a.done = true
}
func (b *builder) nop(a *action) error {
return nil
}
// build is the action for building a single package.
func (b *builder) build(a *action) error {
obj := filepath.Join(b.work, filepath.FromSlash(a.p.ImportPath+"/_obj")) + string(filepath.Separator)
a.pkgobj = filepath.Join(b.work, filepath.FromSlash(a.p.ImportPath+".a"))
// make build directory
if err := b.mkdir(obj); err != nil {
return err
}
var objects []string
var gofiles []string
gofiles = append(gofiles, a.p.GoFiles...)
// run cgo
if len(a.p.CgoFiles) > 0 {
outGo, outObj, err := b.cgo(a.p.Dir, obj, a.p.info)
if err != nil {
return err
}
objects = append(objects, outObj...)
gofiles = append(gofiles, outGo...)
}
// prepare Go import path list
var inc []string
inc = append(inc, "-I", b.work)
incMap := map[string]bool{}
for _, a1 := range a.deps {
p1 := a1.p
if p1.t.Goroot {
continue
}
pkgdir := p1.t.PkgDir()
if !incMap[pkgdir] {
incMap[pkgdir] = true
inc = append(inc, "-I", pkgdir)
}
}
// compile Go
if len(gofiles) > 0 {
out := "_go_.6"
if err := b.gc(a.p.Dir, obj+out, a.p.ImportPath, inc, gofiles); err != nil {
return err
}
objects = append(objects, out)
}
// assemble .s files
if len(a.p.SFiles) > 0 {
for _, sfile := range a.p.SFiles {
out := sfile[:len(sfile)-len(".s")] + "." + b.arch
if err := b.asm(a.p.Dir, obj+out, sfile); err != nil {
return err
}
objects = append(objects, out)
}
}
// pack into archive
if err := b.gopack(obj, a.pkgobj, objects); err != nil {
return err
}
if a.p.Name == "main" {
// command.
// import paths for compiler are introduced by -I.
// for linker, they are introduced by -L.
for i := 0; i < len(inc); i += 2 {
inc[i] = "-L"
}
a.pkgbin = obj + "a.out"
if err := b.ld(a.p.Dir, a.pkgbin, inc, a.pkgobj); err != nil {
return err
}
}
return nil
}
// install is the action for installing a single package.
func (b *builder) install(a *action) error {
if err := b.build(a); err != nil {
return err
}
var src string
var perm uint32
if a.pkgbin != "" {
src = a.pkgbin
perm = 0777
} else {
src = a.pkgobj
perm = 0666
}
// make target directory
dst := a.p.targ
dir, _ := filepath.Split(dst)
if dir != "" {
if err := b.mkdir(dir); err != nil {
return err
}
}
return b.copyFile(dst, src, perm)
}
// copyFile is like 'cp src dst'.
func (b *builder) copyFile(dst, src string, perm uint32) error {
if b.nflag || b.vflag {
b.showcmd("cp %s %s", src, dst)
if b.nflag {
return nil
}
}
sf, err := os.Open(src)
if err != nil {
return err
}
defer sf.Close()
os.Remove(dst)
df, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm)
if err != nil {
return err
}
_, err = io.Copy(df, sf)
df.Close()
if err != nil {
os.Remove(dst)
return err
}
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
}
// showcmd prints the given command to standard output
// for the implementation of -n or -v.
func (b *builder) showcmd(format string, args ...interface{}) {
fmt.Println(b.fmtcmd(format, args...))
}
// 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 {
if b.nflag || b.vflag {
b.showcmd("cd %s; %s", dir, strings.Join(cmdline, " "))
if b.nflag {
return nil
}
}
var buf bytes.Buffer
cmd := exec.Command(cmdline[0], cmdline[1:]...)
cmd.Stdout = &buf
cmd.Stderr = &buf
cmd.Dir = dir
// TODO: cmd.Env
err := cmd.Run()
if buf.Len() > 0 {
fmt.Fprintf(os.Stderr, "# cd %s; %s\n", dir, strings.Join(cmdline, " "))
fmt.Fprintf(os.Stderr, "%s\n", buf.Bytes())
}
return err
}
// mkdir makes the named directory.
func (b *builder) mkdir(dir string) error {
if b.nflag || b.vflag {
b.showcmd("mkdir -p %s", dir)
if b.nflag {
return nil
}
}
if err := os.MkdirAll(dir, 0777); err != nil {
return err
}
return nil
}
// 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, importPath string, importArgs []string, gofiles []string) error {
args := append([]string{b.arch + "g", "-o", ofile, "-p", importPath}, importArgs...)
args = append(args, gofiles...)
return b.run(dir, args...)
}
// asm runs the assembler in a specific directory on a specific file
// to generate the named output file.
func (b *builder) asm(dir, ofile, sfile string) error {
return b.run(dir, b.arch+"a", "-o", ofile, 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...)...)
}
// 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)...)
}
// cc runs the gc-toolchain C compiler in a directory on a C file
// to produce an output file.
func (b *builder) cc(dir, ofile, cfile string) error {
inc := filepath.Join(runtime.GOROOT(), "pkg",
fmt.Sprintf("%s_%s", build.DefaultContext.GOOS, build.DefaultContext.GOARCH))
return b.run(dir, b.arch+"c", "-FVW", "-I", inc, "-o", ofile, 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)...)
}
// 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...)...)
}
// gccCmd returns a gcc command line ending with args
func (b *builder) gccCmd(objdir string, flags []string, args ...string) []string {
// TODO: HOST_CC?
a := []string{"gcc", "-I", objdir, "-g", "-fPIC", "-O2"}
switch b.arch {
case "8":
a = append(a, "-m32")
case "6":
a = append(a, "-m64")
}
a = append(a, flags...)
return append(a, args...)
}
var cgoRe = regexp.MustCompile(`[/\\:]`)
func (b *builder) cgo(dir, obj string, info *build.DirInfo) (outGo, outObj []string, err error) {
// cgo
// TODO: CGOPKGPATH, CGO_FLAGS?
gofiles := []string{obj + "_cgo_gotypes.go"}
cfiles := []string{"_cgo_main.c", "_cgo_export.c"}
for _, fn := range info.CgoFiles {
f := cgoRe.ReplaceAllString(fn[:len(fn)-2], "_")
gofiles = append(gofiles, obj+f+"cgo1.go")
cfiles = append(cfiles, f+"cgo2.c")
}
defunC := obj + "_cgo_defun.c"
// TODO: make cgo not depend on $GOARCH?
// TODO: make cgo write to obj
if err := b.run(dir, append([]string{"cgo", "-objdir", obj, "--"}, info.CgoFiles...)...); 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, defunObj, defunC); err != nil {
return nil, nil, err
}
outObj = append(outObj, defunObj)
// gcc
var linkobj []string
for _, cfile := range cfiles {
ofile := obj + cfile[:len(cfile)-1] + "o"
if err := b.gcc(dir, ofile, info.CgoCFLAGS, obj+cfile); err != nil {
return nil, nil, err
}
linkobj = append(linkobj, ofile)
if !strings.HasSuffix(ofile, "_cgo_main.o") {
outObj = append(outObj, ofile)
}
}
for _, cfile := range info.CFiles {
ofile := obj + cgoRe.ReplaceAllString(cfile[:len(cfile)-1], "_") + "o"
if err := b.gcc(dir, ofile, info.CgoCFLAGS, cfile); err != nil {
return nil, nil, err
}
linkobj = append(linkobj, ofile)
outObj = append(outObj, ofile)
}
dynobj := obj + "_cgo_.o"
if err := b.gccld(dir, dynobj, 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 {
return nil, nil, err
}
// cc _cgo_import.ARCH
importObj := obj + "_cgo_import." + b.arch
if err := b.cc(dir, importObj, importC); err != nil {
return nil, nil, err
}
outObj = append(outObj, importObj)
return outGo, outObj, nil
} }

View File

@ -1,31 +0,0 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
var cmdClean = &Command{
Run: runClean,
UsageLine: "clean [-nuke] [importpath...]",
Short: "remove intermediate objects",
Long: `
Clean removes intermediate object files generated during
the compilation of the packages named by the import paths,
but by default it does not remove the installed package binaries.
The -nuke flag causes clean to remove the installed package binaries too.
TODO: Clean does not clean dependencies of the packages.
TODO: Rename -nuke.
For more about import paths, see 'go help importpath'.
`,
}
var cleanNuke = cmdClean.Flag.Bool("nuke", false, "")
func runClean(cmd *Command, args []string) {
args = importPaths(args)
_ = args
panic("nuke not implemented")
}

View File

@ -58,12 +58,19 @@ func init() {
var listFmt = cmdList.Flag.String("f", "{{.Name}} {{.Dir}}", "") var listFmt = cmdList.Flag.String("f", "{{.Name}} {{.Dir}}", "")
var listJson = cmdList.Flag.Bool("json", false, "") var listJson = cmdList.Flag.Bool("json", false, "")
var nl = []byte{'\n'}
func runList(cmd *Command, args []string) { func runList(cmd *Command, args []string) {
var do func(*Package) var do func(*Package)
if *listJson { if *listJson {
enc := json.NewEncoder(os.Stdout) do = func(p *Package) {
do = func(p *Package) { enc.Encode(p) } b, err := json.MarshalIndent(p, "", "\t")
if err != nil {
fatalf("%s", err)
}
os.Stdout.Write(b)
os.Stdout.Write(nl)
}
} else { } else {
tmpl, err := template.New("main").Parse(*listFmt + "\n") tmpl, err := template.New("main").Parse(*listFmt + "\n")
if err != nil { if err != nil {

View File

@ -7,10 +7,12 @@ package main
import ( import (
"flag" "flag"
"fmt" "fmt"
"go/build"
"io" "io"
"log" "log"
"os" "os"
"os/exec" "os/exec"
"path/filepath"
"strings" "strings"
"text/template" "text/template"
) )
@ -56,13 +58,13 @@ func (c *Command) Usage() {
// The order here is the order in which they are printed by 'go help'. // The order here is the order in which they are printed by 'go help'.
var commands = []*Command{ var commands = []*Command{
cmdBuild, cmdBuild,
cmdClean,
cmdDoc, cmdDoc,
cmdFix, cmdFix,
cmdFmt, cmdFmt,
cmdGet, cmdGet,
cmdInstall, cmdInstall,
cmdList, cmdList,
cmdRun,
cmdTest, cmdTest,
cmdVersion, cmdVersion,
cmdVet, cmdVet,
@ -95,7 +97,7 @@ func main() {
cmd.Flag.Parse(args[1:]) cmd.Flag.Parse(args[1:])
args = cmd.Flag.Args() args = cmd.Flag.Args()
cmd.Run(cmd, args) cmd.Run(cmd, args)
os.Exit(exitStatus) exit()
return return
} }
} }
@ -173,16 +175,31 @@ func help(args []string) {
// importPaths returns the import paths to use for the given command line. // importPaths returns the import paths to use for the given command line.
func importPaths(args []string) []string { func importPaths(args []string) []string {
// TODO: "all" if len(args) == 1 && args[0] == "all" {
return allPackages()
}
if len(args) == 0 { if len(args) == 0 {
return []string{"."} return []string{"."}
} }
return args return args
} }
var atexitFuncs []func()
func atexit(f func()) {
atexitFuncs = append(atexitFuncs, f)
}
func exit() {
for _, f := range atexitFuncs {
f()
}
os.Exit(exitStatus)
}
func fatalf(format string, args ...interface{}) { func fatalf(format string, args ...interface{}) {
log.Printf(format, args...) errorf(format, args...)
os.Exit(1) exit()
} }
func errorf(format string, args ...interface{}) { func errorf(format string, args ...interface{}) {
@ -192,7 +209,7 @@ func errorf(format string, args ...interface{}) {
func exitIfErrors() { func exitIfErrors() {
if exitStatus != 0 { if exitStatus != 0 {
os.Exit(exitStatus) exit()
} }
} }
@ -204,3 +221,52 @@ func run(cmdline ...string) {
errorf("%v", err) errorf("%v", err)
} }
} }
// allPackages returns all the packages that can be found
// under the $GOPATH directories and $GOROOT.
func allPackages() []string {
have := make(map[string]bool)
var pkgs []string
runtime := filepath.Join(build.Path[0].SrcDir(), "runtime") + string(filepath.Separator)
for _, t := range build.Path {
src := t.SrcDir() + string(filepath.Separator)
filepath.Walk(src, func(path string, fi os.FileInfo, err error) error {
if err != nil || !fi.IsDir() {
return nil
}
// Avoid testdata directory trees.
if strings.HasSuffix(path, string(filepath.Separator)+"testdata") {
return filepath.SkipDir
}
// Avoid runtime subdirectories.
if strings.HasPrefix(path, runtime) {
switch path {
case runtime + "darwin", runtime + "freebsd", runtime + "linux", runtime + "netbsd", runtime + "openbsd", runtime + "windows":
return filepath.SkipDir
}
}
_, err = build.ScanDir(path)
if err != nil {
return nil
}
name := path[len(src):]
if have[name] {
return nil
}
pkgs = append(pkgs, name)
have[name] = true
// Avoid go/build test data.
if path == filepath.Join(build.Path[0].SrcDir(), "go/build") {
return filepath.SkipDir
}
return nil
})
// TODO: Commands.
}
return pkgs
}

View File

@ -8,6 +8,7 @@ import (
"fmt" "fmt"
"go/build" "go/build"
"go/doc" "go/doc"
"os"
"path/filepath" "path/filepath"
"sort" "sort"
"strings" "strings"
@ -40,6 +41,7 @@ type Package struct {
info *build.DirInfo info *build.DirInfo
imports []*Package imports []*Package
gofiles []string // GoFiles+CgoFiles gofiles []string // GoFiles+CgoFiles
targ string
} }
// packageCache is a lookup cache for loadPackage, // packageCache is a lookup cache for loadPackage,
@ -66,10 +68,21 @@ 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)
// Maybe it is a standard command.
if err != nil && !filepath.IsAbs(arg) && !strings.HasPrefix(arg, ".") {
goroot := build.Path[0]
p := filepath.Join(goroot.Path, "src/cmd", arg)
if st, err1 := os.Stat(p); err1 == nil && st.IsDir() {
t = goroot
importPath = "../cmd/" + arg
err = nil
}
}
if err != nil { if err != nil {
return nil, err return nil, err
} }
dir := filepath.Join(t.SrcDir(), importPath)
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 {
@ -79,13 +92,25 @@ func loadPackage(arg string) (*Package, error) {
return p, nil return p, nil
} }
return scanPackage(&build.DefaultContext, t, arg, importPath, dir)
}
func scanPackage(ctxt *build.Context, t *build.Tree, arg, importPath, dir string) (*Package, error) {
// Read the files in the directory to learn the structure // Read the files in the directory to learn the structure
// of the package. // of the package.
info, err := build.ScanDir(dir) info, err := ctxt.ScanDir(dir)
if err != nil { if err != nil {
return nil, err return nil, err
} }
var targ string
if info.Package == "main" {
_, elem := filepath.Split(importPath)
targ = filepath.Join(t.BinDir(), elem)
} else {
targ = filepath.Join(t.PkgDir(), filepath.FromSlash(importPath)+".a")
}
p := &Package{ p := &Package{
Name: info.Package, Name: info.Package,
Doc: doc.CommentText(info.PackageComment), Doc: doc.CommentText(info.PackageComment),
@ -97,6 +122,9 @@ func loadPackage(arg string) (*Package, error) {
SFiles: info.SFiles, SFiles: info.SFiles,
CgoFiles: info.CgoFiles, CgoFiles: info.CgoFiles,
Standard: t.Goroot && !strings.Contains(importPath, "."), Standard: t.Goroot && !strings.Contains(importPath, "."),
targ: targ,
t: t,
info: info,
} }
// Build list of full paths to all Go files in the package, // Build list of full paths to all Go files in the package,
@ -123,7 +151,7 @@ func loadPackage(arg string) (*Package, error) {
} }
p1, err := loadPackage(path) p1, err := loadPackage(path)
if err != nil { if err != nil {
delete(packageCache, arg) delete(packageCache, dir)
delete(packageCache, importPath) delete(packageCache, importPath)
// Add extra error detail to show full import chain. // Add extra error detail to show full import chain.
// Always useful, but especially useful in import loops. // Always useful, but especially useful in import loops.

47
src/cmd/go/run.go Normal file
View File

@ -0,0 +1,47 @@
// 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 ()
// Break init loop.
func init() {
cmdRun.Run = runRun
}
var cmdRun = &Command{
UsageLine: "run [-a] [-n] [-v] gofiles...",
Short: "compile and run Go program",
Long: `
Run compiles and runs the main package comprising the named Go source files.
The -a flag forces reinstallation of packages that are already up-to-date.
The -n flag prints the commands but does not run them.
The -v flag prints the commands.
See also: go build.
`,
}
var runA = cmdRun.Flag.Bool("a", false, "")
var runN = cmdRun.Flag.Bool("n", false, "")
var runV = cmdRun.Flag.Bool("v", false, "")
func runRun(cmd *Command, args []string) {
var b builder
b.init(*runA, *runN, *runV)
p := goFilesPackage(args, "")
p.targ = "" // force rebuild - no up-to-date copy anywhere
a1 := b.action(modeBuild, modeBuild, p)
a := &action{f: (*builder).runProgram, deps: []*action{a1}}
b.do(a)
}
// runProgram is the action for running a binary that has already
// been compiled. We ignore exit status.
func (b *builder) runProgram(a *action) error {
run(a.deps[0].pkgbin)
return nil
}