summaryrefslogtreecommitdiff
path: root/src/cmd/go/build.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/go/build.go')
-rw-r--r--src/cmd/go/build.go1142
1 files changed, 1142 insertions, 0 deletions
diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go
new file mode 100644
index 000000000..cbe36f52e
--- /dev/null
+++ b/src/cmd/go/build.go
@@ -0,0 +1,1142 @@
+// 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 (
+ "bytes"
+ "container/heap"
+ "errors"
+ "fmt"
+ "go/build"
+ "io"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "regexp"
+ "runtime"
+ "strings"
+ "sync"
+)
+
+var cmdBuild = &Command{
+ UsageLine: "build [-a] [-n] [-o output] [-p n] [-v] [-x] [importpath... | gofiles...]",
+ Short: "compile packages and dependencies",
+ Long: `
+Build compiles the packages named by the import paths,
+along with their dependencies, but it does not install the results.
+
+If the arguments are a list of .go files, build treats them as a list
+of source files specifying a single package.
+
+When the command line specifies a single main package,
+build writes the resulting executable to output (default a.out).
+Otherwise build compiles the packages but discards the results,
+serving only as a check that the packages can be built.
+
+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 -v flag prints the names of packages as they are compiled.
+The -x flag prints the commands.
+
+The -o flag specifies the output file name.
+It is an error to use -o when the command line specifies multiple packages.
+
+The -p flag specifies the number of builds that can be run in parallel.
+The default is the number of CPUs available.
+
+For more about import paths, see 'go help importpath'.
+
+See also: go install, go get, go clean.
+ `,
+}
+
+func init() {
+ // break init cycle
+ cmdBuild.Run = runBuild
+ cmdInstall.Run = runInstall
+
+ addBuildFlags(cmdBuild)
+ addBuildFlags(cmdInstall)
+}
+
+// Flags set by multiple commands.
+var buildA bool // -a flag
+var buildN bool // -n flag
+var buildP = runtime.NumCPU() // -p flag
+var buildV bool // -v flag
+var buildX bool // -x flag
+var buildO = cmdBuild.Flag.String("o", "", "output file")
+
+var buildContext = build.DefaultContext
+
+// addBuildFlags adds the flags common to the build and install commands.
+func addBuildFlags(cmd *Command) {
+ cmd.Flag.BoolVar(&buildA, "a", false, "")
+ cmd.Flag.BoolVar(&buildN, "n", false, "")
+ cmd.Flag.IntVar(&buildP, "p", buildP, "")
+ cmd.Flag.BoolVar(&buildV, "v", false, "")
+ cmd.Flag.BoolVar(&buildX, "x", false, "")
+
+ // TODO(rsc): This -t flag is used by buildscript.sh but
+ // not documented. Should be documented but the
+ // usage lines are getting too long. Probably need to say
+ // that these flags are applicable to every command and
+ // document them in one help message instead of on every
+ // command's help message.
+ cmd.Flag.Var((*stringsFlag)(&buildContext.BuildTags), "t", "")
+}
+
+type stringsFlag []string
+
+func (v *stringsFlag) Set(s string) error {
+ *v = append(*v, s)
+ return nil
+}
+
+func (v *stringsFlag) String() string {
+ return "<stringsFlag>"
+}
+
+func runBuild(cmd *Command, args []string) {
+ var b builder
+ b.init()
+
+ var pkgs []*Package
+ if len(args) > 0 && strings.HasSuffix(args[0], ".go") {
+ pkg := goFilesPackage(args, "")
+ pkgs = append(pkgs, pkg)
+ } else {
+ pkgs = packagesForBuild(args)
+ }
+
+ if len(pkgs) == 1 && pkgs[0].Name == "main" && *buildO == "" {
+ *buildO = "a.out"
+ }
+
+ if *buildO != "" {
+ if len(pkgs) > 1 {
+ fatalf("go build: cannot use -o with multiple packages")
+ }
+ p := pkgs[0]
+ p.target = "" // must build - not up to date
+ a := b.action(modeInstall, modeBuild, p)
+ a.target = *buildO
+ b.do(a)
+ return
+ }
+
+ a := &action{}
+ for _, p := range packages(args) {
+ a.deps = append(a.deps, b.action(modeBuild, modeBuild, p))
+ }
+ b.do(a)
+}
+
+var cmdInstall = &Command{
+ UsageLine: "install [-a] [-n] [-p n] [-v] [-x] [importpath...]",
+ Short: "compile and install packages and dependencies",
+ Long: `
+Install compiles and installs the packages named by the import paths,
+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 -v flag prints the names of packages as they are compiled.
+The -x flag prints the commands.
+
+The -p flag specifies the number of builds that can be run in parallel.
+The default is the number of CPUs available.
+
+For more about import paths, see 'go help importpath'.
+
+See also: go build, go get, go clean.
+ `,
+}
+
+func runInstall(cmd *Command, args []string) {
+ pkgs := packagesForBuild(args)
+
+ var b builder
+ b.init()
+ a := &action{}
+ for _, p := range pkgs {
+ 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)
+ arch string // e.g., "6"
+ goroot string // the $GOROOT
+ goarch string // the $GOARCH
+ goos string // the $GOOS
+ gobin string // the $GOBIN
+ exe string // the executable suffix - "" or ".exe"
+ gcflags []string // additional flags for Go compiler
+ actionCache map[cacheKey]*action // a cache of already-constructed actions
+ mkdirCache map[string]bool // a cache of created directories
+
+ output sync.Mutex
+ scriptDir string // current directory in printed script
+
+ exec sync.Mutex
+ readySema chan bool
+ ready actionQueue
+}
+
+// An action represents a single action in the action graph.
+type action struct {
+ p *Package // the package this action works on
+ deps []*action // actions that must happen before this one
+ triggers []*action // inverse of deps
+ cgo *action // action for cgo binary if needed
+ args []string // additional args for runProgram
+ testOutput *bytes.Buffer // test output buffer
+
+ f func(*builder, *action) error // the action itself (nil = no-op)
+ ignoreFail bool // whether to run f even if dependencies fail
+
+ // Generated files, directories.
+ link bool // target is executable, not just package
+ pkgdir string // the -I or -L argument to use when importing this package
+ objdir string // directory for intermediate objects
+ objpkg string // the intermediate package .a file created during the action
+ target string // goal of the action: the created package or executable
+
+ // Execution state.
+ pending int // number of deps yet to complete
+ priority int // relative execution priority
+ failed bool // whether the action failed
+}
+
+// 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() {
+ var err error
+ b.actionCache = make(map[cacheKey]*action)
+ b.mkdirCache = make(map[string]bool)
+ b.goarch = buildContext.GOARCH
+ b.goos = buildContext.GOOS
+ b.goroot = build.Path[0].Path
+ b.gobin = build.Path[0].BinDir()
+ if b.goos == "windows" {
+ b.exe = ".exe"
+ }
+ b.gcflags = strings.Fields(os.Getenv("GCFLAGS"))
+
+ b.arch, err = build.ArchChar(b.goarch)
+ if err != nil {
+ fatalf("%s", err)
+ }
+
+ if buildN {
+ b.work = "$WORK"
+ } else {
+ b.work, err = ioutil.TempDir("", "go-build")
+ if err != nil {
+ fatalf("%s", err)
+ }
+ if buildX {
+ 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 {
+ // TODO: Remove this restriction.
+ for _, f := range gofiles {
+ if !strings.HasSuffix(f, ".go") || strings.Contains(f, "/") || strings.Contains(f, string(filepath.Separator)) {
+ fatalf("named files must be in current directory and .go files")
+ }
+ }
+
+ // 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 := buildContext
+ ctxt.ReadDir = func(string) ([]os.FileInfo, error) { return dir, nil }
+ pwd, _ := os.Getwd()
+ var stk importStack
+ pkg := scanPackage(&ctxt, &build.Tree{Path: "."}, "<command line>", "<command line>", pwd+"/.", &stk)
+ if pkg.Error != nil {
+ fatalf("%s", pkg.Error)
+ }
+ if target != "" {
+ pkg.target = target
+ } else if pkg.Name == "main" {
+ pkg.target = gofiles[0][:len(gofiles[0])-len(".go")]
+ } else {
+ pkg.target = pkg.Name + ".a"
+ }
+ pkg.ImportPath = "_/" + pkg.target
+ 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, pkgdir: p.t.PkgDir()}
+ if p.pkgdir != "" { // overrides p.t
+ a.pkgdir = p.pkgdir
+ }
+
+ b.actionCache[key] = a
+
+ for _, p1 := range p.imports {
+ a.deps = append(a.deps, b.action(depMode, depMode, p1))
+ }
+
+ if len(p.CgoFiles) > 0 {
+ var stk importStack
+ p1 := loadPackage("cmd/cgo", &stk)
+ if p1.Error != nil {
+ fatalf("load cmd/cgo: %v", p1.Error)
+ }
+ a.cgo = b.action(depMode, depMode, p1)
+ a.deps = append(a.deps, a.cgo)
+ }
+
+ if p.Standard {
+ switch p.ImportPath {
+ case "builtin", "unsafe":
+ // Fake packages - nothing to build.
+ return a
+ }
+ }
+
+ if !p.Stale && !buildA && p.target != "" {
+ // p.Stale==false implies that p.target is up-to-date.
+ // Record target name for use by actions depending on this one.
+ a.target = p.target
+ return a
+ }
+
+ a.objdir = filepath.Join(b.work, filepath.FromSlash(a.p.ImportPath+"/_obj")) + string(filepath.Separator)
+ a.objpkg = filepath.Join(b.work, filepath.FromSlash(a.p.ImportPath+".a"))
+ a.link = p.Name == "main"
+
+ switch mode {
+ case modeInstall:
+ a.f = (*builder).install
+ a.deps = []*action{b.action(modeBuild, depMode, p)}
+ a.target = a.p.target
+ case modeBuild:
+ a.f = (*builder).build
+ a.target = a.objpkg
+ if a.link {
+ // An executable file.
+ // Have to use something other than .a for the suffix.
+ // It is easier on Windows if we use .exe, so use .exe everywhere.
+ // (This is the name of a temporary file.)
+ a.target = a.objdir + "a.out" + b.exe
+ }
+ }
+
+ return a
+}
+
+// actionList returns the list of actions in the dag rooted at root
+// as visited in a depth-first post-order traversal.
+func actionList(root *action) []*action {
+ seen := map[*action]bool{}
+ all := []*action{}
+ var walk func(*action)
+ walk = func(a *action) {
+ if seen[a] {
+ return
+ }
+ seen[a] = true
+ for _, a1 := range a.deps {
+ walk(a1)
+ }
+ all = append(all, a)
+ }
+ walk(root)
+ return all
+}
+
+// do runs the action graph rooted at root.
+func (b *builder) do(root *action) {
+ // Build list of all actions, assigning depth-first post-order priority.
+ // The original implementation here was a true queue
+ // (using a channel) but it had the effect of getting
+ // distracted by low-level leaf actions to the detriment
+ // of completing higher-level actions. The order of
+ // work does not matter much to overall execution time,
+ // but when running "go test std" it is nice to see each test
+ // results as soon as possible. The priorities assigned
+ // ensure that, all else being equal, the execution prefers
+ // to do what it would have done first in a simple depth-first
+ // dependency order traversal.
+ all := actionList(root)
+ for i, a := range all {
+ a.priority = i
+ }
+
+ b.readySema = make(chan bool, len(all))
+ done := make(chan bool)
+
+ // Initialize per-action execution state.
+ for _, a := range all {
+ for _, a1 := range a.deps {
+ a1.triggers = append(a1.triggers, a)
+ }
+ a.pending = len(a.deps)
+ if a.pending == 0 {
+ b.ready.push(a)
+ b.readySema <- true
+ }
+ }
+
+ // Handle runs a single action and takes care of triggering
+ // any actions that are runnable as a result.
+ handle := func(a *action) {
+ var err error
+ if a.f != nil && (!a.failed || a.ignoreFail) {
+ err = a.f(b, a)
+ }
+
+ // The actions run in parallel but all the updates to the
+ // shared work state are serialized through b.exec.
+ b.exec.Lock()
+ defer b.exec.Unlock()
+
+ if err != nil {
+ if err == errPrintedOutput {
+ exitStatus = 2
+ } else {
+ errorf("%s", err)
+ }
+ a.failed = true
+ }
+
+ for _, a0 := range a.triggers {
+ if a.failed {
+ a0.failed = true
+ }
+ if a0.pending--; a0.pending == 0 {
+ b.ready.push(a0)
+ b.readySema <- true
+ }
+ }
+
+ if a == root {
+ close(b.readySema)
+ done <- true
+ }
+ }
+
+ // Kick off goroutines according to parallelism.
+ // If we are using the -n flag (just printing commands)
+ // drop the parallelism to 1, both to make the output
+ // deterministic and because there is no real work anyway.
+ par := buildP
+ if buildN {
+ par = 1
+ }
+ for i := 0; i < par; i++ {
+ go func() {
+ for _ = range b.readySema {
+ // Receiving a value from b.sema entitles
+ // us to take from the ready queue.
+ b.exec.Lock()
+ a := b.ready.pop()
+ b.exec.Unlock()
+ handle(a)
+ }
+ }()
+ }
+
+ <-done
+}
+
+// build is the action for building a single package or command.
+func (b *builder) build(a *action) error {
+ if buildN {
+ // 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)
+ }
+
+ if buildV {
+ fmt.Fprintf(os.Stderr, "%s\n", a.p.ImportPath)
+ }
+
+ // Make build directory.
+ obj := a.objdir
+ if err := b.mkdir(obj); err != nil {
+ return err
+ }
+
+ var gofiles, cfiles, sfiles, objects, cgoObjects []string
+ gofiles = append(gofiles, a.p.GoFiles...)
+ cfiles = append(cfiles, a.p.CFiles...)
+ sfiles = append(sfiles, a.p.SFiles...)
+
+ // Run cgo.
+ if len(a.p.CgoFiles) > 0 {
+ // In a package using cgo, cgo compiles the C and assembly files with gcc.
+ // There is one exception: runtime/cgo's job is to bridge the
+ // cgo and non-cgo worlds, so it necessarily has files in both.
+ // In that case gcc only gets the gcc_* files.
+ var gccfiles []string
+ if a.p.Standard && a.p.ImportPath == "runtime/cgo" {
+ filter := func(files, nongcc, gcc []string) ([]string, []string) {
+ for _, f := range files {
+ if strings.HasPrefix(f, "gcc_") {
+ gcc = append(gcc, f)
+ } else {
+ nongcc = append(nongcc, f)
+ }
+ }
+ return nongcc, gcc
+ }
+ cfiles, gccfiles = filter(cfiles, cfiles[:0], gccfiles)
+ sfiles, gccfiles = filter(sfiles, sfiles[:0], gccfiles)
+ } else {
+ gccfiles = append(cfiles, sfiles...)
+ cfiles = nil
+ sfiles = nil
+ }
+
+ outGo, outObj, err := b.cgo(a.p, a.cgo.target, obj, gccfiles)
+ if err != nil {
+ return err
+ }
+ cgoObjects = append(cgoObjects, outObj...)
+ gofiles = append(gofiles, outGo...)
+ }
+
+ // Prepare Go import path list.
+ inc := b.includeArgs("-I", a.deps)
+
+ // Compile Go.
+ if len(gofiles) > 0 {
+ out := "_go_." + b.arch
+ gcargs := []string{"-p", a.p.ImportPath}
+ if a.p.Standard && a.p.ImportPath == "runtime" {
+ // runtime compiles with a special 6g flag to emit
+ // additional reflect type data.
+ gcargs = append(gcargs, "-+")
+ }
+ if err := b.gc(a.p, obj+out, gcargs, inc, gofiles); err != nil {
+ return err
+ }
+ objects = append(objects, out)
+ }
+
+ // Copy .h files named for goos or goarch or goos_goarch
+ // to names using GOOS and GOARCH.
+ // For example, defs_linux_amd64.h becomes defs_GOOS_GOARCH.h.
+ _goos_goarch := "_" + b.goos + "_" + b.goarch + ".h"
+ _goos := "_" + b.goos + ".h"
+ _goarch := "_" + b.goarch + ".h"
+ for _, file := range a.p.HFiles {
+ switch {
+ case strings.HasSuffix(file, _goos_goarch):
+ targ := file[:len(file)-len(_goos_goarch)] + "_GOOS_GOARCH.h"
+ if err := b.copyFile(obj+targ, filepath.Join(a.p.Dir, file), 0666); err != nil {
+ return err
+ }
+ case strings.HasSuffix(file, _goarch):
+ targ := file[:len(file)-len(_goarch)] + "_GOARCH.h"
+ if err := b.copyFile(obj+targ, filepath.Join(a.p.Dir, file), 0666); err != nil {
+ return err
+ }
+ case strings.HasSuffix(file, _goos):
+ targ := file[:len(file)-len(_goos)] + "_GOOS.h"
+ if err := b.copyFile(obj+targ, filepath.Join(a.p.Dir, file), 0666); err != nil {
+ return err
+ }
+ }
+ }
+
+ for _, file := range cfiles {
+ out := file[:len(file)-len(".c")] + "." + b.arch
+ if err := b.cc(a.p, obj, obj+out, file); err != nil {
+ return err
+ }
+ objects = append(objects, out)
+ }
+
+ // Assemble .s files.
+ for _, file := range sfiles {
+ out := file[:len(file)-len(".s")] + "." + b.arch
+ if err := b.asm(a.p, obj, obj+out, file); err != nil {
+ return err
+ }
+ objects = append(objects, out)
+ }
+
+ // NOTE(rsc): On Windows, it is critically important that the
+ // gcc-compiled objects (cgoObjects) be listed after the ordinary
+ // objects in the archive. I do not know why this is.
+ // http://golang.org/issue/2601
+ objects = append(objects, cgoObjects...)
+
+ // Pack into archive in obj directory
+ if err := b.gopack(a.p, obj, a.objpkg, objects); err != nil {
+ return err
+ }
+
+ // Link if needed.
+ if a.link {
+ // The compiler only cares about direct imports, but the
+ // linker needs the whole dependency tree.
+ all := actionList(a)
+ all = all[:len(all)-1] // drop a
+ inc := b.includeArgs("-L", all)
+ if err := b.ld(a.p, a.target, inc, a.objpkg); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+// install is the action for installing a single package or executable.
+func (b *builder) install(a *action) error {
+ a1 := a.deps[0]
+ perm := os.FileMode(0666)
+ if a1.link {
+ perm = 0777
+ }
+
+ // make target directory
+ dir, _ := filepath.Split(a.target)
+ if dir != "" {
+ if err := b.mkdir(dir); err != nil {
+ return err
+ }
+ }
+
+ return b.copyFile(a.target, a1.target, perm)
+}
+
+// includeArgs returns the -I or -L directory list for access
+// to the results of the list of actions.
+func (b *builder) includeArgs(flag string, all []*action) []string {
+ inc := []string{}
+ incMap := map[string]bool{
+ b.work: true, // handled later
+ build.Path[0].PkgDir(): true, // goroot
+ "": true, // ignore empty strings
+ }
+
+ // Look in the temporary space for results of test-specific actions.
+ // This is the $WORK/my/package/_test directory for the
+ // package being built, so there are few of these.
+ for _, a1 := range all {
+ if dir := a1.pkgdir; dir != a1.p.t.PkgDir() && !incMap[dir] {
+ incMap[dir] = true
+ inc = append(inc, flag, dir)
+ }
+ }
+
+ // Also look in $WORK for any non-test packages that have
+ // been built but not installed.
+ inc = append(inc, flag, b.work)
+
+ // Finally, look in the installed package directories for each action.
+ for _, a1 := range all {
+ if dir := a1.pkgdir; dir == a1.p.t.PkgDir() && !incMap[dir] {
+ incMap[dir] = true
+ inc = append(inc, flag, dir)
+ }
+ }
+
+ return inc
+}
+
+// removeByRenaming removes file name by moving it to a tmp
+// directory and deleting the target if possible.
+func removeByRenaming(name string) error {
+ f, err := ioutil.TempFile("", "")
+ if err != nil {
+ return err
+ }
+ tmpname := f.Name()
+ f.Close()
+ err = os.Remove(tmpname)
+ if err != nil {
+ return err
+ }
+ err = os.Rename(name, tmpname)
+ if err != nil {
+ // assume name file does not exists,
+ // otherwise later code will fail.
+ return nil
+ }
+ err = os.Remove(tmpname)
+ if err != nil {
+ // TODO(brainman): file is locked and can't be deleted.
+ // We need to come up with a better way of doing it.
+ }
+ return nil
+}
+
+// copyFile is like 'cp src dst'.
+func (b *builder) copyFile(dst, src string, perm os.FileMode) error {
+ if buildN || buildX {
+ b.showcmd("", "cp %s %s", src, dst)
+ if buildN {
+ 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 {
+ if runtime.GOOS != "windows" {
+ return err
+ }
+ // Windows does not allow to replace binary file
+ // while it is executing. We will cheat.
+ err = removeByRenaming(dst)
+ if err != nil {
+ return err
+ }
+ 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 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(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
+// 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,
+// 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 && len(reldir) < len(dir) {
+ 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)
+}
+
+// relPaths returns a copy of paths with absolute paths
+// made relative to the current directory if they would be shorter.
+func relPaths(paths []string) []string {
+ var out []string
+ pwd, _ := os.Getwd()
+ for _, p := range paths {
+ rel, err := filepath.Rel(pwd, p)
+ if err == nil && len(rel) < len(p) {
+ p = rel
+ }
+ out = append(out, p)
+ }
+ return out
+}
+
+// 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, desc string, cmdargs ...interface{}) error {
+ out, err := b.runOut(dir, desc, cmdargs...)
+ if len(out) > 0 {
+ if out[len(out)-1] != '\n' {
+ out = append(out, '\n')
+ }
+ if desc == "" {
+ desc = b.fmtcmd(dir, "%s", strings.Join(stringList(cmdargs...), " "))
+ }
+ b.showOutput(dir, desc, string(out))
+ if err != nil {
+ err = errPrintedOutput
+ }
+ }
+ return err
+}
+
+// runOut runs the command given by cmdline in the directory dir.
+// It returns the command output and any errors that occurred.
+func (b *builder) runOut(dir string, desc string, cmdargs ...interface{}) ([]byte, error) {
+ cmdline := stringList(cmdargs...)
+ if buildN || buildX {
+ b.showcmd(dir, "%s", strings.Join(cmdline, " "))
+ if buildN {
+ return nil, 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()
+ return buf.Bytes(), err
+}
+
+// mkdir makes the named directory.
+func (b *builder) mkdir(dir string) error {
+ b.exec.Lock()
+ defer b.exec.Unlock()
+ // We can be a little aggressive about being
+ // sure directories exist. Skip repeated calls.
+ if b.mkdirCache[dir] {
+ return nil
+ }
+ b.mkdirCache[dir] = true
+
+ if buildN || buildX {
+ b.showcmd("", "mkdir -p %s", dir)
+ if buildN {
+ return nil
+ }
+ }
+
+ if err := os.MkdirAll(dir, 0777); err != nil {
+ return err
+ }
+ 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(p *Package, ofile string, gcargs, importArgs []string, gofiles []string) error {
+ args := stringList(b.arch+"g", "-o", ofile, b.gcflags, gcargs, importArgs)
+ 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(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(p *Package, objDir, afile string, ofiles []string) error {
+ var absOfiles []string
+ for _, f := range ofiles {
+ absOfiles = append(absOfiles, mkAbs(objDir, f))
+ }
+ return b.run(p.Dir, p.ImportPath, "gopack", "grc", mkAbs(objDir, afile), absOfiles)
+}
+
+// ld runs the linker to create a package starting at mainpkg.
+func (b *builder) ld(p *Package, out string, importArgs []string, mainpkg string) error {
+ return b.run(p.Dir, p.ImportPath, 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(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(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(p *Package, out string, flags []string, obj []string) error {
+ return b.run(p.Dir, p.ImportPath, b.gccCmd(p.Dir), "-o", out, obj, flags)
+}
+
+// gccCmd returns a gcc command line prefix
+func (b *builder) gccCmd(objdir string) []string {
+ // TODO: HOST_CC?
+ a := []string{"gcc", "-I", objdir, "-g", "-O2"}
+
+ // Definitely want -fPIC but on Windows gcc complains
+ // "-fPIC ignored for target (all code is position independent)"
+ if b.goos != "windows" {
+ a = append(a, "-fPIC")
+ }
+ switch b.arch {
+ case "8":
+ a = append(a, "-m32")
+ case "6":
+ a = append(a, "-m64")
+ }
+ // gcc-4.5 and beyond require explicit "-pthread" flag
+ // for multithreading with pthread library.
+ if buildContext.CgoEnabled {
+ switch b.goos {
+ case "windows":
+ a = append(a, "-mthreads")
+ default:
+ a = append(a, "-pthread")
+ }
+ }
+ return a
+}
+
+var cgoRe = regexp.MustCompile(`[/\\:]`)
+
+func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo, outObj []string, err error) {
+ if b.goos != runtime.GOOS {
+ return nil, nil, errors.New("cannot use cgo when compiling for a different operating system")
+ }
+
+ outObj = append(outObj, "") // for importObj, at end of function
+
+ cgoCFLAGS := stringList(p.info.CgoCFLAGS)
+ cgoLDFLAGS := stringList(p.info.CgoLDFLAGS)
+ if pkgs := p.info.CgoPkgConfig; len(pkgs) > 0 {
+ out, err := b.runOut(p.Dir, p.ImportPath, "pkg-config", "--cflags", pkgs)
+ if err != nil {
+ return nil, nil, err
+ }
+ if len(out) > 0 {
+ cgoCFLAGS = append(cgoCFLAGS, strings.Fields(string(out))...)
+ }
+ out, err = b.runOut(p.Dir, p.ImportPath, "pkg-config", "--libs", pkgs)
+ if err != nil {
+ return nil, nil, err
+ }
+ if len(out) > 0 {
+ cgoLDFLAGS = append(cgoLDFLAGS, strings.Fields(string(out))...)
+ }
+ }
+
+ // cgo
+ // TODO: CGOPKGPATH, CGO_FLAGS?
+ gofiles := []string{obj + "_cgo_gotypes.go"}
+ cfiles := []string{"_cgo_main.c", "_cgo_export.c"}
+ for _, fn := range p.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?
+ var runtimeFlag []string
+ if p.Standard && p.ImportPath == "runtime/cgo" {
+ runtimeFlag = []string{"-import_runtime_cgo=false"}
+ }
+ if err := b.run(p.Dir, p.ImportPath, cgoExe, "-objdir", obj, runtimeFlag, "--", p.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(p, obj, 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(p, ofile, 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 _, file := range gccfiles {
+ ofile := obj + cgoRe.ReplaceAllString(file[:len(file)-1], "_") + "o"
+ if err := b.gcc(p, ofile, cgoCFLAGS, file); err != nil {
+ return nil, nil, err
+ }
+ linkobj = append(linkobj, ofile)
+ outObj = append(outObj, ofile)
+ }
+ dynobj := obj + "_cgo_.o"
+ if err := b.gccld(p, dynobj, cgoLDFLAGS, linkobj); err != nil {
+ return nil, nil, err
+ }
+
+ // cgo -dynimport
+ importC := obj + "_cgo_import.c"
+ if err := b.run(p.Dir, p.ImportPath, cgoExe, "-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(p, obj, importObj, importC); err != nil {
+ return nil, nil, err
+ }
+
+ // NOTE(rsc): The importObj is a 5c/6c/8c object and on Windows
+ // must be processed before the gcc-generated objects.
+ // Put it first. We left room above. http://golang.org/issue/2601
+ outObj[0] = importObj
+
+ return outGo, outObj, nil
+}
+
+// An actionQueue is a priority queue of actions.
+type actionQueue []*action
+
+// Implement heap.Interface
+func (q *actionQueue) Len() int { return len(*q) }
+func (q *actionQueue) Swap(i, j int) { (*q)[i], (*q)[j] = (*q)[j], (*q)[i] }
+func (q *actionQueue) Less(i, j int) bool { return (*q)[i].priority < (*q)[j].priority }
+func (q *actionQueue) Push(x interface{}) { *q = append(*q, x.(*action)) }
+func (q *actionQueue) Pop() interface{} {
+ n := len(*q) - 1
+ x := (*q)[n]
+ *q = (*q)[:n]
+ return x
+}
+
+func (q *actionQueue) push(a *action) {
+ heap.Push(q, a)
+}
+
+func (q *actionQueue) pop() *action {
+ return heap.Pop(q).(*action)
+}