]> Cypherpunks.ru repositories - gostls13.git/commitdiff
build: require old Go to build new Go (and convert cmd/dist to Go)
authorRuss Cox <rsc@golang.org>
Wed, 7 Jan 2015 16:38:00 +0000 (11:38 -0500)
committerRuss Cox <rsc@golang.org>
Sat, 10 Jan 2015 19:16:00 +0000 (19:16 +0000)
This CL introduces the bootstrap requirement that in order to
build the current release (or development version) of Go, you
need an older Go release (1.4 or newer) already installed.
This requirement is the whole point of this CL.

To enforce the requirement, convert cmd/dist from C to Go.

With this bootstrapping out of the way, we can move on to
replacing other, larger C programs like the Go compiler,
the assemblers, and the linker.

See golang.org/s/go15bootstrap for details.

Change-Id: I53fd08ddacf3df9fae94fe2c986dba427ee4a21d
Reviewed-on: https://go-review.googlesource.com/2470
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Reviewed-by: Rob Pike <r@golang.org>
16 files changed:
src/cmd/dist/README
src/cmd/dist/build.go
src/cmd/dist/buildgc.go
src/cmd/dist/buildgo.go
src/cmd/dist/buildruntime.go
src/cmd/dist/cpuid_386.s [new file with mode: 0644]
src/cmd/dist/cpuid_amd64.s [new file with mode: 0644]
src/cmd/dist/cpuid_default.s [new file with mode: 0644]
src/cmd/dist/main.go
src/cmd/dist/sys_default.go [new file with mode: 0644]
src/cmd/dist/sys_windows.go [new file with mode: 0644]
src/cmd/dist/util.go
src/cmd/dist/vfp_arm.s [new file with mode: 0644]
src/cmd/dist/vfp_default.s [new file with mode: 0644]
src/make.bash
src/sudo.bash [deleted file]

index e6d08cf0284be3c75c9856755e66bf8fe1233be8..0649e887f45a4e162bef8e8279cc4b1ffaba5b5e 100644 (file)
@@ -1,45 +1,27 @@
 This program, dist, is the bootstrapping tool for the Go distribution.
-It takes care of building the C programs (like the Go compiler) and
-the initial bootstrap copy of the go tool.  It also serves as a catch-all
-to replace odd jobs previously done with shell scripts.
 
-Dist is itself written in very simple C.  All interaction with C libraries,
-even standard C libraries, is confined to a single system-specific file
-(plan9.c, unix.c, windows.c), to aid portability.  Functionality needed
-by other files should be exposed via the portability layer.  Functions
-in the portability layer begin with an x prefix when they would otherwise
-use the same name as or be confused for an existing function.
-For example, xprintf is the portable printf.
+As of Go 1.5, dist and other parts of the compiler toolchain are written
+in Go, making bootstrapping a little more involved than in the past.
+The approach is to build the current release of Go with an earlier one.
 
-By far the most common data types in dist are strings and arrays of
-strings.  Instead of using char* and char**, though, dist uses two named
-data structures, Buf and Vec, which own all the data they point at.
-The Buf operations are functions beginning with b; the Vec operations
-are functions beginning with v.  The basic form of any function declaring
-Bufs or Vecs on the stack should be
+The process to install Go 1.x, for x ≥ 5, is:
 
-       void
-       myfunc(void)
-       {
-               Buf b1, b2;
-               Vec v1;
-               
-               binit(&b1);
-               binit(&b2);
-               vinit(&v1);
-               
-               ... main code ...
-               bprintf(&b1, "hello, world");
-               vadd(&v1, bstr(&b1));  // v1 takes a copy of its argument
-               bprintf(&b2, "another string");
-               vadd(&v1, bstr(&b2));  // v1 now has two strings
-               
-               bfree(&b1);
-               bfree(&b2);
-               vfree(&v1);
-       }
-       
-The binit/vinit calls prepare a buffer or vector for use, initializing the 
-data structures, and the bfree/vfree calls free any memory they are still
-holding onto.  Use of this idiom gives us lexically scoped allocations.
+1. Build cmd/dist with Go 1.4.
+2. Using dist, build Go 1.x compiler toolchain with Go 1.4.
+3. Using dist, rebuild Go 1.x compiler toolchain with itself.
+4. Using dist, build Go 1.x cmd/go (as go_bootstrap) with Go 1.x compiler toolchain.
+5. Using go_bootstrap, build the remaining Go 1.x standard library and commands.
 
+NOTE: During the transition from the old C-based toolchain to the Go-based one,
+step 2 also builds the parts of the toolchain written in C, and step 3 does not
+recompile those.
+
+Because of backward compatibility, although the steps above say Go 1.4,
+in practice any release ≥ Go 1.4 but < Go 1.x will work as the bootstrap base.
+
+See golang.org/s/go15bootstrap for more details.
+
+Compared to Go 1.4 and earlier, dist will also take over much of what used to
+be done by make.bash/make.bat/make.rc and all of what used to be done by
+run.bash/run.bat/run.rc, because it is nicer to implement that logic in Go
+than in three different scripting languages simultaneously.
index e4b8b58e4373acb7de43a9f6fa6353efbbfe6091..9e4d1e3c22aef85b803a5baeb0deb6451369ebaa 100644 (file)
@@ -2,47 +2,58 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-#include "a.h"
-#include "arg.h"
+package main
 
-/*
- * Initialization for any invocation.
- */
+import (
+       "bytes"
+       "flag"
+       "fmt"
+       "os"
+       "path/filepath"
+       "runtime"
+       "strings"
+)
+
+// Initialization for any invocation.
 
 // The usual variables.
-char *goarch;
-char *gobin;
-char *gohostarch;
-char *gohostchar;
-char *gohostos;
-char *goos;
-char *goarm;
-char *go386;
-char *goroot = GOROOT_FINAL;
-char *goroot_final = GOROOT_FINAL;
-char *goextlinkenabled = "";
-char *workdir;
-char *tooldir;
-char *gochar;
-char *goversion;
-char *slash;   // / for unix, \ for windows
-char *defaultcc;
-char *defaultcflags;
-char *defaultldflags;
-char *defaultcxxtarget;
-char *defaultcctarget;
-bool   rebuildall;
-bool defaultclang;
-
-static bool shouldbuild(char*, char*);
-static void dopack(char*, char*, char**, int);
-static char *findgoversion(void);
+var (
+       goarch           string
+       gobin            string
+       gohostarch       string
+       gohostchar       string
+       gohostos         string
+       goos             string
+       goarm            string
+       go386            string
+       goroot           string
+       goroot_final     string
+       goextlinkenabled string
+       workdir          string
+       tooldir          string
+       gochar           string
+       goversion        string
+       oldgoos          string
+       oldgoarch        string
+       oldgochar        string
+       slash            string
+       defaultcc        string
+       defaultcflags    string
+       defaultldflags   string
+       defaultcxxtarget string
+       defaultcctarget  string
+       rebuildall       bool
+       defaultclang     bool
+
+       sflag bool // build static binaries
+       vflag int  // verbosity
+)
 
 // The known architecture letters.
-static char *gochars = "566899";
+var gochars = "566899"
 
 // The known architectures.
-static char *okgoarch[] = {
+var okgoarch = []string{
        // same order as gochars
        "arm",
        "amd64",
@@ -50,10 +61,10 @@ static char *okgoarch[] = {
        "386",
        "ppc64",
        "ppc64le",
-};
+}
 
 // The known operating systems.
-static char *okgoos[] = {
+var okgoos = []string{
        "darwin",
        "dragonfly",
        "linux",
@@ -65,294 +76,252 @@ static char *okgoos[] = {
        "openbsd",
        "plan9",
        "windows",
-};
-
-static void rmworkdir(void);
+}
 
 // find reports the first index of p in l[0:n], or else -1.
-int
-find(char *p, char **l, int n)
-{
-       int i;
-
-       for(i=0; i<n; i++)
-               if(streq(p, l[i]))
-                       return i;
-       return -1;
+func find(p string, l []string) int {
+       for i, s := range l {
+               if p == s {
+                       return i
+               }
+       }
+       return -1
 }
 
-// init handles initialization of the various global state, like goroot and goarch.
-void
-init(void)
-{
-       char *p;
-       int i;
-       Buf b;
-
-       binit(&b);
-
-       xgetenv(&b, "GOROOT");
-       if(b.len > 0) {
-               // if not "/", then strip trailing path separator
-               if(b.len >= 2 && b.p[b.len - 1] == slash[0])
-                       b.len--;
-               goroot = btake(&b);
-       }
-
-       xgetenv(&b, "GOBIN");
-       if(b.len == 0)
-               bprintf(&b, "%s%sbin", goroot, slash);
-       gobin = btake(&b);
-
-       xgetenv(&b, "GOOS");
-       if(b.len == 0)
-               bwritestr(&b, gohostos);
-       goos = btake(&b);
-       if(find(goos, okgoos, nelem(okgoos)) < 0)
-               fatal("unknown $GOOS %s", goos);
-
-       xgetenv(&b, "GOARM");
-       if(b.len == 0)
-               bwritestr(&b, xgetgoarm());
-       goarm = btake(&b);
-
-       xgetenv(&b, "GO386");
-       if(b.len == 0) {
-               if(cansse2())
-                       bwritestr(&b, "sse2");
-               else
-                       bwritestr(&b, "387");
-       }
-       go386 = btake(&b);
-
-       p = bpathf(&b, "%s/include/u.h", goroot);
-       if(!isfile(p)) {
-               fatal("$GOROOT is not set correctly or not exported\n"
-                       "\tGOROOT=%s\n"
-                       "\t%s does not exist", goroot, p);
-       }
-
-       xgetenv(&b, "GOHOSTARCH");
-       if(b.len > 0)
-               gohostarch = btake(&b);
-
-       i = find(gohostarch, okgoarch, nelem(okgoarch));
-       if(i < 0)
-               fatal("unknown $GOHOSTARCH %s", gohostarch);
-       bprintf(&b, "%c", gochars[i]);
-       gohostchar = btake(&b);
-
-       xgetenv(&b, "GOARCH");
-       if(b.len == 0)
-               bwritestr(&b, gohostarch);
-       goarch = btake(&b);
-       i = find(goarch, okgoarch, nelem(okgoarch));
-       if(i < 0)
-               fatal("unknown $GOARCH %s", goarch);
-       bprintf(&b, "%c", gochars[i]);
-       gochar = btake(&b);
-
-       xgetenv(&b, "GO_EXTLINK_ENABLED");
-       if(b.len > 0) {
-               goextlinkenabled = btake(&b);
-               if(!streq(goextlinkenabled, "0") && !streq(goextlinkenabled, "1"))
-                       fatal("unknown $GO_EXTLINK_ENABLED %s", goextlinkenabled);
-       }
-       
-       xgetenv(&b, "CC");
-       if(b.len == 0) {
+// xinit handles initialization of the various global state, like goroot and goarch.
+func xinit() {
+       goroot = os.Getenv("GOROOT")
+       if slash == "/" && len(goroot) > 1 || slash == `\` && len(goroot) > 3 {
+               // if not "/" or "c:\", then strip trailing path separator
+               goroot = strings.TrimSuffix(goroot, slash)
+       }
+       if goroot == "" {
+               fatal("$GOROOT must be set")
+       }
+
+       goroot_final = os.Getenv("GOROOT_FINAL")
+       if goroot_final == "" {
+               goroot_final = goroot
+       }
+
+       b := os.Getenv("GOBIN")
+       if b == "" {
+               b = goroot + slash + "bin"
+       }
+       gobin = b
+
+       b = os.Getenv("GOOS")
+       if b == "" {
+               b = gohostos
+       }
+       goos = b
+       if find(goos, okgoos) < 0 {
+               fatal("unknown $GOOS %s", goos)
+       }
+
+       b = os.Getenv("GOARM")
+       if b == "" {
+               b = xgetgoarm()
+       }
+       goarm = b
+
+       b = os.Getenv("GO386")
+       if b == "" {
+               if cansse2() {
+                       b = "sse2"
+               } else {
+                       b = "387"
+               }
+       }
+       go386 = b
+
+       p := pathf("%s/include/u.h", goroot)
+       if !isfile(p) {
+               fatal("$GOROOT is not set correctly or not exported\n"+
+                       "\tGOROOT=%s\n"+
+                       "\t%s does not exist", goroot, p)
+       }
+
+       b = os.Getenv("GOHOSTARCH")
+       if b != "" {
+               gohostarch = b
+       }
+
+       i := find(gohostarch, okgoarch)
+       if i < 0 {
+               fatal("unknown $GOHOSTARCH %s", gohostarch)
+       }
+       gohostchar = gochars[i : i+1]
+
+       b = os.Getenv("GOARCH")
+       if b == "" {
+               b = gohostarch
+       }
+       goarch = b
+       i = find(goarch, okgoarch)
+       if i < 0 {
+               fatal("unknown $GOARCH %s", goarch)
+       }
+       gochar = gochars[i : i+1]
+
+       b = os.Getenv("GO_EXTLINK_ENABLED")
+       if b != "" {
+               if b != "0" && b != "1" {
+                       fatal("unknown $GO_EXTLINK_ENABLED %s", b)
+               }
+               goextlinkenabled = b
+       }
+
+       b = os.Getenv("CC")
+       if b == "" {
                // Use clang on OS X, because gcc is deprecated there.
                // Xcode for OS X 10.9 Mavericks will ship a fake "gcc" binary that
                // actually runs clang. We prepare different command
                // lines for the two binaries, so it matters what we call it.
                // See golang.org/issue/5822.
-               if(defaultclang)
-                       bprintf(&b, "clang");
-               else
-                       bprintf(&b, "gcc");
+               if defaultclang {
+                       b = "clang"
+               } else {
+                       b = "gcc"
+               }
        }
-       defaultcc = btake(&b);
+       defaultcc = b
 
-       xgetenv(&b, "CFLAGS");
-       defaultcflags = btake(&b);
+       defaultcflags = os.Getenv("CFLAGS")
 
-       xgetenv(&b, "LDFLAGS");
-       defaultldflags = btake(&b);
+       defaultldflags = os.Getenv("LDFLAGS")
 
-       xgetenv(&b, "CC_FOR_TARGET");
-       if(b.len == 0) {
-               bprintf(&b, defaultcc);
+       b = os.Getenv("CC_FOR_TARGET")
+       if b == "" {
+               b = defaultcc
        }
-       defaultcctarget = btake(&b);
-
-       xgetenv(&b, "CXX_FOR_TARGET");
-       if(b.len == 0) {
-               xgetenv(&b, "CXX");
-               if(b.len == 0) {
-                       if(defaultclang)
-                               bprintf(&b, "clang++");
-                       else
-                               bprintf(&b, "g++");
+       defaultcctarget = b
+
+       b = os.Getenv("CXX_FOR_TARGET")
+       if b == "" {
+               b = os.Getenv("CXX")
+               if b == "" {
+                       if defaultclang {
+                               b = "clang++"
+                       } else {
+                               b = "g++"
+                       }
                }
        }
-       defaultcxxtarget = btake(&b);
-
-       xsetenv("GOROOT", goroot);
-       xsetenv("GOARCH", goarch);
-       xsetenv("GOOS", goos);
-       xsetenv("GOARM", goarm);
-       xsetenv("GO386", go386);
+       defaultcxxtarget = b
+
+       // For tools being invoked but also for os.ExpandEnv.
+       os.Setenv("GO386", go386)
+       os.Setenv("GOARCH", goarch)
+       os.Setenv("GOARM", goarm)
+       os.Setenv("GOHOSTARCH", gohostarch)
+       os.Setenv("GOHOSTOS", gohostos)
+       os.Setenv("GOOS", goos)
+       os.Setenv("GOROOT", goroot)
+       os.Setenv("GOROOT_FINAL", goroot_final)
 
        // Make the environment more predictable.
-       xsetenv("LANG", "C");
-       xsetenv("LANGUAGE", "en_US.UTF8");
-
-       goversion = findgoversion();
+       os.Setenv("LANG", "C")
+       os.Setenv("LANGUAGE", "en_US.UTF8")
 
-       workdir = xworkdir();
-       xatexit(rmworkdir);
+       goversion = findgoversion()
 
-       bpathf(&b, "%s/pkg/tool/%s_%s", goroot, gohostos, gohostarch);
-       tooldir = btake(&b);
+       workdir = xworkdir()
+       xatexit(rmworkdir)
 
-       bfree(&b);
+       tooldir = pathf("%s/pkg/tool/%s_%s", goroot, gohostos, gohostarch)
 }
 
 // rmworkdir deletes the work directory.
-static void
-rmworkdir(void)
-{
-       if(vflag > 1)
-               errprintf("rm -rf %s\n", workdir);
-       xremoveall(workdir);
+func rmworkdir() {
+       if vflag > 1 {
+               errprintf("rm -rf %s\n", workdir)
+       }
+       xremoveall(workdir)
 }
 
 // Remove trailing spaces.
-static void
-chomp(Buf *b)
-{
-       int c;
-
-       while(b->len > 0 && ((c=b->p[b->len-1]) == ' ' || c == '\t' || c == '\r' || c == '\n'))
-               b->len--;
+func chomp(s string) string {
+       return strings.TrimRight(s, " \t\r\n")
 }
 
-static char*
-branchtag(char *branch, bool *precise)
-{
-       char *tag, *p, *q;
-       int i;
-       Buf b, arg;
-       Vec tags;
-
-       binit(&b);
-       binit(&arg);
-       vinit(&tags);
-
-       bprintf(&arg, "master..%s", branch);
-       run(&b, goroot, CheckExit, "git", "log", "--decorate=full", "--format=format:%d", bstr(&arg), nil);
-
-       splitlines(&tags, bstr(&b));
-       tag = branch;
-       for(i=0; i < tags.len; i++) {
+func branchtag(branch string) (tag string, precise bool) {
+       b := run(goroot, CheckExit, "git", "log", "--decorate=full", "--format=format:%d", "master.."+branch)
+       tag = branch
+       for _, line := range splitlines(b) {
                // Each line is either blank, or looks like
                //        (tag: refs/tags/go1.4rc2, refs/remotes/origin/release-branch.go1.4, refs/heads/release-branch.go1.4)
                // We need to find an element starting with refs/tags/.
-               p = xstrstr(tags.p[i], " refs/tags/");
-               if(p == nil)
-                       continue;
-               p += xstrlen(" refs/tags/");
+               i := strings.Index(line, " refs/tags/")
+               if i < 0 {
+                       continue
+               }
+               i += len(" refs/tags/")
                // The tag name ends at a comma or paren (prefer the first).
-               q = xstrstr(p, ",");
-               if(q == nil)
-                       q = xstrstr(p, ")");
-               if(q == nil)
-                       continue;  // malformed line; ignore it
-               *q = '\0';
-               tag = xstrdup(p);
-               if(i == 0)
-                       *precise = 1;  // tag denotes HEAD
-               break;
-       }
-
-       bfree(&b);
-       bfree(&arg);
-       vfree(&tags);
-       return tag;
+               j := strings.Index(line[i:], ",")
+               if j < 0 {
+                       j = strings.Index(line[i:], ")")
+               }
+               if j < 0 {
+                       continue // malformed line; ignore it
+               }
+               tag = line[i : i+j]
+               if i == 0 {
+                       precise = true // tag denotes HEAD
+               }
+               break
+       }
+       return
 }
 
 // findgoversion determines the Go version to use in the version string.
-static char*
-findgoversion(void)
-{
-       char *tag, *p;
-       bool precise;
-       Buf b, path, bmore, branch;
-
-       binit(&b);
-       binit(&path);
-       binit(&bmore);
-       binit(&branch);
-
+func findgoversion() string {
        // The $GOROOT/VERSION file takes priority, for distributions
        // without the source repo.
-       bpathf(&path, "%s/VERSION", goroot);
-       if(isfile(bstr(&path))) {
-               readfile(&b, bstr(&path));
-               chomp(&b);
+       path := pathf("%s/VERSION", goroot)
+       if isfile(path) {
+               b := chomp(readfile(path))
                // Commands such as "dist version > VERSION" will cause
                // the shell to create an empty VERSION file and set dist's
                // stdout to its fd. dist in turn looks at VERSION and uses
                // its content if available, which is empty at this point.
-               if(b.len > 0)
-                       goto done;
+               // Only use the VERSION file if it is non-empty.
+               if b != "" {
+                       return b
+               }
        }
 
        // The $GOROOT/VERSION.cache file is a cache to avoid invoking
        // git every time we run this command.  Unlike VERSION, it gets
        // deleted by the clean command.
-       bpathf(&path, "%s/VERSION.cache", goroot);
-       if(isfile(bstr(&path))) {
-               readfile(&b, bstr(&path));
-               chomp(&b);
-               goto done;
+       path = pathf("%s/VERSION.cache", goroot)
+       if isfile(path) {
+               return chomp(readfile(path))
        }
 
        // Otherwise, use Git.
        // What is the current branch?
-       run(&branch, goroot, CheckExit, "git", "rev-parse", "--abbrev-ref", "HEAD", nil);
-       chomp(&branch);
+       branch := chomp(run(goroot, CheckExit, "git", "rev-parse", "--abbrev-ref", "HEAD"))
 
        // What are the tags along the current branch?
-       tag = "devel";
-       precise = 0;
+       tag := "devel"
+       precise := false
 
        // If we're on a release branch, use the closest matching tag
        // that is on the release branch (and not on the master branch).
-       if(hasprefix(bstr(&branch), "release-branch."))
-               tag = branchtag(bstr(&branch), &precise);
+       if strings.HasPrefix(branch, "release-branch.") {
+               tag, precise = branchtag(branch)
+       }
 
-       bprintf(&b, "%s", tag);
-       if(!precise) {
+       if !precise {
                // Tag does not point at HEAD; add hash and date to version.
-               run(&bmore, goroot, CheckExit, "git", "log", "-n", "1", "--format=format: +%h %cd", "HEAD", nil);
-               chomp(&bmore);
-               bwriteb(&b, &bmore);
+               tag += chomp(run(goroot, CheckExit, "git", "log", "-n", "1", "--format=format: +%h %cd", "HEAD"))
        }
 
        // Cache version.
-       writefile(&b, bstr(&path), 0);
-
-done:
-       p = btake(&b);
+       writefile(tag, path, 0)
 
-
-       bfree(&b);
-       bfree(&path);
-       bfree(&bmore);
-       bfree(&branch);
-
-       return p;
+       return tag
 }
 
 /*
@@ -360,7 +329,7 @@ done:
  */
 
 // The old tools that no longer live in $GOBIN or $GOROOT/bin.
-static char *oldtool[] = {
+var oldtool = []string{
        "5a", "5c", "5g", "5l",
        "6a", "6c", "6g", "6l",
        "8a", "8c", "8g", "8l",
@@ -381,44 +350,40 @@ static char *oldtool[] = {
        "govet",
        "goyacc",
        "quietgcc",
-};
+}
 
 // Unreleased directories (relative to $GOROOT) that should
 // not be in release branches.
-static char *unreleased[] = {
+var unreleased = []string{
        "src/cmd/link",
        "src/debug/goobj",
        "src/old",
-};
+}
 
 // setup sets up the tree for the initial build.
-static void
-setup(void)
-{
-       int i;
-       Buf b;
-       char *p;
-
-       binit(&b);
-
+func setup() {
        // Create bin directory.
-       p = bpathf(&b, "%s/bin", goroot);
-       if(!isdir(p))
-               xmkdir(p);
+       if p := pathf("%s/bin", goroot); !isdir(p) {
+               xmkdir(p)
+       }
 
        // Create package directory.
-       p = bpathf(&b, "%s/pkg", goroot);
-       if(!isdir(p))
-               xmkdir(p);
-       p = bpathf(&b, "%s/pkg/%s_%s", goroot, gohostos, gohostarch);
-       if(rebuildall)
-               xremoveall(p);
-       xmkdirall(p);
-       if(!streq(goos, gohostos) || !streq(goarch, gohostarch)) {
-               p = bpathf(&b, "%s/pkg/%s_%s", goroot, goos, goarch);
-               if(rebuildall)
-                       xremoveall(p);
-               xmkdirall(p);
+       if p := pathf("%s/pkg", goroot); !isdir(p) {
+               xmkdir(p)
+       }
+
+       p := pathf("%s/pkg/%s_%s", goroot, gohostos, gohostarch)
+       if rebuildall {
+               xremoveall(p)
+       }
+       xmkdirall(p)
+
+       if goos != gohostos || goarch != gohostarch {
+               p := pathf("%s/pkg/%s_%s", goroot, goos, goarch)
+               if rebuildall {
+                       xremoveall(p)
+               }
+               xmkdirall(p)
        }
 
        // Create object directory.
@@ -426,44 +391,48 @@ setup(void)
        // are in one tree.  If pkg/obj/libgc.a exists, it is a dreg from
        // before we used subdirectories of obj.  Delete all of obj
        // to clean up.
-       bpathf(&b, "%s/pkg/obj/libgc.a", goroot);
-       if(isfile(bstr(&b)))
-               xremoveall(bpathf(&b, "%s/pkg/obj", goroot));
-       p = bpathf(&b, "%s/pkg/obj/%s_%s", goroot, gohostos, gohostarch);
-       if(rebuildall)
-               xremoveall(p);
-       xmkdirall(p);
+       if p := pathf("%s/pkg/obj/libgc.a", goroot); isfile(p) {
+               xremoveall(pathf("%s/pkg/obj", goroot))
+       }
+       p = pathf("%s/pkg/obj/%s_%s", goroot, gohostos, gohostarch)
+       if rebuildall {
+               xremoveall(p)
+       }
+       xmkdirall(p)
 
        // Create tool directory.
        // We keep it in pkg/, just like the object directory above.
-       if(rebuildall)
-               xremoveall(tooldir);
-       xmkdirall(tooldir);
+       if rebuildall {
+               xremoveall(tooldir)
+       }
+       xmkdirall(tooldir)
 
        // Remove tool binaries from before the tool/gohostos_gohostarch
-       xremoveall(bpathf(&b, "%s/bin/tool", goroot));
+       xremoveall(pathf("%s/bin/tool", goroot))
 
        // Remove old pre-tool binaries.
-       for(i=0; i<nelem(oldtool); i++)
-               xremove(bpathf(&b, "%s/bin/%s", goroot, oldtool[i]));
+       for _, old := range oldtool {
+               xremove(pathf("%s/bin/%s", goroot, old))
+       }
 
        // If $GOBIN is set and has a Go compiler, it must be cleaned.
-       for(i=0; gochars[i]; i++) {
-               if(isfile(bprintf(&b, "%s%s%c%s", gobin, slash, gochars[i], "g"))) {
-                       for(i=0; i<nelem(oldtool); i++)
-                               xremove(bprintf(&b, "%s%s%s", gobin, slash, oldtool[i]));
-                       break;
+       for _, char := range gochars {
+               if isfile(pathf("%s%s%c%s", gobin, slash, char, "g")) {
+                       for _, old := range oldtool {
+                               xremove(pathf("%s/%s", gobin, old))
+                       }
+                       break
                }
        }
 
        // For release, make sure excluded things are excluded.
-       if(hasprefix(goversion, "release.") || (hasprefix(goversion, "go") && !contains(goversion, "beta"))) {
-               for(i=0; i<nelem(unreleased); i++)
-                       if(isdir(bpathf(&b, "%s/%s", goroot, unreleased[i])))
-                               fatal("%s should not exist in release build", bstr(&b));
+       if strings.HasPrefix(goversion, "release.") || (strings.HasPrefix(goversion, "go") && !strings.Contains(goversion, "beta")) {
+               for _, dir := range unreleased {
+                       if p := pathf("%s/%s", goroot, dir); isdir(p) {
+                               fatal("%s should not exist in release build", p)
+                       }
+               }
        }
-
-       bfree(&b);
 }
 
 /*
@@ -471,7 +440,7 @@ setup(void)
  */
 
 // gccargs is the gcc command line to use for compiling a single C file.
-static char *proto_gccargs[] = {
+var proto_gccargs = []string{
        "-Wall",
        // native Plan 9 compilers don't like non-standard prototypes
        // so let gcc catch them.
@@ -489,34 +458,36 @@ static char *proto_gccargs[] = {
        "-fno-common",
        "-ggdb",
        "-pipe",
-};
+}
 
 // gccargs2 is the second part of gccargs.
 // it is used if the environment isn't defining CFLAGS.
-static char *proto_gccargs2[] = {
+var proto_gccargs2 = []string{
        // on older versions of GCC, -Wuninitialized is not supported
        // without -O, so put it here together with -O settings in case
        // the user's $CFLAGS doesn't include -O.
        "-Wuninitialized",
-#if defined(__NetBSD__) && defined(__arm__)
-       // GCC 4.5.4 (NetBSD nb1 20120916) on ARM is known to mis-optimize gc/mparith3.c
-       // Fix available at http://patchwork.ozlabs.org/patch/64562/.
-       "-O1",
-#else
        "-O2",
-#endif
-};
+}
+
+func init() {
+       if runtime.GOOS == "netbsd" && runtime.GOARCH == "arm" {
+               // GCC 4.5.4 (NetBSD nb1 20120916) on ARM is known to mis-optimize gc/mparith3.c
+               // Fix available at http://patchwork.ozlabs.org/patch/64562/.
+               proto_gccargs2[1] = "-O1"
+       }
+}
 
-static Vec gccargs, ldargs;
+var gccargs, ldargs []string
 
 // deptab lists changes to the default dependencies for a given prefix.
 // deps ending in /* read the whole directory; deps beginning with -
 // exclude files with that prefix.
-static struct {
-       char *prefix;  // prefix of target
-       char *dep[20];  // dependency tweaks for targets with that prefix
-} deptab[] = {
-       {"lib9", {
+var deptab = []struct {
+       prefix string   // prefix of target
+       dep    []string // dependency tweaks for targets with that prefix
+}{
+       {"lib9", []string{
                "$GOROOT/include/u.h",
                "$GOROOT/include/utf.h",
                "$GOROOT/include/fmt.h",
@@ -524,14 +495,14 @@ static struct {
                "fmt/*",
                "utf/*",
        }},
-       {"libbio", {
+       {"libbio", []string{
                "$GOROOT/include/u.h",
                "$GOROOT/include/utf.h",
                "$GOROOT/include/fmt.h",
                "$GOROOT/include/libc.h",
                "$GOROOT/include/bio.h",
        }},
-       {"liblink", {
+       {"liblink", []string{
                "$GOROOT/include/u.h",
                "$GOROOT/include/utf.h",
                "$GOROOT/include/fmt.h",
@@ -544,85 +515,85 @@ static struct {
                "anames8.c",
                "anames9.c",
        }},
-       {"cmd/gc", {
+       {"cmd/gc", []string{
                "-cplx.c",
                "-pgen.c",
                "-plive.c",
                "-popt.c",
-               "-y1.tab.c",  // makefile dreg
+               "-y1.tab.c", // makefile dreg
                "opnames.h",
        }},
-       {"cmd/5g", {
+       {"cmd/5g", []string{
                "../gc/cplx.c",
                "../gc/pgen.c",
                "../gc/plive.c",
                "../gc/popt.c",
                "../gc/popt.h",
-               "$GOROOT/pkg/obj/$GOHOSTOS_$GOHOSTARCH/libgc.a",
+               "$GOROOT/pkg/obj/${GOHOSTOS}_$GOHOSTARCH/libgc.a",
        }},
-       {"cmd/6g", {
+       {"cmd/6g", []string{
                "../gc/cplx.c",
                "../gc/pgen.c",
                "../gc/plive.c",
                "../gc/popt.c",
                "../gc/popt.h",
-               "$GOROOT/pkg/obj/$GOHOSTOS_$GOHOSTARCH/libgc.a",
+               "$GOROOT/pkg/obj/${GOHOSTOS}_$GOHOSTARCH/libgc.a",
        }},
-       {"cmd/8g", {
+       {"cmd/8g", []string{
                "../gc/cplx.c",
                "../gc/pgen.c",
                "../gc/plive.c",
                "../gc/popt.c",
                "../gc/popt.h",
-               "$GOROOT/pkg/obj/$GOHOSTOS_$GOHOSTARCH/libgc.a",
+               "$GOROOT/pkg/obj/${GOHOSTOS}_$GOHOSTARCH/libgc.a",
        }},
-       {"cmd/9g", {
+       {"cmd/9g", []string{
                "../gc/cplx.c",
                "../gc/pgen.c",
                "../gc/plive.c",
                "../gc/popt.c",
                "../gc/popt.h",
-               "$GOROOT/pkg/obj/$GOHOSTOS_$GOHOSTARCH/libgc.a",
+               "$GOROOT/pkg/obj/${GOHOSTOS}_$GOHOSTARCH/libgc.a",
        }},
-       {"cmd/5l", {
+       {"cmd/5l", []string{
                "../ld/*",
        }},
-       {"cmd/6l", {
+       {"cmd/6l", []string{
                "../ld/*",
        }},
-       {"cmd/8l", {
+       {"cmd/8l", []string{
                "../ld/*",
        }},
-       {"cmd/9l", {
+       {"cmd/9l", []string{
                "../ld/*",
        }},
-       {"cmd/go", {
+       {"cmd/go", []string{
                "zdefaultcc.go",
        }},
-       {"cmd/", {
-               "$GOROOT/pkg/obj/$GOHOSTOS_$GOHOSTARCH/liblink.a",
-               "$GOROOT/pkg/obj/$GOHOSTOS_$GOHOSTARCH/libbio.a",
-               "$GOROOT/pkg/obj/$GOHOSTOS_$GOHOSTARCH/lib9.a",
+       {"cmd/", []string{
+               "$GOROOT/pkg/obj/${GOHOSTOS}_$GOHOSTARCH/liblink.a",
+               "$GOROOT/pkg/obj/${GOHOSTOS}_$GOHOSTARCH/libbio.a",
+               "$GOROOT/pkg/obj/${GOHOSTOS}_$GOHOSTARCH/lib9.a",
        }},
-       {"runtime", {
+       {"runtime", []string{
                "zaexperiment.h",
                "zversion.go",
        }},
-};
+}
 
 // depsuffix records the allowed suffixes for source files.
-char *depsuffix[] = {
+var depsuffix = []string{
        ".c",
        ".h",
        ".s",
        ".go",
-};
+}
 
 // gentab records how to generate some trivial files.
-static struct {
-       char *nameprefix;
-       void (*gen)(char*, char*);
-} gentab[] = {
+var gentab = []struct {
+       nameprefix string
+       gen        func(string, string)
+}{
        {"opnames.h", gcopnames},
        {"anames5.c", mkanames},
        {"anames6.c", mkanames},
@@ -634,272 +605,238 @@ static struct {
 
        // not generated anymore, but delete the file if we see it
        {"enam.c", nil},
-};
+}
 
 // install installs the library, package, or binary associated with dir,
 // which is relative to $GOROOT/src.
-static void
-install(char *dir)
-{
-       char *name, *p, *elem, *prefix, *exe;
-       bool islib, ispkg, isgo, stale, ispackcmd;
-       Buf b, b1, path, final_path, final_name, archive;
-       Vec compile, files, link, go, missing, clean, lib, extra;
-       Time ttarg, t;
-       int i, j, k, n, doclean, targ;
-
-       if(vflag) {
-               if(!streq(goos, gohostos) || !streq(goarch, gohostarch))
-                       errprintf("%s (%s/%s)\n", dir, goos, goarch);
-               else
-                       errprintf("%s\n", dir);
-       }
-
-       binit(&b);
-       binit(&b1);
-       binit(&path);
-       binit(&final_path);
-       binit(&final_name);
-       binit(&archive);
-       vinit(&compile);
-       vinit(&files);
-       vinit(&link);
-       vinit(&go);
-       vinit(&missing);
-       vinit(&clean);
-       vinit(&lib);
-       vinit(&extra);
+func install(dir string) {
+       if vflag > 0 {
+               if goos != gohostos || goarch != gohostarch {
+                       errprintf("%s (%s/%s)\n", dir, goos, goarch)
+               } else {
+                       errprintf("%s\n", dir)
+               }
+       }
 
+       var clean []string
+       defer func() {
+               for _, name := range clean {
+                       xremove(name)
+               }
+       }()
 
        // path = full path to dir.
-       bpathf(&path, "%s/src/%s", goroot, dir);
-       bpathf(&final_path, "%s/src/%s", goroot_final, dir);
-       name = lastelem(dir);
+       path := pathf("%s/src/%s", goroot, dir)
+       name := filepath.Base(dir)
 
        // set up gcc command line on first run.
-       if(gccargs.len == 0) {
-               bprintf(&b, "%s %s", defaultcc, defaultcflags);
-               splitfields(&gccargs, bstr(&b));
-               for(i=0; i<nelem(proto_gccargs); i++)
-                       vadd(&gccargs, proto_gccargs[i]);
-               if(defaultcflags[0] == '\0') {
-                       for(i=0; i<nelem(proto_gccargs2); i++)
-                               vadd(&gccargs, proto_gccargs2[i]);
+       if gccargs == nil {
+               gccargs = splitfields(defaultcc + " " + defaultcflags)
+               gccargs = append(gccargs, proto_gccargs...)
+               if defaultcflags == "" {
+                       gccargs = append(gccargs, proto_gccargs2...)
                }
-               if(contains(gccargs.p[0], "clang")) {
+               if strings.Contains(gccargs[0], "clang") {
                        // disable ASCII art in clang errors, if possible
-                       vadd(&gccargs, "-fno-caret-diagnostics");
+                       gccargs = append(gccargs, "-fno-caret-diagnostics")
                        // clang is too smart about unused command-line arguments
-                       vadd(&gccargs, "-Qunused-arguments");
+                       gccargs = append(gccargs, "-Qunused-arguments")
                }
                // disable word wrapping in error messages
-               vadd(&gccargs, "-fmessage-length=0");
-               if(streq(gohostos, "darwin")) {
+               gccargs = append(gccargs, "-fmessage-length=0")
+               if gohostos == "darwin" {
                        // golang.org/issue/5261
-                       vadd(&gccargs, "-mmacosx-version-min=10.6");
+                       gccargs = append(gccargs, "-mmacosx-version-min=10.6")
                }
        }
-       if(ldargs.len == 0 && defaultldflags[0] != '\0') {
-               bprintf(&b, "%s", defaultldflags);
-               splitfields(&ldargs, bstr(&b));
+       if ldargs == nil && defaultldflags != "" {
+               ldargs = splitfields(defaultldflags)
        }
 
-       islib = hasprefix(dir, "lib") || streq(dir, "cmd/gc");
-       ispkg = !islib && !hasprefix(dir, "cmd/");
-       isgo = ispkg || streq(dir, "cmd/go") || streq(dir, "cmd/cgo");
+       islib := strings.HasPrefix(dir, "lib") || dir == "cmd/gc"
+       ispkg := !islib && !strings.HasPrefix(dir, "cmd/")
+       isgo := ispkg || dir == "cmd/go" || dir == "cmd/cgo"
 
-       exe = "";
-       if(streq(gohostos, "windows"))
-               exe = ".exe";
+       exe := ""
+       if gohostos == "windows" {
+               exe = ".exe"
+       }
 
        // Start final link command line.
        // Note: code below knows that link.p[targ] is the target.
-       ispackcmd = 0;
-       if(islib) {
+       var (
+               link      []string
+               targ      int
+               ispackcmd bool
+       )
+       switch {
+       case islib:
                // C library.
-               vadd(&link, "ar");
-               if(streq(gohostos, "plan9"))
-                       vadd(&link, "rc");
-               else
-                       vadd(&link, "rsc");
-               prefix = "";
-               if(!hasprefix(name, "lib"))
-                       prefix = "lib";
-               targ = link.len;
-               vadd(&link, bpathf(&b, "%s/pkg/obj/%s_%s/%s%s.a", goroot, gohostos, gohostarch, prefix, name));
-       } else if(ispkg) {
+               prefix := ""
+               if !strings.HasPrefix(name, "lib") {
+                       prefix = "lib"
+               }
+               link = []string{"ar", "rsc", pathf("%s/pkg/obj/%s_%s/%s%s.a", goroot, gohostos, gohostarch, prefix, name)}
+               if gohostos == "plan9" {
+                       link[1] = "rc"
+               }
+               targ = len(link) - 1
+
+       case ispkg:
                // Go library (package).
-               ispackcmd = 1;
-               vadd(&link, "pack"); // program name - unused here, but all the other cases record one
-               p = bprintf(&b, "%s/pkg/%s_%s/%s", goroot, goos, goarch, dir);
-               *xstrrchr(p, '/') = '\0';
-               xmkdirall(p);
-               targ = link.len;
-               vadd(&link, bpathf(&b, "%s/pkg/%s_%s/%s.a", goroot, goos, goarch, dir));
-       } else if(streq(dir, "cmd/go") || streq(dir, "cmd/cgo")) {
+               ispackcmd = true
+               link = []string{"pack", pathf("%s/pkg/%s_%s/%s.a", goroot, goos, goarch, dir)}
+               targ = len(link) - 1
+               xmkdirall(filepath.Dir(link[targ]))
+
+       case dir == "cmd/go" || dir == "cmd/cgo":
                // Go command.
-               vadd(&link, bpathf(&b, "%s/%sl", tooldir, gochar));
-               vadd(&link, "-o");
-               elem = name;
-               if(streq(elem, "go"))
-                       elem = "go_bootstrap";
-               targ = link.len;
-               vadd(&link, bpathf(&b, "%s/%s%s", tooldir, elem, exe));
-       } else {
+               elem := name
+               if elem == "go" {
+                       elem = "go_bootstrap"
+               }
+               link = []string{fmt.Sprintf("%s/%sl", tooldir, gochar), "-o", pathf("%s/%s%s", tooldir, elem, exe)}
+               targ = len(link) - 1
+
+       default:
                // C command. Use gccargs and ldargs.
-               if(streq(gohostos, "plan9")) {
-                       vadd(&link, bprintf(&b, "%sl", gohostchar));
-                       vadd(&link, "-o");
-                       targ = link.len;
-                       vadd(&link, bpathf(&b, "%s/%s", tooldir, name));
+               if gohostos == "plan9" {
+                       link = []string{fmt.Sprintf("%sl", gohostchar), "-o", pathf("%s/%s", tooldir, name)}
+                       targ = len(link) - 1
                } else {
-                       vcopy(&link, gccargs.p, gccargs.len);
-                       vcopy(&link, ldargs.p, ldargs.len);
-                       if(sflag)
-                               vadd(&link, "-static");
-                       vadd(&link, "-o");
-                       targ = link.len;
-                       vadd(&link, bpathf(&b, "%s/%s%s", tooldir, name, exe));
-                       if(streq(gohostarch, "amd64"))
-                               vadd(&link, "-m64");
-                       else if(streq(gohostarch, "386"))
-                               vadd(&link, "-m32");
+                       link = append(link, gccargs...)
+                       link = append(link, ldargs...)
+                       if sflag {
+                               link = append(link, "-static")
+                       }
+                       link = append(link, "-o", pathf("%s/%s%s", tooldir, name, exe))
+                       targ = len(link) - 1
+                       switch gohostarch {
+                       case "amd64":
+                               link = append(link, "-m64")
+                       case "386":
+                               link = append(link, "-m32")
+                       }
                }
        }
-       ttarg = mtime(link.p[targ]);
+       ttarg := mtime(link[targ])
 
        // Gather files that are sources for this target.
        // Everything in that directory, and any target-specific
        // additions.
-       xreaddir(&files, bstr(&path));
+       files := xreaddir(path)
 
        // Remove files beginning with . or _,
        // which are likely to be editor temporary files.
        // This is the same heuristic build.ScanDir uses.
        // There do exist real C files beginning with _,
        // so limit that check to just Go files.
-       n = 0;
-       for(i=0; i<files.len; i++) {
-               p = files.p[i];
-               if(hasprefix(p, ".") || (hasprefix(p, "_") && hassuffix(p, ".go")))
-                       xfree(p);
-               else
-                       files.p[n++] = p;
-       }
-       files.len = n;
-
-       for(i=0; i<nelem(deptab); i++) {
-               if(streq(dir, deptab[i].prefix) ||
-                  (hassuffix(deptab[i].prefix, "/") && hasprefix(dir, deptab[i].prefix))) {
-                       for(j=0; (p=deptab[i].dep[j])!=nil; j++) {
-                               breset(&b1);
-                               bwritestr(&b1, p);
-                               bsubst(&b1, "$GOROOT", goroot);
-                               bsubst(&b1, "$GOOS", goos);
-                               bsubst(&b1, "$GOARCH", goarch);
-                               bsubst(&b1, "$GOHOSTOS", gohostos);
-                               bsubst(&b1, "$GOHOSTARCH", gohostarch);
-                               p = bstr(&b1);
-                               if(hassuffix(p, ".a")) {
-                                       vadd(&lib, bpathf(&b, "%s", p));
-                                       continue;
-                               }
-                               if(hassuffix(p, "/*")) {
-                                       bpathf(&b, "%s/%s", bstr(&path), p);
-                                       b.len -= 2;
-                                       xreaddir(&extra, bstr(&b));
-                                       bprintf(&b, "%s", p);
-                                       b.len -= 2;
-                                       for(k=0; k<extra.len; k++)
-                                               vadd(&files, bpathf(&b1, "%s/%s", bstr(&b), extra.p[k]));
-                                       continue;
-                               }
-                               if(hasprefix(p, "-")) {
-                                       p++;
-                                       n = 0;
-                                       for(k=0; k<files.len; k++) {
-                                               if(hasprefix(files.p[k], p))
-                                                       xfree(files.p[k]);
-                                               else
-                                                       files.p[n++] = files.p[k];
+       files = filter(files, func(p string) bool {
+               return !strings.HasPrefix(p, ".") && (!strings.HasPrefix(p, "_") || !strings.HasSuffix(p, ".go"))
+       })
+
+       var libs []string
+
+       for _, dt := range deptab {
+               if dir == dt.prefix || strings.HasSuffix(dt.prefix, "/") && strings.HasPrefix(dir, dt.prefix) {
+                       for _, p := range dt.dep {
+                               p = os.ExpandEnv(p)
+                               switch {
+                               case strings.HasSuffix(p, ".a"):
+                                       libs = append(libs, p)
+
+                               case strings.HasSuffix(p, "/*"):
+                                       dir := strings.TrimSuffix(p, "/*")
+                                       for _, name := range xreaddir(pathf("%s/%s", path, dir)) {
+                                               files = append(files, pathf("%s/%s", dir, name))
                                        }
-                                       files.len = n;
-                                       continue;
+
+                               case strings.HasPrefix(p, "-"):
+                                       files = filter(files, func(s string) bool {
+                                               return !strings.HasPrefix(s, p[1:])
+                                       })
+
+                               default:
+                                       files = append(files, p)
                                }
-                               vadd(&files, p);
                        }
                }
        }
-       vuniq(&files);
+       files = uniq(files)
 
        // Convert to absolute paths.
-       for(i=0; i<files.len; i++) {
-               if(!isabs(files.p[i])) {
-                       bpathf(&b, "%s/%s", bstr(&path), files.p[i]);
-                       xfree(files.p[i]);
-                       files.p[i] = btake(&b);
+       for i, p := range files {
+               if !isabs(p) {
+                       files[i] = pathf("%s/%s", path, p)
                }
        }
 
        // Is the target up-to-date?
-       stale = rebuildall;
-       n = 0;
-       for(i=0; i<files.len; i++) {
-               p = files.p[i];
-               for(j=0; j<nelem(depsuffix); j++)
-                       if(hassuffix(p, depsuffix[j]))
-                               goto ok;
-               xfree(files.p[i]);
-               continue;
+       var gofiles, missing []string
+       stale := rebuildall
+       files = filter(files, func(p string) bool {
+               for _, suf := range depsuffix {
+                       if strings.HasSuffix(p, suf) {
+                               goto ok
+                       }
+               }
+               return false
        ok:
-               t = mtime(p);
-               if(t != 0 && !hassuffix(p, ".a") && !shouldbuild(p, dir)) {
-                       xfree(files.p[i]);
-                       continue;
+               t := mtime(p)
+               if !t.IsZero() && !strings.HasSuffix(p, ".a") && !shouldbuild(p, dir) {
+                       return false
                }
-               if(hassuffix(p, ".go"))
-                       vadd(&go, p);
-               if(t > ttarg)
-                       stale = 1;
-               if(t == 0) {
-                       vadd(&missing, p);
-                       files.p[n++] = files.p[i];
-                       continue;
+               if strings.HasSuffix(p, ".go") {
+                       gofiles = append(gofiles, p)
                }
-               files.p[n++] = files.p[i];
-       }
-       files.len = n;
+               if t.After(ttarg) {
+                       stale = true
+               }
+               if t.IsZero() {
+                       missing = append(missing, p)
+               }
+               return true
+       })
 
        // If there are no files to compile, we're done.
-       if(files.len == 0)
-               goto out;
-       
-       for(i=0; i<lib.len && !stale; i++)
-               if(mtime(lib.p[i]) > ttarg)
-                       stale = 1;
+       if len(files) == 0 {
+               return
+       }
 
-       if(!stale)
-               goto out;
+       if !stale {
+               for _, p := range libs {
+                       if mtime(p).After(ttarg) {
+                               stale = true
+                               break
+                       }
+               }
+       }
+
+       if !stale {
+               return
+       }
 
        // For package runtime, copy some files into the work space.
-       if(streq(dir, "runtime")) {
-               copyfile(bpathf(&b, "%s/pkg/%s_%s/textflag.h", goroot, goos, goarch),
-                       bpathf(&b1, "%s/src/cmd/ld/textflag.h", goroot), 0);
-               copyfile(bpathf(&b, "%s/pkg/%s_%s/funcdata.h", goroot, goos, goarch),
-                       bpathf(&b1, "%s/src/runtime/funcdata.h", goroot), 0);
+       if dir == "runtime" {
+               // For use by assembly and C files.
+               copyfile(pathf("%s/pkg/%s_%s/textflag.h", goroot, goos, goarch),
+                       pathf("%s/src/cmd/ld/textflag.h", goroot), 0)
+               copyfile(pathf("%s/pkg/%s_%s/funcdata.h", goroot, goos, goarch),
+                       pathf("%s/src/runtime/funcdata.h", goroot), 0)
        }
 
        // Generate any missing files; regenerate existing ones.
-       for(i=0; i<files.len; i++) {
-               p = files.p[i];
-               elem = lastelem(p);
-               for(j=0; j<nelem(gentab); j++) {
-                       if(gentab[j].gen == nil)
-                               continue;
-                       if(hasprefix(elem, gentab[j].nameprefix)) {
-                               if(vflag > 1)
-                                       errprintf("generate %s\n", p);
-                               gentab[j].gen(bstr(&path), p);
+       for _, p := range files {
+               elem := filepath.Base(p)
+               for _, gt := range gentab {
+                       if gt.gen == nil {
+                               continue
+                       }
+                       if strings.HasPrefix(elem, gt.nameprefix) {
+                               if vflag > 1 {
+                                       errprintf("generate %s\n", p)
+                               }
+                               gt.gen(path, p)
                                // Do not add generated file to clean list.
                                // In runtime, we want to be able to
                                // build the package with the go tool,
@@ -907,222 +844,173 @@ install(char *dir)
                                // exist (it does not know how to build them).
                                // The 'clean' command can remove
                                // the generated files.
-                               goto built;
+                               goto built
                        }
                }
                // Did not rebuild p.
-               if(find(p, missing.p, missing.len) >= 0)
-                       fatal("missing file %s", p);
-       built:;
+               if find(p, missing) >= 0 {
+                       fatal("missing file %s", p)
+               }
+       built:
        }
 
-       if((!streq(goos, gohostos) || !streq(goarch, gohostarch)) && isgo) {
+       if (goos != gohostos || goarch != gohostarch) && isgo {
                // We've generated the right files; the go command can do the build.
-               if(vflag > 1)
-                       errprintf("skip build for cross-compile %s\n", dir);
-               goto nobuild;
+               if vflag > 1 {
+                       errprintf("skip build for cross-compile %s\n", dir)
+               }
+               return
        }
 
-       if(isgo) {
+       var archive string
+       if isgo {
                // The next loop will compile individual non-Go files.
                // Hand the Go files to the compiler en masse.
                // For package runtime, this writes go_asm.h, which
                // the assembly files will need.
-               vreset(&compile);
-               vadd(&compile, bpathf(&b, "%s/%sg", tooldir, gochar));
-
-               bpathf(&b, "%s/_go_.a", workdir);
-               vadd(&compile, "-pack");
-               vadd(&compile, "-o");
-               vadd(&compile, bstr(&b));
-               vadd(&clean, bstr(&b));
-               if(!ispackcmd)
-                       vadd(&link, bstr(&b));
-               else
-                       bwriteb(&archive, &b);
-
-               vadd(&compile, "-p");
-               if(hasprefix(dir, "cmd/"))
-                       vadd(&compile, "main");
-               else
-                       vadd(&compile, dir);
-
-               if(streq(dir, "runtime")) {
-                       vadd(&compile, "-+");
-                       vadd(&compile, "-asmhdr");
-                       bpathf(&b1, "%s/go_asm.h", workdir);
-                       vadd(&compile, bstr(&b1));
+               pkg := dir
+               if strings.HasPrefix(dir, "cmd/") {
+                       pkg = "main"
                }
-
-               vcopy(&compile, go.p, go.len);
-
-               runv(nil, bstr(&path), CheckExit, &compile);
+               b := pathf("%s/_go_.a", workdir)
+               clean = append(clean, b)
+               if !ispackcmd {
+                       link = append(link, b)
+               } else {
+                       archive = b
+               }
+               compile := []string{pathf("%s/%sg", tooldir, gochar), "-pack", "-o", b, "-p", pkg}
+               if dir == "runtime" {
+                       compile = append(compile, "-+", "-asmhdr", pathf("%s/go_asm.h", workdir))
+               }
+               compile = append(compile, gofiles...)
+               run(path, CheckExit|ShowOutput, compile...)
        }
 
        // Compile the files.
-       for(i=0; i<files.len; i++) {
-               if(!hassuffix(files.p[i], ".c") && !hassuffix(files.p[i], ".s"))
-                       continue;
-               name = lastelem(files.p[i]);
+       for _, p := range files {
+               if !strings.HasSuffix(p, ".c") && !strings.HasSuffix(p, ".s") {
+                       continue
+               }
+               name := filepath.Base(p)
 
-               vreset(&compile);
-               if(!isgo) {
+               var compile []string
+               if !isgo {
                        // C library or tool.
-                       if(streq(gohostos, "plan9")) {
-                               vadd(&compile, bprintf(&b, "%sc", gohostchar));
-                               vadd(&compile, "-FTVwp");
-                               vadd(&compile, "-DPLAN9");
-                               vadd(&compile, "-D__STDC__=1");
-                               vadd(&compile, "-D__SIZE_TYPE__=ulong"); // for GNU Bison
-                               vadd(&compile, bpathf(&b, "-I%s/include/plan9", goroot));
-                               vadd(&compile, bpathf(&b, "-I%s/include/plan9/%s", goroot, gohostarch));
+                       if gohostos == "plan9" {
+                               compile = []string{
+                                       gohostchar + "c", "-FTVwp",
+                                       "-DPLAN9",
+                                       "-D__STDC__=1",
+                                       "-D__SIZE_TYPE__=ulong", // for GNU bison
+                                       pathf("-I%s/include/plan9", goroot),
+                                       pathf("-I%s/include/plan9/%s", goroot, gohostarch),
+                               }
                        } else {
-                               vcopy(&compile, gccargs.p, gccargs.len);
-                               vadd(&compile, "-c");
-                               if(streq(gohostarch, "amd64"))
-                                       vadd(&compile, "-m64");
-                               else if(streq(gohostarch, "386"))
-                                       vadd(&compile, "-m32");
-       
-                               vadd(&compile, "-I");
-                               vadd(&compile, bpathf(&b, "%s/include", goroot));
+                               compile = gccargs[0:len(gccargs):len(gccargs)]
+                               compile = append(compile, "-c")
+                               switch gohostarch {
+                               case "amd64":
+                                       compile = append(compile, "-m64")
+                               case "386":
+                                       compile = append(compile, "-m32")
+                               }
+                               compile = append(compile, "-I", pathf("%s/include", goroot))
                        }
 
-                       if(streq(dir, "lib9"))
-                               vadd(&compile, "-DPLAN9PORT");
-
+                       if dir == "lib9" {
+                               compile = append(compile, "-DPLAN9PORT")
+                       }
 
-                       vadd(&compile, "-I");
-                       vadd(&compile, bstr(&path));
+                       compile = append(compile, "-I", path)
 
                        // lib9/goos.c gets the default constants hard-coded.
-                       if(streq(name, "goos.c")) {
-                               vadd(&compile, "-D");
-                               vadd(&compile, bprintf(&b, "GOOS=\"%s\"", goos));
-                               vadd(&compile, "-D");
-                               vadd(&compile, bprintf(&b, "GOARCH=\"%s\"", goarch));
-                               bprintf(&b1, "%s", goroot_final);
-                               bsubst(&b1, "\\", "\\\\");  // turn into C string
-                               vadd(&compile, "-D");
-                               vadd(&compile, bprintf(&b, "GOROOT=\"%s\"", bstr(&b1)));
-                               vadd(&compile, "-D");
-                               vadd(&compile, bprintf(&b, "GOVERSION=\"%s\"", goversion));
-                               vadd(&compile, "-D");
-                               vadd(&compile, bprintf(&b, "GOARM=\"%s\"", goarm));
-                               vadd(&compile, "-D");
-                               vadd(&compile, bprintf(&b, "GO386=\"%s\"", go386));
-                               vadd(&compile, "-D");
-                               vadd(&compile, bprintf(&b, "GO_EXTLINK_ENABLED=\"%s\"", goextlinkenabled));
+                       if name == "goos.c" {
+                               compile = append(compile,
+                                       "-D", fmt.Sprintf("GOOS=%q", goos),
+                                       "-D", fmt.Sprintf("GOARCH=%q", goarch),
+                                       "-D", fmt.Sprintf("GOROOT=%q", goroot_final),
+                                       "-D", fmt.Sprintf("GOVERSION=%q", goversion),
+                                       "-D", fmt.Sprintf("GOARM=%q", goarm),
+                                       "-D", fmt.Sprintf("GO386=%q", go386),
+                                       "-D", fmt.Sprintf("GO_EXTLINK_ENABLED=%q", goextlinkenabled),
+                               )
                        }
 
                        // gc/lex.c records the GOEXPERIMENT setting used during the build.
-                       if(streq(name, "lex.c")) {
-                               xgetenv(&b, "GOEXPERIMENT");
-                               vadd(&compile, "-D");
-                               vadd(&compile, bprintf(&b1, "GOEXPERIMENT=\"%s\"", bstr(&b)));
+                       if name == "lex.c" {
+                               compile = append(compile,
+                                       "-D", fmt.Sprintf("GOEXPERIMENT=%q", os.Getenv("GOEXPERIMENT")))
                        }
                } else {
-                       // Supporting files for a Go package.
-                       if(hassuffix(files.p[i], ".s"))
-                               vadd(&compile, bpathf(&b, "%s/%sa", tooldir, gochar));
-                       else {
-                               vadd(&compile, bpathf(&b, "%s/%sc", tooldir, gochar));
-                               vadd(&compile, "-F");
-                               vadd(&compile, "-V");
-                               vadd(&compile, "-w");
+                       // Assembly file for a Go package.
+                       compile = []string{
+                               pathf("%s/%sa", tooldir, gochar),
+                               "-I", workdir,
+                               "-I", pathf("%s/pkg/%s_%s", goroot, goos, goarch),
+                               "-D", "GOOS_" + goos,
+                               "-D", "GOARCH_" + goarch,
+                               "-D", "GOOS_GOARCH_" + goos + "_" + goarch,
                        }
-                       vadd(&compile, "-I");
-                       vadd(&compile, workdir);
-                       vadd(&compile, "-I");
-                       vadd(&compile, bprintf(&b, "%s/pkg/%s_%s", goroot, goos, goarch));
-                       vadd(&compile, "-D");
-                       vadd(&compile, bprintf(&b, "GOOS_%s", goos));
-                       vadd(&compile, "-D");
-                       vadd(&compile, bprintf(&b, "GOARCH_%s", goarch));
-                       vadd(&compile, "-D");
-                       vadd(&compile, bprintf(&b, "GOOS_GOARCH_%s_%s", goos, goarch));
                }
 
-               bpathf(&b, "%s/%s", workdir, lastelem(files.p[i]));
-               doclean = 1;
-               if(!isgo && streq(gohostos, "darwin")) {
+               doclean := true
+               b := pathf("%s/%s", workdir, filepath.Base(p))
+               if !isgo && gohostos == "darwin" {
                        // To debug C programs on OS X, it is not enough to say -ggdb
                        // on the command line.  You have to leave the object files
                        // lying around too.  Leave them in pkg/obj/, which does not
                        // get removed when this tool exits.
-                       bpathf(&b1, "%s/pkg/obj/%s", goroot, dir);
-                       xmkdirall(bstr(&b1));
-                       bpathf(&b, "%s/%s", bstr(&b1), lastelem(files.p[i]));
-                       doclean = 0;
+                       obj := pathf("%s/pkg/obj/%s", goroot, dir)
+                       xmkdirall(obj)
+                       b = pathf("%s/%s", obj, filepath.Base(p))
+                       doclean = false
                }
 
                // Change the last character of the output file (which was c or s).
-               if(streq(gohostos, "plan9"))
-                       b.p[b.len-1] = gohostchar[0];
-               else
-                       b.p[b.len-1] = 'o';
-               vadd(&compile, "-o");
-               vadd(&compile, bstr(&b));
-               vadd(&compile, files.p[i]);
-               bgrunv(bstr(&path), CheckExit, &compile);
+               if gohostos == "plan9" {
+                       b = b[:len(b)-1] + gohostchar
+               } else {
+                       b = b[:len(b)-1] + "o"
+               }
+               compile = append(compile, "-o", b, p)
+               bgrun(path, compile...)
 
-               vadd(&link, bstr(&b));
-               if(doclean)
-                       vadd(&clean, bstr(&b));
+               link = append(link, b)
+               if doclean {
+                       clean = append(clean, b)
+               }
        }
-       bgwait();
+       bgwait()
 
-       if(isgo && ispackcmd) {
-               xremove(link.p[targ]);
-               dopack(link.p[targ], bstr(&archive), &link.p[targ+1], link.len - (targ+1));
-               goto nobuild;
+       if isgo && ispackcmd {
+               xremove(link[targ])
+               dopack(link[targ], archive, link[targ+1:])
+               return
        }
 
-       if(!islib && !isgo) {
+       if !islib && !isgo {
                // C binaries need the libraries explicitly, and -lm.
-               vcopy(&link, lib.p, lib.len);
-               if(!streq(gohostos, "plan9"))
-                       vadd(&link, "-lm");
+               link = append(link, libs...)
+               if gohostos != "plan9" {
+                       link = append(link, "-lm")
+               }
        }
 
        // Remove target before writing it.
-       xremove(link.p[targ]);
-
-       runv(nil, nil, CheckExit, &link);
-nobuild:
-
-out:
-       for(i=0; i<clean.len; i++)
-               xremove(clean.p[i]);
-
-       bfree(&b);
-       bfree(&b1);
-       bfree(&path);
-       bfree(&archive);
-       vfree(&compile);
-       vfree(&files);
-       vfree(&link);
-       vfree(&go);
-       vfree(&missing);
-       vfree(&clean);
-       vfree(&lib);
-       vfree(&extra);
+       xremove(link[targ])
+       run("", CheckExit|ShowOutput, link...)
 }
 
 // matchfield reports whether the field matches this build.
-static bool
-matchfield(char *f)
-{
-       char *p;
-       bool res;
-
-       p = xstrrchr(f, ',');
-       if(p == nil)
-               return streq(f, goos) || streq(f, goarch) || streq(f, "cmd_go_bootstrap") || streq(f, "go1.1") || (streq(goos, "android") && streq(f, "linux"));
-       *p = 0;
-       res = matchfield(f) && matchfield(p+1);
-       *p = ',';
-       return res;
+func matchfield(f string) bool {
+       for _, tag := range strings.Split(f, ",") {
+               if tag == goos || tag == goarch || tag == "cmd_go_bootstrap" || tag == "go1.1" || (goos == "android" && tag == "linux") {
+                       continue
+               }
+               return false
+       }
+       return true
 }
 
 // shouldbuild reports whether we should build this file.
@@ -1132,162 +1020,117 @@ matchfield(char *f)
 // In particular, they can be the entire file name (like windows.c).
 // We also allow the special tag cmd_go_bootstrap.
 // See ../go/bootstrap.go and package go/build.
-static bool
-shouldbuild(char *file, char *dir)
-{
-       char *name, *p;
-       int i, j, ret;
-       Buf b;
-       Vec lines, fields;
-       
+func shouldbuild(file, dir string) bool {
        // Check file name for GOOS or GOARCH.
-       name = lastelem(file);
-       for(i=0; i<nelem(okgoos); i++) {
-               if(streq(okgoos[i], goos))
-                       continue;
-               p = xstrstr(name, okgoos[i]);
-               if(p == nil)
-                       continue;
-               p += xstrlen(okgoos[i]);
-               if(*p == '.' || *p == '_' || *p == '\0')
-                       return 0;
-       }
-       for(i=0; i<nelem(okgoarch); i++) {
-               if(streq(okgoarch[i], goarch))
-                       continue;
-               p = xstrstr(name, okgoarch[i]);
-               if(p == nil)
-                       continue;
-               p += xstrlen(okgoarch[i]);
-               if(*p == '.' || *p == '_' || *p == '\0')
-                       return 0;
+       name := filepath.Base(file)
+       excluded := func(list []string, ok string) bool {
+               for _, x := range list {
+                       if x == ok {
+                               continue
+                       }
+                       i := strings.Index(name, x)
+                       if i < 0 {
+                               continue
+                       }
+                       i += len(x)
+                       if i == len(name) || name[i] == '.' || name[i] == '_' {
+                               return true
+                       }
+               }
+               return false
+       }
+       if excluded(okgoos, goos) || excluded(okgoarch, goarch) {
+               return false
        }
 
        // Omit test files.
-       if(contains(name, "_test"))
-               return 0;
+       if strings.Contains(name, "_test") {
+               return false
+       }
 
        // cmd/go/doc.go has a giant /* */ comment before
        // it gets to the important detail that it is not part of
        // package main.  We don't parse those comments,
        // so special case that file.
-       if(hassuffix(file, "cmd/go/doc.go") || hassuffix(file, "cmd\\go\\doc.go"))
-               return 0;
-       if(hassuffix(file, "cmd/cgo/doc.go") || hassuffix(file, "cmd\\cgo\\doc.go"))
-               return 0;
+       if strings.HasSuffix(file, "cmd/go/doc.go") || strings.HasSuffix(file, "cmd\\go\\doc.go") {
+               return false
+       }
+       if strings.HasSuffix(file, "cmd/cgo/doc.go") || strings.HasSuffix(file, "cmd\\cgo\\doc.go") {
+               return false
+       }
 
        // Check file contents for // +build lines.
-       binit(&b);
-       vinit(&lines);
-       vinit(&fields);
-
-       ret = 1;
-       readfile(&b, file);
-       splitlines(&lines, bstr(&b));
-       for(i=0; i<lines.len; i++) {
-               p = lines.p[i];
-               while(*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n')
-                       p++;
-               if(*p == '\0')
-                       continue;
-               if(contains(p, "package documentation")) {
-                       ret = 0;
-                       goto out;
+       for _, p := range splitlines(readfile(file)) {
+               p = strings.TrimSpace(p)
+               if p == "" {
+                       continue
+               }
+               if strings.Contains(p, "package documentation") {
+                       return false
                }
-               if(contains(p, "package main") && !streq(dir, "cmd/go") && !streq(dir, "cmd/cgo")) {
-                       ret = 0;
-                       goto out;
+               if strings.Contains(p, "package main") && dir != "cmd/go" && dir != "cmd/cgo" {
+                       return false
                }
-               if(!hasprefix(p, "//"))
-                       break;
-               if(!contains(p, "+build"))
-                       continue;
-               splitfields(&fields, lines.p[i]);
-               if(fields.len < 2 || !streq(fields.p[1], "+build"))
-                       continue;
-               for(j=2; j<fields.len; j++) {
-                       p = fields.p[j];
-                       if((*p == '!' && !matchfield(p+1)) || matchfield(p))
-                               goto fieldmatch;
+               if !strings.HasPrefix(p, "//") {
+                       break
                }
-               ret = 0;
-               goto out;
-       fieldmatch:;
+               if !strings.Contains(p, "+build") {
+                       continue
+               }
+               fields := splitfields(p)
+               if len(fields) < 2 || fields[1] != "+build" {
+                       continue
+               }
+               for _, p := range fields[2:] {
+                       if (p[0] == '!' && !matchfield(p[1:])) || matchfield(p) {
+                               goto fieldmatch
+                       }
+               }
+               return false
+       fieldmatch:
        }
 
-out:
-       bfree(&b);
-       vfree(&lines);
-       vfree(&fields);
-
-       return ret;
+       return true
 }
 
 // copy copies the file src to dst, via memory (so only good for small files).
-void
-copyfile(char *dst, char *src, int exec)
-{
-       Buf b;
-
-       if(vflag > 1)
-               errprintf("cp %s %s\n", src, dst);
-
-       binit(&b);
-       readfile(&b, src);
-       writefile(&b, dst, exec);
-       bfree(&b);
+func copyfile(dst, src string, exec int) {
+       if vflag > 1 {
+               errprintf("cp %s %s\n", src, dst)
+       }
+       writefile(readfile(src), dst, exec)
 }
 
 // dopack copies the package src to dst,
 // appending the files listed in extra.
 // The archive format is the traditional Unix ar format.
-static void
-dopack(char *dst, char *src, char **extra, int nextra)
-{
-       int i;
-       char c, *p, *q;
-       Buf b, bdst;
-       
-       binit(&b);
-       binit(&bdst);
-
-       readfile(&bdst, src);
-       for(i=0; i<nextra; i++) {
-               readfile(&b, extra[i]);
+func dopack(dst, src string, extra []string) {
+       bdst := bytes.NewBufferString(readfile(src))
+       for _, file := range extra {
+               b := readfile(file)
                // find last path element for archive member name
-               p = xstrrchr(extra[i], '/');
-               if(p)
-                       p++;
-               q = xstrrchr(extra[i], '\\');
-               if(q) {
-                       q++;
-                       if(p == nil || q > p)
-                               p = q;
+               i := strings.LastIndex(file, "/") + 1
+               j := strings.LastIndex(file, `\`) + 1
+               if i < j {
+                       i = j
                }
-               if(p == nil)
-                       p = extra[i];
-               bwritef(&bdst, "%-16.16s%-12d%-6d%-6d%-8o%-10d`\n", p, 0, 0, 0, 0644, b.len);
-               bwriteb(&bdst, &b);
-               if(b.len&1) {
-                       c = 0;
-                       bwrite(&bdst, &c, 1);
+               fmt.Fprintf(bdst, "%-16.16s%-12d%-6d%-6d%-8o%-10d`\n", file[i:], 0, 0, 0, 0644, len(b))
+               bdst.WriteString(b)
+               if len(b)&1 != 0 {
+                       bdst.WriteByte(0)
                }
        }
-
-       writefile(&bdst, dst, 0);
-
-       bfree(&b);
-       bfree(&bdst);
+       writefile(bdst.String(), dst, 0)
 }
 
 // buildorder records the order of builds for the 'go bootstrap' command.
-static char *buildorder[] = {
+var buildorder = []string{
        "lib9",
        "libbio",
        "liblink",
 
        "cmd/gc",  // must be before g
-       "cmd/%sl",  // must be before a, g
+       "cmd/%sl", // must be before a, g
        "cmd/%sa",
        "cmd/%sg",
 
@@ -1337,12 +1180,12 @@ static char *buildorder[] = {
        "go/doc",
        "go/build",
        "cmd/go",
-};
+}
 
 // cleantab records the directories to clean in 'go clean'.
 // It is bigger than the buildorder because we clean all the
 // compilers but build only the $GOARCH ones.
-static char *cleantab[] = {
+var cleantab = []string{
        // Commands and C libraries.
        "cmd/5a",
        "cmd/5g",
@@ -1357,7 +1200,7 @@ static char *cleantab[] = {
        "cmd/9g",
        "cmd/9l",
        "cmd/gc",
-       "cmd/go",       
+       "cmd/go",
        "lib9",
        "libbio",
        "liblink",
@@ -1403,383 +1246,246 @@ static char *cleantab[] = {
        "unicode",
        "unicode/utf16",
        "unicode/utf8",
-};
+}
 
-static char *runtimegen[] = {
+var runtimegen = []string{
        "zaexperiment.h",
        "zversion.go",
-};
-
-static void
-clean(void)
-{
-       int i, j, k;
-       Buf b, path;
-       Vec dir;
-
-       binit(&b);
-       binit(&path);
-       vinit(&dir);
-
-       for(i=0; i<nelem(cleantab); i++) {
-               bpathf(&path, "%s/src/%s", goroot, cleantab[i]);
-               xreaddir(&dir, bstr(&path));
+}
+
+func clean() {
+       for _, name := range cleantab {
+               path := pathf("%s/src/%s", goroot, name)
                // Remove generated files.
-               for(j=0; j<dir.len; j++) {
-                       for(k=0; k<nelem(gentab); k++) {
-                               if(hasprefix(dir.p[j], gentab[k].nameprefix))
-                                       xremove(bpathf(&b, "%s/%s", bstr(&path), dir.p[j]));
+               for _, elem := range xreaddir(path) {
+                       for _, gt := range gentab {
+                               if strings.HasPrefix(elem, gt.nameprefix) {
+                                       xremove(pathf("%s/%s", path, elem))
+                               }
                        }
                }
                // Remove generated binary named for directory.
-               if(hasprefix(cleantab[i], "cmd/"))
-                       xremove(bpathf(&b, "%s/%s", bstr(&path), cleantab[i]+4));
+               if strings.HasPrefix(name, "cmd/") {
+                       xremove(pathf("%s/%s", path, name[4:]))
+               }
        }
 
-       // remove src/runtime/zaexperiment.h and 
-       // except leave zgoos and zgoarch, now maintained with go generate.
-       bpathf(&path, "%s/src/runtime", goroot);
-       for(j=0; j<nelem(runtimegen); j++)
-               xremove(bpathf(&b, "%s/%s", bstr(&path), runtimegen[j]));
+       // remove runtimegen files.
+       path := pathf("%s/src/runtime", goroot)
+       for _, elem := range runtimegen {
+               xremove(pathf("%s/%s", path, elem))
+       }
 
-       if(rebuildall) {
+       if rebuildall {
                // Remove object tree.
-               xremoveall(bpathf(&b, "%s/pkg/obj/%s_%s", goroot, gohostos, gohostarch));
+               xremoveall(pathf("%s/pkg/obj/%s_%s", goroot, gohostos, gohostarch))
 
                // Remove installed packages and tools.
-               xremoveall(bpathf(&b, "%s/pkg/%s_%s", goroot, gohostos, gohostarch));
-               xremoveall(bpathf(&b, "%s/pkg/%s_%s", goroot, goos, goarch));
-               xremoveall(tooldir);
+               xremoveall(pathf("%s/pkg/%s_%s", goroot, gohostos, gohostarch))
+               xremoveall(pathf("%s/pkg/%s_%s", goroot, goos, goarch))
+               xremoveall(tooldir)
 
                // Remove cached version info.
-               xremove(bpathf(&b, "%s/VERSION.cache", goroot));
+               xremove(pathf("%s/VERSION.cache", goroot))
        }
-
-       bfree(&b);
-       bfree(&path);
-       vfree(&dir);
 }
 
 /*
  * command implementations
  */
 
-void
-usage(void)
-{
-       xprintf("usage: go tool dist [command]\n"
-               "Commands are:\n"
-               "\n"
-               "banner         print installation banner\n"
-               "bootstrap      rebuild everything\n"
-               "clean          deletes all built files\n"
-               "env [-p]       print environment (-p: include $PATH)\n"
-               "install [dir]  install individual directory\n"
-               "version        print Go version\n"
-               "\n"
-               "All commands take -v flags to emit extra information.\n"
-       );
-       xexit(2);
+func usage() {
+       xprintf("usage: go tool dist [command]\n" +
+               "Commands are:\n" +
+               "\n" +
+               "banner         print installation banner\n" +
+               "bootstrap      rebuild everything\n" +
+               "clean          deletes all built files\n" +
+               "env [-p]       print environment (-p: include $PATH)\n" +
+               "install [dir]  install individual directory\n" +
+               "version        print Go version\n" +
+               "\n" +
+               "All commands take -v flags to emit extra information.\n",
+       )
+       xexit(2)
 }
 
 // The env command prints the default environment.
-void
-cmdenv(int argc, char **argv)
-{
-       bool pflag;
-       char *sep;
-       Buf b, b1;
-       char *format;
-
-       binit(&b);
-       binit(&b1);
-
-       format = "%s=\"%s\"\n";
-       pflag = 0;
-       ARGBEGIN{
-       case '9':
-               format = "%s='%s'\n";
-               break;
-       case 'p':
-               pflag = 1;
-               break;
-       case 'v':
-               vflag++;
-               break;
-       case 'w':
-               format = "set %s=%s\r\n";
-               break;
-       default:
-               usage();
-       }ARGEND
-
-       if(argc > 0)
-               usage();
-
-       xprintf(format, "CC", defaultcc);
-       xprintf(format, "CC_FOR_TARGET", defaultcctarget);
-       xprintf(format, "GOROOT", goroot);
-       xprintf(format, "GOBIN", gobin);
-       xprintf(format, "GOARCH", goarch);
-       xprintf(format, "GOOS", goos);
-       xprintf(format, "GOHOSTARCH", gohostarch);
-       xprintf(format, "GOHOSTOS", gohostos);
-       xprintf(format, "GOTOOLDIR", tooldir);
-       xprintf(format, "GOCHAR", gochar);
-       if(streq(goarch, "arm"))
-               xprintf(format, "GOARM", goarm);
-       if(streq(goarch, "386"))
-               xprintf(format, "GO386", go386);
-
-       if(pflag) {
-               sep = ":";
-               if(streq(gohostos, "windows"))
-                       sep = ";";
-               xgetenv(&b, "PATH");
-               bprintf(&b1, "%s%s%s", gobin, sep, bstr(&b));
-               xprintf(format, "PATH", bstr(&b1));
-       }
-
-       bfree(&b);
-       bfree(&b1);
+func cmdenv() {
+       path := flag.Bool("p", false, "emit updated PATH")
+       plan9 := flag.Bool("9", false, "emit plan 9 syntax")
+       windows := flag.Bool("w", false, "emit windows syntax")
+       xflagparse(0)
+
+       format := "%s=\"%s\"\n"
+       switch {
+       case *plan9:
+               format = "%s='%s'\n"
+       case *windows:
+               format = "set %s=%s\r\n"
+       }
+
+       xprintf(format, "CC", defaultcc)
+       xprintf(format, "CC_FOR_TARGET", defaultcctarget)
+       xprintf(format, "GOROOT", goroot)
+       xprintf(format, "GOBIN", gobin)
+       xprintf(format, "GOARCH", goarch)
+       xprintf(format, "GOOS", goos)
+       xprintf(format, "GOHOSTARCH", gohostarch)
+       xprintf(format, "GOHOSTOS", gohostos)
+       xprintf(format, "GOTOOLDIR", tooldir)
+       xprintf(format, "GOCHAR", gochar)
+       if goarch == "arm" {
+               xprintf(format, "GOARM", goarm)
+       }
+       if goarch == "386" {
+               xprintf(format, "GO386", go386)
+       }
+
+       if *path {
+               sep := ":"
+               if gohostos == "windows" {
+                       sep = ";"
+               }
+               xprintf(format, "PATH", fmt.Sprintf("%s%s%s", gobin, sep, os.Getenv("PATH")))
+       }
 }
 
 // The bootstrap command runs a build from scratch,
 // stopping at having installed the go_bootstrap command.
-void
-cmdbootstrap(int argc, char **argv)
-{
-       int i;
-       Buf b;
-       char *oldgoos, *oldgoarch, *oldgochar;
-
-       binit(&b);
-
-       ARGBEGIN{
-       case 'a':
-               rebuildall = 1;
-               break;
-       case 's':
-               sflag++;
-               break;
-       case 'v':
-               vflag++;
-               break;
-       default:
-               usage();
-       }ARGEND
-
-       if(argc > 0)
-               usage();
+func cmdbootstrap() {
+       flag.BoolVar(&rebuildall, "a", rebuildall, "rebuild all")
+       flag.BoolVar(&sflag, "s", sflag, "build static binaries")
+       xflagparse(0)
+
+       if isdir(pathf("%s/src/pkg", goroot)) {
+               fatal("\n\n"+
+                       "The Go package sources have moved to $GOROOT/src.\n"+
+                       "*** %s still exists. ***\n"+
+                       "It probably contains stale files that may confuse the build.\n"+
+                       "Please (check what's there and) remove it and try again.\n"+
+                       "See http://golang.org/s/go14nopkg\n",
+                       pathf("%s/src/pkg", goroot))
+       }
 
-       if(isdir(bpathf(&b, "%s/src/pkg", goroot))) {
-               fatal("\n\n"
-                       "The Go package sources have moved to $GOROOT/src.\n"
-                       "*** %s still exists. ***\n"
-                       "It probably contains stale files that may confuse the build.\n"
-                       "Please (check what's there and) remove it and try again.\n"
-                       "See http://golang.org/s/go14nopkg\n", bpathf(&b, "%s/src/pkg", goroot));
+       if rebuildall {
+               clean()
        }
-       
-       if(rebuildall)
-               clean();
-       goversion = findgoversion();
-       setup();
 
-       xsetenv("GOROOT", goroot);
-       xsetenv("GOROOT_FINAL", goroot_final);
+       setup()
 
        // For the main bootstrap, building for host os/arch.
-       oldgoos = goos;
-       oldgoarch = goarch;
-       oldgochar = gochar;
-       goos = gohostos;
-       goarch = gohostarch;
-       gochar = gohostchar;
-       xsetenv("GOARCH", goarch);
-       xsetenv("GOOS", goos);
-
-       for(i=0; i<nelem(buildorder); i++) {
-               install(bprintf(&b, buildorder[i], gohostchar));
-               if(!streq(oldgochar, gohostchar) && xstrstr(buildorder[i], "%s"))
-                       install(bprintf(&b, buildorder[i], oldgochar));
-       }
-
-       goos = oldgoos;
-       goarch = oldgoarch;
-       gochar = oldgochar;
-       xsetenv("GOARCH", goarch);
-       xsetenv("GOOS", goos);
+       oldgoos = goos
+       oldgoarch = goarch
+       oldgochar = gochar
+       goos = gohostos
+       goarch = gohostarch
+       gochar = gohostchar
+       os.Setenv("GOHOSTARCH", gohostarch)
+       os.Setenv("GOHOSTOS", gohostos)
+       os.Setenv("GOARCH", goarch)
+       os.Setenv("GOOS", goos)
+
+       for _, pattern := range buildorder {
+               dir := pattern
+               if strings.Contains(pattern, "%s") {
+                       dir = fmt.Sprintf(pattern, gohostchar)
+               }
+               install(dir)
+               if oldgochar != gohostchar && strings.Contains(pattern, "%s") {
+                       install(fmt.Sprintf(pattern, oldgochar))
+               }
+       }
 
-       // Build runtime for actual goos/goarch too.
-       if(!streq(goos, gohostos) || !streq(goarch, gohostarch))
-               install("runtime");
+       goos = oldgoos
+       goarch = oldgoarch
+       gochar = oldgochar
+       os.Setenv("GOARCH", goarch)
+       os.Setenv("GOOS", goos)
 
-       bfree(&b);
+       // Build runtime for actual goos/goarch too.
+       if goos != gohostos || goarch != gohostarch {
+               install("runtime")
+       }
 }
 
-static char*
-defaulttarg(void)
-{
-       char *p;
-       Buf pwd, src, real_src;
-
-       binit(&pwd);
-       binit(&src);
-       binit(&real_src);
-
+func defaulttarg() string {
        // xgetwd might return a path with symlinks fully resolved, and if
        // there happens to be symlinks in goroot, then the hasprefix test
        // will never succeed. Instead, we use xrealwd to get a canonical
        // goroot/src before the comparison to avoid this problem.
-       xgetwd(&pwd);
-       p = btake(&pwd);
-       bpathf(&src, "%s/src/", goroot);
-       xrealwd(&real_src, bstr(&src));
-       if(!hasprefix(p, bstr(&real_src)))
-               fatal("current directory %s is not under %s", p, bstr(&real_src));
-       p += real_src.len;
+       pwd := xgetwd()
+       src := pathf("%s/src/", goroot)
+       real_src := xrealwd(src)
+       if !strings.HasPrefix(pwd, real_src) {
+               fatal("current directory %s is not under %s", pwd, real_src)
+       }
+       pwd = pwd[len(real_src):]
        // guard againt xrealwd return the directory without the trailing /
-       if(*p == slash[0])
-               p++;
-
-       bfree(&pwd);
-       bfree(&src);
-       bfree(&real_src);
+       pwd = strings.TrimPrefix(pwd, "/")
 
-       return p;
+       return pwd
 }
 
 // Install installs the list of packages named on the command line.
-void
-cmdinstall(int argc, char **argv)
-{
-       int i;
-
-       ARGBEGIN{
-       case 's':
-               sflag++;
-               break;
-       case 'v':
-               vflag++;
-               break;
-       default:
-               usage();
-       }ARGEND
+func cmdinstall() {
+       flag.BoolVar(&sflag, "s", sflag, "build static binaries")
+       xflagparse(-1)
 
-       if(argc == 0)
-               install(defaulttarg());
+       if flag.NArg() == 0 {
+               install(defaulttarg())
+       }
 
-       for(i=0; i<argc; i++)
-               install(argv[i]);
+       for _, arg := range flag.Args() {
+               install(arg)
+       }
 }
 
 // Clean deletes temporary objects.
-// Clean -i deletes the installed objects too.
-void
-cmdclean(int argc, char **argv)
-{
-       ARGBEGIN{
-       case 'v':
-               vflag++;
-               break;
-       default:
-               usage();
-       }ARGEND
-
-       if(argc > 0)
-               usage();
-
-       clean();
+func cmdclean() {
+       xflagparse(0)
+       clean()
 }
 
 // Banner prints the 'now you've installed Go' banner.
-void
-cmdbanner(int argc, char **argv)
-{
-       char *pathsep, *pid, *ns;
-       Buf b, b1, search, path;
-
-       ARGBEGIN{
-       case 'v':
-               vflag++;
-               break;
-       default:
-               usage();
-       }ARGEND
-
-       if(argc > 0)
-               usage();
-
-       binit(&b);
-       binit(&b1);
-       binit(&search);
-       binit(&path);
+func cmdbanner() {
+       xflagparse(0)
 
-       xprintf("\n");
-       xprintf("---\n");
-       xprintf("Installed Go for %s/%s in %s\n", goos, goarch, goroot);
-       xprintf("Installed commands in %s\n", gobin);
+       xprintf("\n")
+       xprintf("---\n")
+       xprintf("Installed Go for %s/%s in %s\n", goos, goarch, goroot)
+       xprintf("Installed commands in %s\n", gobin)
 
-       if(!xsamefile(goroot_final, goroot)) {
+       if !xsamefile(goroot_final, goroot) {
                // If the files are to be moved, don't check that gobin
                // is on PATH; assume they know what they are doing.
-       } else if(streq(gohostos, "plan9")) {
+       } else if gohostos == "plan9" {
                // Check that gobin is bound before /bin.
-               readfile(&b, "#c/pid");
-               bsubst(&b, " ", "");
-               pid = btake(&b);
-               bprintf(&b, "/proc/%s/ns", pid);
-               ns = btake(&b);
-               readfile(&b, ns);
-               bprintf(&search, "bind -b %s /bin\n", gobin);
-               if(xstrstr(bstr(&b), bstr(&search)) == nil)
-                       xprintf("*** You need to bind %s before /bin.\n", gobin);
+               pid := strings.Replace(readfile("#c/pid"), " ", "", -1)
+               ns := fmt.Sprintf("/proc/%s/ns", pid)
+               if !strings.Contains(readfile(ns), fmt.Sprintf("bind -b %s /bin", gobin)) {
+                       xprintf("*** You need to bind %s before /bin.\n", gobin)
+               }
        } else {
                // Check that gobin appears in $PATH.
-               xgetenv(&b, "PATH");
-               pathsep = ":";
-               if(streq(gohostos, "windows"))
-                       pathsep = ";";
-               bprintf(&b1, "%s%s%s", pathsep, bstr(&b), pathsep);
-               bprintf(&search, "%s%s%s", pathsep, gobin, pathsep);
-               if(xstrstr(bstr(&b1), bstr(&search)) == nil)
-                       xprintf("*** You need to add %s to your PATH.\n", gobin);
-       }
-
-       if(streq(gohostos, "darwin")) {
-               if(isfile(bpathf(&path, "%s/cov", tooldir)))
-                       xprintf("\n"
-                               "On OS X the debuggers must be installed setgid procmod.\n"
-                               "Read and run ./sudo.bash to install the debuggers.\n");
+               pathsep := ":"
+               if gohostos == "windows" {
+                       pathsep = ";"
+               }
+               if !strings.Contains(pathsep+os.Getenv("PATH")+pathsep, pathsep+gobin+pathsep) {
+                       xprintf("*** You need to add %s to your PATH.\n", gobin)
+               }
        }
 
-       if(!xsamefile(goroot_final, goroot)) {
-               xprintf("\n"
+       if !xsamefile(goroot_final, goroot) {
+               xprintf("\n"+
                        "The binaries expect %s to be copied or moved to %s\n",
-                       goroot, goroot_final);
+                       goroot, goroot_final)
        }
-
-       bfree(&b);
-       bfree(&b1);
-       bfree(&search);
-       bfree(&path);
 }
 
 // Version prints the Go version.
-void
-cmdversion(int argc, char **argv)
-{
-       ARGBEGIN{
-       case 'v':
-               vflag++;
-               break;
-       default:
-               usage();
-       }ARGEND
-
-       if(argc > 0)
-               usage();
-
-       xprintf("%s\n", goversion);
+func cmdversion() {
+       xflagparse(0)
+       xprintf("%s\n", goversion)
 }
index 64434d51e16852112ee26aa63a503a0b3fa7bdaf..b1b5d5e7ba5869428fddff627a36378debf7d417 100644 (file)
@@ -2,7 +2,14 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-#include "a.h"
+package main
+
+import (
+       "bytes"
+       "fmt"
+       "strconv"
+       "strings"
+)
 
 /*
  * Helpers for building cmd/gc.
 // It finds the OXXX enum, pulls out all the constants
 // from OXXX to OEND, and writes a table mapping
 // op to string.
-void
-gcopnames(char *dir, char *file)
-{
-       char *p, *q;
-       int i, j, end;
-       Buf in, b, out;
-       Vec lines, fields;
-       
-       binit(&in);
-       binit(&b);
-       binit(&out);
-       vinit(&lines);
-       vinit(&fields);
-       
-       bwritestr(&out, bprintf(&b, "// auto generated by go tool dist\n"));
-       bwritestr(&out, bprintf(&b, "static char *opnames[] = {\n"));
-
-       readfile(&in, bprintf(&b, "%s/go.h", dir));
-       splitlines(&lines, bstr(&in));
-       i = 0;
-       while(i<lines.len && !contains(lines.p[i], "OXXX"))
-               i++;
-       end = 0;
-       for(; i<lines.len && !end; i++) {
-               p = xstrstr(lines.p[i], "//");
-               if(p != nil)
-                       *p = '\0';
-               end = contains(lines.p[i], "OEND");
-               splitfields(&fields, lines.p[i]);
-               for(j=0; j<fields.len; j++) {
-                       q = fields.p[j];
-                       if(*q == 'O')
-                               q++;
-                       p = q+xstrlen(q)-1;
-                       if(*p == ',')
-                               *p = '\0';
-                       bwritestr(&out, bprintf(&b, "   [O%s] = \"%s\",\n", q, q));
+func gcopnames(dir, file string) {
+       var out bytes.Buffer
+       fmt.Fprintf(&out, "// auto generated by go tool dist\n")
+       fmt.Fprintf(&out, "static char *opnames[] = {\n")
+
+       in := readfile(pathf("%s/go.h", dir))
+       lines := splitlines(in)
+       i := 0
+       for i < len(lines) && !strings.Contains(lines[i], "OXXX") {
+               i++
+       }
+       for _, line := range lines[i:] {
+               if i := strings.Index(line, "//"); i >= 0 {
+                       line = line[:i]
+               }
+               for _, field := range splitfields(line) {
+                       field = strings.TrimPrefix(field, "O")
+                       field = strings.TrimSuffix(field, ",")
+                       fmt.Fprintf(&out, "\t[O%s] = \"%s\",\n", field, field)
+               }
+               if strings.Contains(line, "OEND") {
+                       break
                }
        }
-       
-       bwritestr(&out, bprintf(&b, "};\n"));
-
-       writefile(&out, file, 0);
+       fmt.Fprintf(&out, "};\n")
 
-       bfree(&in);
-       bfree(&b);
-       bfree(&out);
-       vfree(&lines);
-       vfree(&fields);
-}
-
-static int
-xatoi(char *s, char **end)
-{
-       int val = 0;
-       for(; *s && *s >= '0' && *s <= '9'; ++s)
-               val = val * 10 + (*s - '0');
-       *end = s;
-       return val;
+       writefile(out.String(), file, 0)
 }
 
 // mkanames reads [5689].out.h and writes anames[5689].c
 // The format is much the same as the Go opcodes above.
 // It also writes out cnames array for C_* constants and the dnames
 // array for D_* constants.
-void
-mkanames(char *dir, char *file)
-{
-       int i, j, ch, n, unknown;
-       Buf in, b, out, out2;
-       Vec lines;
-       char *p, *p2;
-       Vec dnames[128];
-
-       binit(&b);
-       binit(&in);
-       binit(&out);
-       binit(&out2);
-       vinit(&lines);
-       for(i=0; i<nelem(dnames); i++)
-               vinit(&dnames[i]);
-
-       ch = file[xstrlen(file)-3];
-       bprintf(&b, "%s/../cmd/%cl/%c.out.h", dir, ch, ch);
-       readfile(&in, bstr(&b));
-       splitlines(&lines, bstr(&in));
-       
+func mkanames(dir, file string) {
+       ch := file[len(file)-3]
+       targ := pathf("%s/../cmd/%cl/%c.out.h", dir, ch, ch)
+       in := readfile(targ)
+       lines := splitlines(in)
+
        // Include link.h so that the extern declaration there is
        // checked against the non-extern declaration we are generating.
-       bwritestr(&out, bprintf(&b, "// auto generated by go tool dist\n"));
-       bwritestr(&out, bprintf(&b, "#include <u.h>\n"));
-       bwritestr(&out, bprintf(&b, "#include <libc.h>\n"));
-       bwritestr(&out, bprintf(&b, "#include <bio.h>\n"));
-       bwritestr(&out, bprintf(&b, "#include <link.h>\n"));
-       bwritestr(&out, bprintf(&b, "#include \"../cmd/%cl/%c.out.h\"\n", ch, ch));
-       bwritestr(&out, bprintf(&b, "\n"));
-
-       bwritestr(&out, bprintf(&b, "char*      anames%c[] = {\n", ch));
-       for(i=0; i<lines.len; i++) {
-               if(hasprefix(lines.p[i], "\tA")) {
-                       p = xstrstr(lines.p[i], ",");
-                       if(p)
-                               *p = '\0';
-                       p = xstrstr(lines.p[i], "\n");
-                       if(p)
-                               *p = '\0';
-                       p = lines.p[i] + 2;
-                       bwritestr(&out, bprintf(&b, "\t\"%s\",\n", p));
+       var out bytes.Buffer
+       fmt.Fprintf(&out, "// auto generated by go tool dist\n")
+       fmt.Fprintf(&out, "#include <u.h>\n")
+       fmt.Fprintf(&out, "#include <libc.h>\n")
+       fmt.Fprintf(&out, "#include <bio.h>\n")
+       fmt.Fprintf(&out, "#include <link.h>\n")
+       fmt.Fprintf(&out, "#include \"../cmd/%cl/%c.out.h\"\n", ch, ch)
+       fmt.Fprintf(&out, "\n")
+
+       fmt.Fprintf(&out, "char*        anames%c[] = {\n", ch)
+       for _, line := range lines {
+               if strings.HasPrefix(line, "\tA") {
+                       if i := strings.Index(line, ","); i >= 0 {
+                               line = line[:i]
+                       }
+                       if i := strings.Index(line, "\n"); i >= 0 {
+                               line = line[:i]
+                       }
+                       line = line[2:]
+                       fmt.Fprintf(&out, "\t\"%s\",\n", line)
                }
        }
-       bwritestr(&out, "};\n");
-
-       j=0;
-       bprintf(&out2, "char*   cnames%c[] = {\n", ch);
-       for(i=0; i<lines.len; i++) {
-               if(hasprefix(lines.p[i], "\tC_")) {
-                       p = xstrstr(lines.p[i], ",");
-                       if(p)
-                               *p = '\0';
-                       p = xstrstr(lines.p[i], "\n");
-                       if(p)
-                               *p = '\0';
-                       p = lines.p[i] + 3;
-                       bwritestr(&out2, bprintf(&b, "\t\"%s\",\n", p));
-                       j++;
+       fmt.Fprintf(&out, "};\n")
+
+       j := 0
+       var out2 bytes.Buffer
+       fmt.Fprintf(&out2, "char*       cnames%c[] = {\n", ch)
+       for _, line := range lines {
+               if strings.HasPrefix(line, "\tC_") {
+                       if i := strings.Index(line, ","); i >= 0 {
+                               line = line[:i]
+                       }
+                       if i := strings.Index(line, "\n"); i >= 0 {
+                               line = line[:i]
+                       }
+                       line = line[3:]
+                       fmt.Fprintf(&out2, "\t\"%s\",\n", line)
+                       j++
                }
        }
-       bwritestr(&out2, "};\n");
-       if(j>0)
-               bwriteb(&out, &out2);
-
-       j=unknown=0;
-       n=-1;
-       for(i=0; i<lines.len; i++) {
-               if(hasprefix(lines.p[i], "\tD_")) {
-                       p = xstrstr(lines.p[i], ",");
-                       if(p)
-                               *p = '\0';
-                       p = xstrstr(lines.p[i], "\n");
-                       if(p)
-                               *p = '\0';
+       fmt.Fprintf(&out2, "};\n")
+       if j > 0 {
+               out.Write(out2.Bytes())
+       }
+
+       var dnames [128][]string
+       j = 0
+       unknown := false
+       n := -1
+       for _, line := range lines {
+               if strings.HasPrefix(line, "\tD_") {
+                       if i := strings.Index(line, ","); i >= 0 {
+                               line = line[:i]
+                       }
 
                        // Parse explicit value, if any
-                       p = xstrstr(lines.p[i], "=");
-                       if(p) {
-                               // Skip space after '='
-                               p2 = p + 1;
-                               while(*p2 == ' ' || *p2 == '\t')
-                                       p2++;
-                               n = xatoi(p2, &p2);
-                               // We can't do anything about
-                               // non-numeric values or anything that
-                               // follows
-                               while(*p2 == ' ' || *p2 == '\t')
-                                       p2++;
-                               if(*p2 != 0) {
-                                       unknown = 1;
-                                       continue;
+                       if i := strings.Index(line, "="); i >= 0 {
+                               value := strings.TrimSpace(line[i+1:])
+                               line = strings.TrimSpace(line[:i])
+                               var err error
+                               n, err = strconv.Atoi(value)
+                               if err != nil {
+                                       // We can't do anything about
+                                       // non-numeric values or anything that
+                                       // follows.
+                                       unknown = true
+                                       continue
                                }
-                               // Truncate space before '='
-                               while(*(p-1) == ' ' || *(p-1) == '\t')
-                                       p--;
-                               *p = '\0';
-                               unknown = 0;
+                               unknown = false
                        } else {
-                               n++;
+                               n++
+                       }
+
+                       if unknown || n < 0 || n >= len(dnames) {
+                               continue
                        }
 
-                       if(unknown || n >= nelem(dnames))
-                               continue;
+                       line = strings.TrimSpace(line)
+                       line = line[len("D_"):]
 
-                       p = lines.p[i] + 3;
-                       if(xstrcmp(p, "LAST") == 0)
-                               continue;
-                       vadd(&dnames[n], p);
-                       j++;
+                       if strings.Contains(line, "LAST") {
+                               continue
+                       }
+                       dnames[n] = append(dnames[n], line)
+                       j++
                }
        }
-       if(j>0){
-               bwritestr(&out, bprintf(&b, "char*      dnames%c[D_LAST] = {\n", ch));
-               for(i=0; i<nelem(dnames); i++) {
-                       if(dnames[i].len == 0)
-                               continue;
-                       bwritestr(&out, bprintf(&b, "\t[D_%s] = \"", dnames[i].p[0]));
-                       for(j=0; j<dnames[i].len; j++) {
-                               if(j != 0)
-                                       bwritestr(&out, "/");
-                               bwritestr(&out, dnames[i].p[j]);
+
+       if j > 0 {
+               fmt.Fprintf(&out, "char*        dnames%c[D_LAST] = {\n", ch)
+               for _, d := range dnames {
+                       if len(d) == 0 {
+                               continue
                        }
-                       bwritestr(&out, "\",\n");
+                       fmt.Fprintf(&out, "\t[D_%s] = \"", d[0])
+                       for k, name := range d {
+                               if k > 0 {
+                                       fmt.Fprintf(&out, "/")
+                               }
+                               fmt.Fprintf(&out, "%s", name)
+                       }
+                       fmt.Fprintf(&out, "\",\n")
                }
-               bwritestr(&out, "};\n");
+               fmt.Fprintf(&out, "};\n")
        }
 
-       writefile(&out, file, 0);
-
-       bfree(&b);
-       bfree(&in);
-       bfree(&out);
-       bfree(&out2);
-       vfree(&lines);
-       for(i=0; i<nelem(dnames); i++)
-               vfree(&dnames[i]);
+       writefile(out.String(), file, 0)
 }
index 41208fac5f734d3e3078d868a77a5c88d11fbdcc..9cc650840d4bb0ae48877038ca502ee65ab07548 100644 (file)
@@ -2,7 +2,9 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-#include "a.h"
+package main
+
+import "fmt"
 
 /*
  * Helpers for building cmd/go and cmd/cgo.
 //     const defaultCXX = <defaultcxx>
 //
 // It is invoked to write cmd/go/zdefaultcc.go
-// but we also write cmd/cgo/zdefaultcc.go.
-void
-mkzdefaultcc(char *dir, char *file)
-{
-       Buf b, out;
-       
-       USED(dir);
-
-       binit(&out);
-       bprintf(&out,
-               "// auto generated by go tool dist\n"
-               "\n"
-               "package main\n"
-               "\n"
-               "const defaultCC = `%s`\n"
-               "const defaultCXX = `%s`\n",
-               defaultcctarget, defaultcxxtarget);
+// but we also write cmd/cgo/zdefaultcc.go
+func mkzdefaultcc(dir, file string) {
+       var out string
 
-       writefile(&out, file, 0);
+       out = fmt.Sprintf(
+               "// auto generated by go tool dist\n"+
+                       "\n"+
+                       "package main\n"+
+                       "\n"+
+                       "const defaultCC = `%s`\n"+
+                       "const defaultCXX = `%s`\n",
+               defaultcctarget, defaultcxxtarget)
 
-       // Convert file name to replace.
-       binit(&b);      
-       bwritestr(&b, file);
-       if(slash[0] == '/')
-               bsubst(&b, "/go/zdefaultcc.go", "/cgo/zdefaultcc.go");
-       else
-               bsubst(&b, "\\go\\zdefaultcc.go", "\\cgo\\zdefaultcc.go");
-       writefile(&out, bstr(&b), 0);
+       writefile(out, file, 0)
 
-       bfree(&b);
-       bfree(&out);
+       // Convert file name to replace: turn go into cgo.
+       i := len(file) - len("go/zdefaultcc.go")
+       file = file[:i] + "c" + file[i:]
+       writefile(out, file, 0)
 }
index add68976825a7388fdaafab234ead55d09195c86..c0ec2efbd6cae94f00f906d96798b2cc115ffae0 100644 (file)
@@ -2,7 +2,12 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-#include "a.h"
+package main
+
+import (
+       "fmt"
+       "os"
+)
 
 /*
  * Helpers for building runtime.
 //     const defaultGoroot = <goroot>
 //     const theVersion = <version>
 //
-void
-mkzversion(char *dir, char *file)
-{
-       Buf b, out;
-       
-       USED(dir);
-
-       binit(&b);
-       binit(&out);
-       
-       bwritestr(&out, bprintf(&b,
-               "// auto generated by go tool dist\n"
-               "\n"
-               "package runtime\n"
-               "\n"
-               "const defaultGoroot = `%s`\n"
-               "const theVersion = `%s`\n"
-               "var buildVersion = theVersion\n", goroot_final, goversion));
-
-       writefile(&out, file, 0);
-       
-       bfree(&b);
-       bfree(&out);
+func mkzversion(dir, file string) {
+       out := fmt.Sprintf(
+               "// auto generated by go tool dist\n"+
+                       "\n"+
+                       "package runtime\n"+
+                       "\n"+
+                       "const defaultGoroot = `%s`\n"+
+                       "const theVersion = `%s`\n"+
+                       "var buildVersion = theVersion\n", goroot_final, goversion)
+
+       writefile(out, file, 0)
 }
 
 // mkzexperiment writes zaexperiment.h (sic):
 //
 //     #define GOEXPERIMENT "experiment string"
 //
-void
-mkzexperiment(char *dir, char *file)
-{
-       Buf b, out, exp;
-       
-       USED(dir);
-
-       binit(&b);
-       binit(&out);
-       binit(&exp);
-       
-       xgetenv(&exp, "GOEXPERIMENT");
-       bwritestr(&out, bprintf(&b,
-               "// auto generated by go tool dist\n"
-               "\n"
-               "#define GOEXPERIMENT \"%s\"\n", bstr(&exp)));
+func mkzexperiment(dir, file string) {
+       out := fmt.Sprintf(
+               "// auto generated by go tool dist\n"+
+                       "\n"+
+                       "#define GOEXPERIMENT \"%s\"\n", os.Getenv("GOEXPERIMENT"))
 
-       writefile(&out, file, 0);
-       
-       bfree(&b);
-       bfree(&out);
-       bfree(&exp);
+       writefile(out, file, 0)
 }
diff --git a/src/cmd/dist/cpuid_386.s b/src/cmd/dist/cpuid_386.s
new file mode 100644 (file)
index 0000000..853824a
--- /dev/null
@@ -0,0 +1,14 @@
+// Copyright 2015 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.
+
+TEXT ·cpuid(SB),$0-8
+       MOVL ax+4(FP), AX
+       CPUID
+       MOVL info+0(FP), DI
+       MOVL AX, 0(DI)
+       MOVL BX, 4(DI)
+       MOVL CX, 8(DI)
+       MOVL DX, 12(DI)
+       RET
+
diff --git a/src/cmd/dist/cpuid_amd64.s b/src/cmd/dist/cpuid_amd64.s
new file mode 100644 (file)
index 0000000..dbb1085
--- /dev/null
@@ -0,0 +1,14 @@
+// Copyright 2015 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.
+
+TEXT ·cpuid(SB),$0-12
+       MOVL ax+8(FP), AX
+       CPUID
+       MOVQ info+0(FP), DI
+       MOVL AX, 0(DI)
+       MOVL BX, 4(DI)
+       MOVL CX, 8(DI)
+       MOVL DX, 12(DI)
+       RET
+
diff --git a/src/cmd/dist/cpuid_default.s b/src/cmd/dist/cpuid_default.s
new file mode 100644 (file)
index 0000000..e5bfd18
--- /dev/null
@@ -0,0 +1,10 @@
+// Copyright 2015 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.
+
+// +build !386,!amd64
+
+#include "textflag.h"
+
+TEXT ·cpuid(SB),NOSPLIT,$0-0
+       RET
index fad01802a5952b6c2456d8d3b848dea4757b1fd5..a2ac65ee87ffb70b13dd23f89c9dd6ffca65e9bc 100644 (file)
@@ -2,41 +2,84 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-#include "a.h"
+package main
 
-int vflag;
-int sflag;
-char *argv0;
+import (
+       "flag"
+       "fmt"
+       "os"
+       "strconv"
+)
 
 // cmdtab records the available commands.
-static struct {
-       char *name;
-       void (*f)(int, char**);
-} cmdtab[] = {
+var cmdtab = []struct {
+       name string
+       f    func()
+}{
        {"banner", cmdbanner},
        {"bootstrap", cmdbootstrap},
        {"clean", cmdclean},
        {"env", cmdenv},
        {"install", cmdinstall},
        {"version", cmdversion},
-};
+}
 
 // The OS-specific main calls into the portable code here.
-void
-xmain(int argc, char **argv)
-{
-       int i;
-
-       if(argc <= 1)
-               usage();
-       
-       for(i=0; i<nelem(cmdtab); i++) {
-               if(streq(cmdtab[i].name, argv[1])) {
-                       cmdtab[i].f(argc-1, argv+1);
-                       return;
+func xmain() {
+       if len(os.Args) < 2 {
+               usage()
+       }
+       cmd := os.Args[1]
+       os.Args = os.Args[1:] // for flag parsing during cmd
+       for _, ct := range cmdtab {
+               if ct.name == cmd {
+                       flag.Usage = func() {
+                               fmt.Fprintf(os.Stderr, "usage: go tool dist %s [options]\n", cmd)
+                               flag.PrintDefaults()
+                               os.Exit(2)
+                       }
+                       ct.f()
+                       return
+               }
+       }
+
+       xprintf("unknown command %s\n", cmd)
+       usage()
+}
+
+func xflagparse(maxargs int) {
+       flag.Var((*count)(&vflag), "v", "verbosity")
+       flag.Parse()
+       if maxargs >= 0 && flag.NArg() > maxargs {
+               flag.Usage()
+       }
+}
+
+// count is a flag.Value that is like a flag.Bool and a flag.Int.
+// If used as -name, it increments the count, but -name=x sets the count.
+// Used for verbose flag -v.
+type count int
+
+func (c *count) String() string {
+       return fmt.Sprint(int(*c))
+}
+
+func (c *count) Set(s string) error {
+       switch s {
+       case "true":
+               *c++
+       case "false":
+               *c = 0
+       default:
+               n, err := strconv.Atoi(s)
+               if err != nil {
+                       return fmt.Errorf("invalid count %q", s)
                }
+               *c = count(n)
        }
+       return nil
+}
 
-       xprintf("unknown command %s\n", argv[1]);
-       usage();
+func (c *count) IsBoolFlag() bool {
+       return true
 }
diff --git a/src/cmd/dist/sys_default.go b/src/cmd/dist/sys_default.go
new file mode 100644 (file)
index 0000000..ab97f19
--- /dev/null
@@ -0,0 +1,10 @@
+// Copyright 2015 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.
+
+// +build !windows,!plan9
+
+package main
+
+func sysinit() {
+}
diff --git a/src/cmd/dist/sys_windows.go b/src/cmd/dist/sys_windows.go
new file mode 100644 (file)
index 0000000..c6867fb
--- /dev/null
@@ -0,0 +1,49 @@
+// Copyright 2015 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 (
+       "syscall"
+       "unsafe"
+)
+
+var (
+       modkernel32       = syscall.NewLazyDLL("kernel32.dll")
+       procGetSystemInfo = syscall.NewProc("GetSystemInfo")
+)
+
+// see http://msdn.microsoft.com/en-us/library/windows/desktop/ms724958(v=vs.85).aspx
+type systeminfo struct {
+       wProcessorArchitecture      uint16
+       wReserved                   uint16
+       dwPageSize                  uint32
+       lpMinimumApplicationAddress uintptr
+       lpMaximumApplicationAddress uintptr
+       dwActiveProcessorMask       uintptr
+       dwNumberOfProcessors        uint32
+       dwProcessorType             uint32
+       dwAllocationGranularity     uint32
+       wProcessorLevel             uint16
+       wProcessorRevision          uint16
+}
+
+const (
+       PROCESSOR_ARCHITECTURE_AMD64 = 9
+       PROCESSOR_ARCHITECTURE_INTEL = 0
+)
+
+var sysinfo systeminfo
+
+func sysinit() {
+       syscall.Syscall(procGetSystemInfo.Addr(), 1, uintptr(unsafe.Pointer(&sysinfo)), 0, 0)
+       switch sysinfo.wProcessorArchitecture {
+       case PROCESSOR_ARCHITECTURE_AMD64:
+               gohostarch = "amd64"
+       case PROCESSOR_ARCHITECTURE_INTEL:
+               gohostarch = "386"
+       default:
+               fatal("unknown processor architecture")
+       }
+}
index 0fd17c15091461152d60929f0566cd1447c2ca59..4628eead80fe2f4c5a2c8ac1b600f5a922254606 100644 (file)
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// These #ifdefs are being used as a substitute for
-// build configuration, so that on any system, this
-// tool can be built with the local equivalent of
-//     cc *.c
-//
-#ifndef WIN32
-#ifndef PLAN9
-
-#include "a.h"
-#include <unistd.h>
-#include <dirent.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-#include <sys/param.h>
-#include <sys/utsname.h>
-#include <fcntl.h>
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <stdarg.h>
-#include <setjmp.h>
-#include <signal.h>
-
-// bprintf replaces the buffer with the result of the printf formatting
-// and returns a pointer to the NUL-terminated buffer contents.
-char*
-bprintf(Buf *b, char *fmt, ...)
-{
-       va_list arg;
-       char buf[4096];
-       
-       breset(b);
-       va_start(arg, fmt);
-       vsnprintf(buf, sizeof buf, fmt, arg);
-       va_end(arg);
-       bwritestr(b, buf);
-       return bstr(b);
-}
-
-// bpathf is the same as bprintf (on windows it turns / into \ after the printf).
-// It returns a pointer to the NUL-terminated buffer contents.
-char*
-bpathf(Buf *b, char *fmt, ...)
-{
-       va_list arg;
-       char buf[4096];
-       
-       breset(b);
-       va_start(arg, fmt);
-       vsnprintf(buf, sizeof buf, fmt, arg);
-       va_end(arg);
-       bwritestr(b, buf);
-       return bstr(b);
-}
-
-// bwritef is like bprintf but does not reset the buffer
-// and does not return the NUL-terminated string.
-void
-bwritef(Buf *b, char *fmt, ...)
-{
-       va_list arg;
-       char buf[4096];
-       
-       va_start(arg, fmt);
-       vsnprintf(buf, sizeof buf, fmt, arg);
-       va_end(arg);
-       bwritestr(b, buf);
-}
-
-// breadfrom appends to b all the data that can be read from fd.
-static void
-breadfrom(Buf *b, int fd)
-{
-       int n;
-
-       for(;;) {
-               bgrow(b, 4096);
-               n = read(fd, b->p+b->len, 4096);
-               if(n < 0)
-                       fatal("read: %s", strerror(errno));
-               if(n == 0)
-                       break;
-               b->len += n;
-       }
-}
-
-// xgetenv replaces b with the value of the named environment variable.
-void
-xgetenv(Buf *b, char *name)
-{
-       char *p;
-       
-       breset(b);
-       p = getenv(name);
-       if(p != NULL)
-               bwritestr(b, p);
-}
-
-static void genrun(Buf *b, char *dir, int mode, Vec *argv, int bg);
-
-// run runs the command named by cmd.
-// If b is not nil, run replaces b with the output of the command.
-// If dir is not nil, run runs the command in that directory.
-// If mode is CheckExit, run calls fatal if the command is not successful.
-void
-run(Buf *b, char *dir, int mode, char *cmd, ...)
-{
-       va_list arg;
-       Vec argv;
-       char *p;
-       
-       vinit(&argv);
-       vadd(&argv, cmd);
-       va_start(arg, cmd);
-       while((p = va_arg(arg, char*)) != nil)
-               vadd(&argv, p);
-       va_end(arg);
-       
-       runv(b, dir, mode, &argv);
-       
-       vfree(&argv);
-}
-
-// runv is like run but takes a vector.
-void
-runv(Buf *b, char *dir, int mode, Vec *argv)
-{
-       genrun(b, dir, mode, argv, 1);
-}
-
-// bgrunv is like run but runs the command in the background.
-// bgwait waits for pending bgrunv to finish.
-void
-bgrunv(char *dir, int mode, Vec *argv)
-{
-       genrun(nil, dir, mode, argv, 0);
-}
-
-#define MAXBG 4 /* maximum number of jobs to run at once */
-
-static struct {
-       int pid;
-       int mode;
-       char *cmd;
-       Buf *b;
-} bg[MAXBG];
-static int nbg;
-static int maxnbg = nelem(bg);
-
-static void bgwait1(void);
-
-// genrun is the generic run implementation.
-static void
-genrun(Buf *b, char *dir, int mode, Vec *argv, int wait)
-{
-       int i, p[2], pid;
-       Buf cmd;
-       char *q;
-
-       while(nbg >= maxnbg)
-               bgwait1();
-
-       // Generate a copy of the command to show in a log.
-       // Substitute $WORK for the work directory.
-       binit(&cmd);
-       for(i=0; i<argv->len; i++) {
-               if(i > 0)
-                       bwritestr(&cmd, " ");
-               q = argv->p[i];
-               if(workdir != nil && hasprefix(q, workdir)) {
-                       bwritestr(&cmd, "$WORK");
-                       q += strlen(workdir);
+package main
+
+import (
+       "fmt"
+       "io/ioutil"
+       "os"
+       "os/exec"
+       "path/filepath"
+       "runtime"
+       "sort"
+       "strconv"
+       "strings"
+       "sync"
+       "sync/atomic"
+       "time"
+)
+
+// pathf is fmt.Sprintf for generating paths
+// (on windows it turns / into \ after the printf).
+func pathf(format string, args ...interface{}) string {
+       return filepath.Clean(fmt.Sprintf(format, args...))
+}
+
+// filter returns a slice containing the elements x from list for which f(x) == true.
+func filter(list []string, f func(string) bool) []string {
+       var out []string
+       for _, x := range list {
+               if f(x) {
+                       out = append(out, x)
                }
-               bwritestr(&cmd, q);
-       }
-       if(vflag > 1)
-               errprintf("%s\n", bstr(&cmd));
-
-       if(b != nil) {
-               breset(b);
-               if(pipe(p) < 0)
-                       fatal("pipe: %s", strerror(errno));
-       }
-
-       switch(pid = fork()) {
-       case -1:
-               fatal("fork: %s", strerror(errno));
-       case 0:
-               if(b != nil) {
-                       close(0);
-                       close(p[0]);
-                       dup2(p[1], 1);
-                       dup2(p[1], 2);
-                       if(p[1] > 2)
-                               close(p[1]);
+       }
+       return out
+}
+
+// uniq returns a sorted slice containing the unique elements of list.
+func uniq(list []string) []string {
+       out := make([]string, len(list))
+       copy(out, list)
+       sort.Strings(out)
+       keep := out[:0]
+       for _, x := range out {
+               if len(keep) == 0 || keep[len(keep)-1] != x {
+                       keep = append(keep, x)
+               }
+       }
+       return keep
+}
+
+// splitlines returns a slice with the result of splitting
+// the input p after each \n.
+func splitlines(p string) []string {
+       return strings.SplitAfter(p, "\n")
+}
+
+// splitfields replaces the vector v with the result of splitting
+// the input p into non-empty fields containing no spaces.
+func splitfields(p string) []string {
+       return strings.Fields(p)
+}
+
+const (
+       CheckExit = 1 << iota
+       ShowOutput
+       Background
+)
+
+var outputLock sync.Mutex
+
+// run runs the command line cmd in dir.
+// If mode has ShowOutput set, run collects cmd's output and returns it as a string;
+// otherwise, run prints cmd's output to standard output after the command finishes.
+// If mode has CheckExit set and the command fails, run calls fatal.
+// If mode has Background set, this command is being run as a
+// Background job. Only bgrun should use the Background mode,
+// not other callers.
+func run(dir string, mode int, cmd ...string) string {
+       if vflag > 1 {
+               errprintf("run: %s\n", strings.Join(cmd, " "))
+       }
+
+       xcmd := exec.Command(cmd[0], cmd[1:]...)
+       xcmd.Dir = dir
+       var err error
+       data, err := xcmd.CombinedOutput()
+       if err != nil && mode&CheckExit != 0 {
+               outputLock.Lock()
+               if len(data) > 0 {
+                       xprintf("%s\n", data)
+               }
+               outputLock.Unlock()
+               atomic.AddInt32(&ndone, +1)
+               die := func() {
+                       time.Sleep(100 * time.Millisecond)
+                       fatal("FAILED: %v", strings.Join(cmd, " "))
                }
-               if(dir != nil) {
-                       if(chdir(dir) < 0) {
-                               fprintf(stderr, "chdir %s: %s\n", dir, strerror(errno));
-                               _exit(1);
-                       }
+               if mode&Background != 0 {
+                       // This is a background run, and fatal will
+                       // wait for it to finish before exiting.
+                       // If we call fatal directly, that's a deadlock.
+                       // Instead, call fatal in a background goroutine
+                       // and let this run return normally, so that
+                       // fatal can wait for it to finish.
+                       go die()
+               } else {
+                       die()
                }
-               vadd(argv, nil);
-               execvp(argv->p[0], argv->p);
-               fprintf(stderr, "%s\n", bstr(&cmd));
-               fprintf(stderr, "exec %s: %s\n", argv->p[0], strerror(errno));
-               _exit(1);
-       }
-       if(b != nil) {
-               close(p[1]);
-               breadfrom(b, p[0]);
-               close(p[0]);
-       }
-
-       if(nbg < 0)
-               fatal("bad bookkeeping");
-       bg[nbg].pid = pid;
-       bg[nbg].mode = mode;
-       bg[nbg].cmd = btake(&cmd);
-       bg[nbg].b = b;
-       nbg++;
-       
-       if(wait)
-               bgwait();
-
-       bfree(&cmd);
-}
-
-// bgwait1 waits for a single background job.
-static void
-bgwait1(void)
-{
-       int i, pid, status, mode;
-       char *cmd;
-       Buf *b;
-
-       errno = 0;
-       while((pid = wait(&status)) < 0) {
-               if(errno != EINTR)
-                       fatal("waitpid: %s", strerror(errno));
-       }
-       for(i=0; i<nbg; i++)
-               if(bg[i].pid == pid)
-                       goto ok;
-       fatal("waitpid: unexpected pid");
-
-ok:
-       cmd = bg[i].cmd;
-       mode = bg[i].mode;
-       bg[i].pid = 0;
-       b = bg[i].b;
-       bg[i].b = nil;
-       bg[i] = bg[--nbg];
-       
-       if(mode == CheckExit && (!WIFEXITED(status) || WEXITSTATUS(status) != 0)) {
-               if(b != nil)
-                       xprintf("%s\n", bstr(b));
-               fatal("FAILED: %s", cmd);
-       }
-       xfree(cmd);
-}
-
-// bgwait waits for all the background jobs.
-void
-bgwait(void)
-{
-       while(nbg > 0)
-               bgwait1();
-}
-
-// xgetwd replaces b with the current directory.
-void
-xgetwd(Buf *b)
-{
-       char buf[MAXPATHLEN];
-       
-       breset(b);
-       if(getcwd(buf, MAXPATHLEN) == nil)
-               fatal("getcwd: %s", strerror(errno));
-       bwritestr(b, buf);      
-}
-
-// xrealwd replaces b with the 'real' name for the given path.
-// real is defined as what getcwd returns in that directory.
-void
-xrealwd(Buf *b, char *path)
-{
-       int fd;
-       
-       fd = open(".", 0);
-       if(fd < 0)
-               fatal("open .: %s", strerror(errno));
-       if(chdir(path) < 0)
-               fatal("chdir %s: %s", path, strerror(errno));
-       xgetwd(b);
-       if(fchdir(fd) < 0)
-               fatal("fchdir: %s", strerror(errno));
-       close(fd);
+       }
+       if mode&ShowOutput != 0 {
+               os.Stdout.Write(data)
+       }
+       return string(data)
+}
+
+var maxbg = 4 /* maximum number of jobs to run at once */
+
+var (
+       bgwork = make(chan func())
+       bgdone = make(chan struct{}, 1e6)
+       nwork  int32
+       ndone  int32
+)
+
+func bginit() {
+       for i := 0; i < maxbg; i++ {
+               go bghelper()
+       }
+}
+
+func bghelper() {
+       for {
+               (<-bgwork)()
+       }
+}
+
+// bgrun is like run but runs the command in the background.
+// CheckExit|ShowOutput mode is implied (since output cannot be returned).
+func bgrun(dir string, cmd ...string) {
+       bgwork <- func() {
+               run(dir, CheckExit|ShowOutput|Background, cmd...)
+       }
+}
+
+// bgwait waits for pending bgruns to finish.
+func bgwait() {
+       var wg sync.WaitGroup
+       wg.Add(maxbg)
+       for i := 0; i < maxbg; i++ {
+               bgwork <- func() {
+                       wg.Done()
+                       wg.Wait()
+               }
+       }
+       wg.Wait()
+}
+
+// xgetwd returns the current directory.
+func xgetwd() string {
+       wd, err := os.Getwd()
+       if err != nil {
+               fatal("%s", err)
+       }
+       return wd
+}
+
+// xrealwd returns the 'real' name for the given path.
+// real is defined as what xgetwd returns in that directory.
+func xrealwd(path string) string {
+       old := xgetwd()
+       if err := os.Chdir(path); err != nil {
+               fatal("chdir %s: %v", path, err)
+       }
+       real := xgetwd()
+       if err := os.Chdir(old); err != nil {
+               fatal("chdir %s: %v", old, err)
+       }
+       return real
 }
 
 // isdir reports whether p names an existing directory.
-bool
-isdir(char *p)
-{
-       struct stat st;
-       
-       return stat(p, &st) >= 0 && S_ISDIR(st.st_mode);
+func isdir(p string) bool {
+       fi, err := os.Stat(p)
+       return err == nil && fi.IsDir()
 }
 
 // isfile reports whether p names an existing file.
-bool
-isfile(char *p)
-{
-       struct stat st;
-       
-       return stat(p, &st) >= 0 && S_ISREG(st.st_mode);
+func isfile(p string) bool {
+       fi, err := os.Stat(p)
+       return err == nil && fi.Mode().IsRegular()
 }
 
 // mtime returns the modification time of the file p.
-Time
-mtime(char *p)
-{
-       struct stat st;
-       
-       if(stat(p, &st) < 0)
-               return 0;
-       return (Time)st.st_mtime*1000000000LL;
+func mtime(p string) time.Time {
+       fi, err := os.Stat(p)
+       if err != nil {
+               return time.Time{}
+       }
+       return fi.ModTime()
 }
 
 // isabs reports whether p is an absolute path.
-bool
-isabs(char *p)
-{
-       return hasprefix(p, "/");
-}
-
-// readfile replaces b with the content of the named file.
-void
-readfile(Buf *b, char *file)
-{
-       int fd;
-       
-       breset(b);
-       fd = open(file, 0);
-       if(fd < 0)
-               fatal("open %s: %s", file, strerror(errno));
-       breadfrom(b, fd);
-       close(fd);
+func isabs(p string) bool {
+       return filepath.IsAbs(p)
+}
+
+// readfile returns the content of the named file.
+func readfile(file string) string {
+       data, err := ioutil.ReadFile(file)
+       if err != nil {
+               fatal("%v", err)
+       }
+       return string(data)
 }
 
 // writefile writes b to the named file, creating it if needed.  if
 // exec is non-zero, marks the file as executable.
-void
-writefile(Buf *b, char *file, int exec)
-{
-       int fd;
-       
-       fd = creat(file, 0666);
-       if(fd < 0)
-               fatal("create %s: %s", file, strerror(errno));
-       if(write(fd, b->p, b->len) != b->len)
-               fatal("short write: %s", strerror(errno));
-       if(exec)
-               fchmod(fd, 0755);
-       close(fd);
+func writefile(b, file string, exec int) {
+       mode := os.FileMode(0666)
+       if exec != 0 {
+               mode = 0777
+       }
+       err := ioutil.WriteFile(file, []byte(b), mode)
+       if err != nil {
+               fatal("%v", err)
+       }
 }
 
 // xmkdir creates the directory p.
-void
-xmkdir(char *p)
-{
-       if(mkdir(p, 0777) < 0)
-               fatal("mkdir %s: %s", p, strerror(errno));
+func xmkdir(p string) {
+       err := os.Mkdir(p, 0777)
+       if err != nil {
+               fatal("%v", err)
+       }
 }
 
 // xmkdirall creates the directory p and its parents, as needed.
-void
-xmkdirall(char *p)
-{
-       char *q;
-
-       if(isdir(p))
-               return;
-       q = strrchr(p, '/');
-       if(q != nil) {
-               *q = '\0';
-               xmkdirall(p);
-               *q = '/';
+func xmkdirall(p string) {
+       err := os.MkdirAll(p, 0777)
+       if err != nil {
+               fatal("%v", err)
        }
-       xmkdir(p);
 }
 
 // xremove removes the file p.
-void
-xremove(char *p)
-{
-       if(vflag > 2)
-               errprintf("rm %s\n", p);
-       unlink(p);
+func xremove(p string) {
+       if vflag > 2 {
+               errprintf("rm %s\n", p)
+       }
+       os.Remove(p)
 }
 
 // xremoveall removes the file or directory tree rooted at p.
-void
-xremoveall(char *p)
-{
-       int i;
-       Buf b;
-       Vec dir;
-
-       binit(&b);
-       vinit(&dir);
-
-       if(isdir(p)) {
-               xreaddir(&dir, p);
-               for(i=0; i<dir.len; i++) {
-                       bprintf(&b, "%s/%s", p, dir.p[i]);
-                       xremoveall(bstr(&b));
-               }
-               if(vflag > 2)
-                       errprintf("rm %s\n", p);
-               rmdir(p);
-       } else {
-               if(vflag > 2)
-                       errprintf("rm %s\n", p);
-               unlink(p);
+func xremoveall(p string) {
+       if vflag > 2 {
+               errprintf("rm -r %s\n", p)
        }
-       
-       bfree(&b);
-       vfree(&dir);
+       os.RemoveAll(p)
 }
 
 // xreaddir replaces dst with a list of the names of the files in dir.
 // The names are relative to dir; they are not full paths.
-void
-xreaddir(Vec *dst, char *dir)
-{
-       DIR *d;
-       struct dirent *dp;
-
-       vreset(dst);
-       d = opendir(dir);
-       if(d == nil)
-               fatal("opendir %s: %s", dir, strerror(errno));
-       while((dp = readdir(d)) != nil) {
-               if(streq(dp->d_name, ".") || streq(dp->d_name, ".."))
-                       continue;
-               vadd(dst, dp->d_name);
+func xreaddir(dir string) []string {
+       f, err := os.Open(dir)
+       if err != nil {
+               fatal("%v", err)
        }
-       closedir(d);
+       defer f.Close()
+       names, err := f.Readdirnames(-1)
+       if err != nil {
+               fatal("reading %s: %v", dir, err)
+       }
+       return names
 }
 
 // xworkdir creates a new temporary directory to hold object files
 // and returns the name of that directory.
-char*
-xworkdir(void)
-{
-       Buf b;
-       char *p;
-
-       binit(&b);
-
-       xgetenv(&b, "TMPDIR");
-       if(b.len == 0)
-               bwritestr(&b, "/var/tmp");
-       if(b.p[b.len-1] != '/')
-               bwrite(&b, "/", 1);
-       bwritestr(&b, "go-cbuild-XXXXXX");
-       p = bstr(&b);
-       if(mkdtemp(p) == nil)
-               fatal("mkdtemp(%s): %s", p, strerror(errno));
-       p = btake(&b);
-
-       bfree(&b);
-
-       return p;
+func xworkdir() string {
+       name, err := ioutil.TempDir("", "go-tool-dist-")
+       if err != nil {
+               fatal("%v", err)
+       }
+       return name
 }
 
 // fatal prints an error message to standard error and exits.
-void
-fatal(char *msg, ...)
-{
-       va_list arg;
-       
-       fflush(stdout);
-       fprintf(stderr, "go tool dist: ");
-       va_start(arg, msg);
-       vfprintf(stderr, msg, arg);
-       va_end(arg);
-       fprintf(stderr, "\n");
-       
-       bgwait();
-       exit(1);
-}
-
-// xmalloc returns a newly allocated zeroed block of n bytes of memory.
-// It calls fatal if it runs out of memory.
-void*
-xmalloc(int n)
-{
-       void *p;
-       
-       p = malloc(n);
-       if(p == nil)
-               fatal("out of memory");
-       memset(p, 0, n);
-       return p;
-}
-
-// xstrdup returns a newly allocated copy of p.
-// It calls fatal if it runs out of memory.
-char*
-xstrdup(char *p)
-{
-       p = strdup(p);
-       if(p == nil)
-               fatal("out of memory");
-       return p;
-}
-
-// xrealloc grows the allocation p to n bytes and
-// returns the new (possibly moved) pointer.
-// It calls fatal if it runs out of memory.
-void*
-xrealloc(void *p, int n)
-{
-       p = realloc(p, n);
-       if(p == nil)
-               fatal("out of memory");
-       return p;
-}
-
-// xfree frees the result returned by xmalloc, xstrdup, or xrealloc.
-void
-xfree(void *p)
-{
-       free(p);
-}
-
-// hassuffix reports whether p ends with suffix.
-bool
-hassuffix(char *p, char *suffix)
-{
-       int np, ns;
-
-       np = strlen(p);
-       ns = strlen(suffix);
-       return np >= ns && streq(p+np-ns, suffix);
-}
-
-// hasprefix reports whether p begins with prefix.
-bool
-hasprefix(char *p, char *prefix)
-{
-       return strncmp(p, prefix, strlen(prefix)) == 0;
-}
-
-// contains reports whether sep appears in p.
-bool
-contains(char *p, char *sep)
-{
-       return strstr(p, sep) != nil;
-}
-
-// streq reports whether p and q are the same string.
-bool
-streq(char *p, char *q)
-{
-       return strcmp(p, q) == 0;
-}
-
-// lastelem returns the final path element in p.
-char*
-lastelem(char *p)
-{
-       char *out;
-
-       out = p;
-       for(; *p; p++)
-               if(*p == '/')
-                       out = p+1;
-       return out;
-}
-
-// xmemmove copies n bytes from src to dst.
-void
-xmemmove(void *dst, void *src, int n)
-{
-       memmove(dst, src, n);
-}
-
-// xmemcmp compares the n-byte regions starting at a and at b.
-int
-xmemcmp(void *a, void *b, int n)
-{
-       return memcmp(a, b, n);
-}
-
-// xstrlen returns the length of the NUL-terminated string at p.
-int
-xstrlen(char *p)
-{
-       return strlen(p);
+func fatal(format string, args ...interface{}) {
+       fmt.Fprintf(os.Stderr, "go tool dist: %s\n", fmt.Sprintf(format, args...))
+       bgwait()
+       xexit(2)
 }
 
+var atexits []func()
+
 // xexit exits the process with return code n.
-void
-xexit(int n)
-{
-       exit(n);
+func xexit(n int) {
+       for i := len(atexits) - 1; i >= 0; i-- {
+               atexits[i]()
+       }
+       os.Exit(n)
 }
 
 // xatexit schedules the exit-handler f to be run when the program exits.
-void
-xatexit(void (*f)(void))
-{
-       atexit(f);
+func xatexit(f func()) {
+       atexits = append(atexits, f)
 }
 
 // xprintf prints a message to standard output.
-void
-xprintf(char *fmt, ...)
-{
-       va_list arg;
-       
-       va_start(arg, fmt);
-       vprintf(fmt, arg);
-       va_end(arg);
+func xprintf(format string, args ...interface{}) {
+       fmt.Printf(format, args...)
 }
 
 // errprintf prints a message to standard output.
-void
-errprintf(char *fmt, ...)
-{
-       va_list arg;
-       
-       va_start(arg, fmt);
-       vfprintf(stderr, fmt, arg);
-       va_end(arg);
-}
-
-// xsetenv sets the environment variable $name to the given value.
-void
-xsetenv(char *name, char *value)
-{
-       setenv(name, value, 1);
+func errprintf(format string, args ...interface{}) {
+       fmt.Fprintf(os.Stderr, format, args...)
 }
 
 // main takes care of OS-specific startup and dispatches to xmain.
-int
-main(int argc, char **argv)
-{
-       Buf b;
-       int osx;
-       struct utsname u;
-
-       setvbuf(stdout, nil, _IOLBF, 0);
-       setvbuf(stderr, nil, _IOLBF, 0);
-
-       setenv("TERM", "dumb", 1); // disable escape codes in clang errors
-
-       binit(&b);
-       
-       slash = "/";
-
-#if defined(__APPLE__)
-       gohostos = "darwin";
-       // Even on 64-bit platform, darwin uname -m prints i386.
-       run(&b, nil, 0, "sysctl", "machdep.cpu.extfeatures", nil);
-       if(contains(bstr(&b), "EM64T"))
-               gohostarch = "amd64";
-#elif defined(__linux__)
-       gohostos = "linux";
-#elif defined(__DragonFly__)
-       gohostos = "dragonfly";
-#elif defined(__FreeBSD__)
-       gohostos = "freebsd";
-#elif defined(__FreeBSD_kernel__)
-       // detect debian/kFreeBSD. 
-       // http://wiki.debian.org/Debian_GNU/kFreeBSD_FAQ#Q._How_do_I_detect_kfreebsd_with_preprocessor_directives_in_a_C_program.3F
-       gohostos = "freebsd";   
-#elif defined(__OpenBSD__)
-       gohostos = "openbsd";
-#elif defined(__NetBSD__)
-       gohostos = "netbsd";
-#elif defined(__sun) && defined(__SVR4)
-       gohostos = "solaris";
-       // Even on 64-bit platform, solaris uname -m prints i86pc.
-       run(&b, nil, 0, "isainfo", "-n", nil);
-       if(contains(bstr(&b), "amd64"))
-               gohostarch = "amd64";
-       if(contains(bstr(&b), "i386"))
-               gohostarch = "386";
-#else
-       fatal("unknown operating system");
-#endif
-
-       if(gohostarch == nil) {
-               if(uname(&u) < 0)
-                       fatal("uname: %s", strerror(errno));
-               if(contains(u.machine, "x86_64") || contains(u.machine, "amd64"))
-                       gohostarch = "amd64";
-               else if(hassuffix(u.machine, "86"))
-                       gohostarch = "386";
-               else if(contains(u.machine, "arm"))
-                       gohostarch = "arm";
-               else if(contains(u.machine, "ppc64le"))
-                       gohostarch = "ppc64le";
-               else if(contains(u.machine, "ppc64"))
-                       gohostarch = "ppc64";
-               else
-                       fatal("unknown architecture: %s", u.machine);
-       }
-
-       if(streq(gohostarch, "arm"))
-               maxnbg = 1;
+func main() {
+       os.Setenv("TERM", "dumb") // disable escape codes in clang errors
+
+       slash = string(filepath.Separator)
+
+       gohostos = runtime.GOOS
+       switch gohostos {
+       case "darwin":
+               // Even on 64-bit platform, darwin uname -m prints i386.
+               if strings.Contains(run("", CheckExit, "sysctl", "machdep.cpu.extfeatures"), "EM64T") {
+                       gohostarch = "amd64"
+               }
+       case "solaris":
+               // Even on 64-bit platform, solaris uname -m prints i86pc.
+               out := run("", CheckExit, "isainfo", "-n")
+               if strings.Contains(out, "amd64") {
+                       gohostarch = "amd64"
+               }
+               if strings.Contains(out, "i386") {
+                       gohostarch = "386"
+               }
+       case "plan9":
+               gohostarch = os.Getenv("objtype")
+               if gohostarch == "" {
+                       fatal("$objtype is unset")
+               }
+       }
+
+       sysinit()
+
+       if gohostarch == "" {
+               // Default Unix system.
+               out := run("", CheckExit, "uname", "-m")
+               switch {
+               case strings.Contains(out, "x86_64"), strings.Contains(out, "amd64"):
+                       gohostarch = "amd64"
+               case strings.Contains(out, "86"):
+                       gohostarch = "386"
+               case strings.Contains(out, "arm"):
+                       gohostarch = "arm"
+               case strings.Contains(out, "ppc64le"):
+                       gohostarch = "ppc64le"
+               case strings.Contains(out, "ppc64"):
+                       gohostarch = "ppc64"
+               default:
+                       fatal("unknown architecture: %s", out)
+               }
+       }
+
+       if gohostarch == "arm" {
+               maxbg = 1
+       }
+       bginit()
 
        // The OS X 10.6 linker does not support external linking mode.
        // See golang.org/issue/5130.
@@ -728,120 +381,77 @@ main(int argc, char **argv)
        //
        // Roughly, OS X 10.N shows up as uname release (N+4),
        // so OS X 10.6 is uname version 10 and OS X 10.8 is uname version 12.
-       if(streq(gohostos, "darwin")) {
-               if(uname(&u) < 0)
-                       fatal("uname: %s", strerror(errno));
-               osx = atoi(u.release) - 4;
-               if(osx <= 6)
-                       goextlinkenabled = "0";
-               if(osx >= 8)
-                       defaultclang = 1;
+       if gohostos == "darwin" {
+               rel := run("", CheckExit, "uname", "-r")
+               if i := strings.Index(rel, "."); i >= 0 {
+                       rel = rel[:i]
+               }
+               osx, _ := strconv.Atoi(rel)
+               if osx <= 6+4 {
+                       goextlinkenabled = "0"
+               }
+               if osx >= 8+4 {
+                       defaultclang = true
+               }
        }
 
-       init();
-       xmain(argc, argv);
-       bfree(&b);
-       return 0;
+       xinit()
+       xmain()
 }
 
-// xqsort is a wrapper for the C standard qsort.
-void
-xqsort(void *data, int n, int elemsize, int (*cmp)(const void*, const void*))
-{
-       qsort(data, n, elemsize, cmp);
+// xsamefile reports whether f1 and f2 are the same file (or dir)
+func xsamefile(f1, f2 string) bool {
+       fi1, err1 := os.Stat(f1)
+       fi2, err2 := os.Stat(f2)
+       if err1 != nil || err2 != nil {
+               return f1 == f2
+       }
+       return os.SameFile(fi1, fi2)
 }
 
-// xstrcmp compares the NUL-terminated strings a and b.
-int
-xstrcmp(char *a, char *b)
-{
-       return strcmp(a, b);
+func cpuid(info *[4]uint32, ax uint32)
+
+func cansse2() bool {
+       if gohostarch != "386" && gohostarch != "amd64" {
+               return false
+       }
+
+       var info [4]uint32
+       cpuid(&info, 1)
+       return info[3]&(1<<26) != 0 // SSE2
 }
 
-// xstrstr returns a pointer to the first occurrence of b in a.
-char*
-xstrstr(char *a, char *b)
-{
-       return strstr(a, b);
+func xgetgoarm() string {
+       if goos == "nacl" {
+               // NaCl guarantees VFPv3 and is always cross-compiled.
+               return "7"
+       }
+       if gohostarch != "arm" || goos != gohostos {
+               // Conservative default for cross-compilation.
+               return "5"
+       }
+       if goos == "freebsd" {
+               // FreeBSD has broken VFP support.
+               return "5"
+       }
+       if xtryexecfunc(useVFPv3) {
+               return "7"
+       }
+       if xtryexecfunc(useVFPv1) {
+               return "6"
+       }
+       return "5"
 }
 
-// xstrrchr returns a pointer to the final occurrence of c in p.
-char*
-xstrrchr(char *p, int c)
-{
-       return strrchr(p, c);
+func xtryexecfunc(f func()) bool {
+       // TODO(rsc): Implement.
+       // The C cmd/dist used this to test whether certain assembly
+       // sequences could be executed properly. It used signals and
+       // timers and sigsetjmp, which is basically not possible in Go.
+       // We probably have to invoke ourselves as a subprocess instead,
+       // to contain the fault/timeout.
+       return false
 }
 
-// xsamefile reports whether f1 and f2 are the same file (or dir)
-int
-xsamefile(char *f1, char *f2)
-{
-       return streq(f1, f2); // suffice for now
-}
-
-sigjmp_buf sigill_jmpbuf;
-static void sigillhand(int);
-
-// xtryexecfunc tries to execute function f, if any illegal instruction
-// signal received in the course of executing that function, it will
-// return 0, otherwise it will return 1.
-// Some systems (notably NetBSD) will spin and spin when executing VFPv3
-// instructions on VFPv2 system (e.g. Raspberry Pi) without ever triggering
-// SIGILL, so we set a 1-second alarm to catch that case.
-int
-xtryexecfunc(void (*f)(void))
-{
-       int r;
-       r = 0;
-       signal(SIGILL, sigillhand);
-       signal(SIGALRM, sigillhand);
-       alarm(1);
-       if(sigsetjmp(sigill_jmpbuf, 1) == 0) {
-               f();
-               r = 1;
-       }
-       signal(SIGILL, SIG_DFL);
-       alarm(0);
-       signal(SIGALRM, SIG_DFL);
-       return r;
-}
-
-// SIGILL handler helper
-static void
-sigillhand(int signum)
-{
-       USED(signum);
-       siglongjmp(sigill_jmpbuf, 1);
-}
-
-static void
-__cpuid(int dst[4], int ax)
-{
-#ifdef __i386__
-       // we need to avoid ebx on i386 (esp. when -fPIC).
-       asm volatile(
-               "mov %%ebx, %%edi\n\t"
-               "cpuid\n\t"
-               "xchgl %%ebx, %%edi"
-               : "=a" (dst[0]), "=D" (dst[1]), "=c" (dst[2]), "=d" (dst[3])
-               : "0" (ax));
-#elif defined(__x86_64__)
-       asm volatile("cpuid"
-               : "=a" (dst[0]), "=b" (dst[1]), "=c" (dst[2]), "=d" (dst[3])
-               : "0" (ax));
-#else
-       dst[0] = dst[1] = dst[2] = dst[3] = 0;
-#endif
-}
-
-bool
-cansse2(void)
-{
-       int info[4];
-       
-       __cpuid(info, 1);
-       return (info[3] & (1<<26)) != 0;        // SSE2
-}
-
-#endif // PLAN9
-#endif // __WINDOWS__
+func useVFPv1()
+func useVFPv3()
diff --git a/src/cmd/dist/vfp_arm.s b/src/cmd/dist/vfp_arm.s
new file mode 100644 (file)
index 0000000..3cc11b2
--- /dev/null
@@ -0,0 +1,15 @@
+// Copyright 2015 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.
+
+#include "textflag.h"
+
+// try to run "vmov.f64 d0, d0" instruction
+TEXT useVFPv1(SB),NOSPLIT,$0
+       VMOV.F64 D0, D0
+       RET
+
+// try to run VFPv3-only "vmov.f64 d0, #112" instruction
+TEXT useVFPv3(SB),NOSPLIT,$0
+       VMOV.F64 $112, D0
+       RET
diff --git a/src/cmd/dist/vfp_default.s b/src/cmd/dist/vfp_default.s
new file mode 100644 (file)
index 0000000..c795b35
--- /dev/null
@@ -0,0 +1,14 @@
+// Copyright 2015 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.
+
+// +build !arm
+
+#include "textflag.h"
+
+TEXT ·useVFPv1(SB),NOSPLIT,$0
+       RET
+
+TEXT ·useVFPv3(SB),NOSPLIT,$0
+       RET
+
index a90937a77e7ccfa0e2f049c01140cfe31377819d..54c4d61249ac90258fb3ee1971b611cd07d008d7 100755 (executable)
@@ -3,6 +3,8 @@
 # Use of this source code is governed by a BSD-style
 # license that can be found in the LICENSE file.
 
+# See golang.org/s/go15bootstrap for an overview of the build process.
+
 # Environment variables that control make.bash:
 #
 # GOROOT_FINAL: The expected final Go root, baked into binaries.
@@ -110,26 +112,16 @@ rm -f ./runtime/runtime_defs.go
 
 # Finally!  Run the build.
 
-echo '##### Building C bootstrap tool.'
+echo '##### Building Go bootstrap tool.'
 echo cmd/dist
 export GOROOT="$(cd .. && pwd)"
-GOROOT_FINAL="${GOROOT_FINAL:-$GOROOT}"
-DEFGOROOT='-DGOROOT_FINAL="'"$GOROOT_FINAL"'"'
-
-mflag=""
-case "$GOHOSTARCH" in
-386) mflag=-m32;;
-amd64) mflag=-m64;;
-esac
-if [ "$(uname)" == "Darwin" ]; then
-       # golang.org/issue/5261
-       mflag="$mflag -mmacosx-version-min=10.6"
-fi
-# if gcc does not exist and $CC is not set, try clang if available.
-if [ -z "$CC" -a -z "$(type -t gcc)" -a -n "$(type -t clang)" ]; then
-       export CC=clang CXX=clang++
+GOROOT_BOOTSTRAP=${GOROOT_BOOTSTRAP:-$HOME/go1.4}
+if [ ! -x "$GOROOT_BOOTSTRAP/bin/go" ]; then
+       echo "ERROR: Cannot find $GOROOT_BOOTSTRAP/bin/go." >&2
+       echo "Set \$GOROOT_BOOTSTRAP to a working Go tree >= Go 1.4." >&2
 fi
-${CC:-gcc} $mflag -O2 -Wall -Werror -o cmd/dist/dist -Icmd/dist "$DEFGOROOT" cmd/dist/*.c
+rm -f cmd/dist/dist
+GOROOT="$GOROOT_BOOTSTRAP" "$GOROOT_BOOTSTRAP/bin/go" build -o cmd/dist/dist ./cmd/dist
 
 # -e doesn't propagate out of eval, so check success by hand.
 eval $(./cmd/dist/dist env -p || echo FAIL=true)
diff --git a/src/sudo.bash b/src/sudo.bash
deleted file mode 100755 (executable)
index 33254c2..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-#!/usr/bin/env bash
-# Copyright 2009 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.
-
-set -e
-
-case "`uname`" in
-Darwin)
-       ;;
-*)
-       exit 0
-esac
-
-# Check that the go command exists
-if ! go help >/dev/null 2>&1; then
-       echo "The go command is not in your PATH." >&2
-       exit 2
-fi
-
-eval $(go env)
-if ! [ -x $GOTOOLDIR/prof ]; then
-       echo "You don't need to run sudo.bash." >&2
-       exit 2
-fi
-
-if [[ ! -d /usr/local/bin ]]; then
-       echo 1>&2 'sudo.bash: problem with /usr/local/bin; cannot install tools.'
-       exit 2
-fi
-
-cd $(dirname $0)
-for i in prof
-do
-       # Remove old binaries if present
-       sudo rm -f /usr/local/bin/6$i
-       # Install new binaries
-       sudo cp $GOTOOLDIR/$i /usr/local/bin/go$i
-       sudo chgrp procmod /usr/local/bin/go$i
-       sudo chmod g+s /usr/local/bin/go$i
-done