--- /dev/null
+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.
+
+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
+
+ 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(&b1, "another string");
+ vadd(&v1, bstr(&b1)); // 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.
+
--- /dev/null
+// Copyright 2012 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.
+
+typedef int bool;
+
+// The Time unit is unspecified; we just need to
+// be able to compare whether t1 is older than t2 with t1 < t2.
+typedef long long Time;
+
+#define nil ((void*)0)
+#define nelem(x) (sizeof(x)/sizeof((x)[0]))
+#define USED(x) ((void)(x))
+
+// A Buf is a byte buffer, like Go's []byte.
+typedef struct Buf Buf;
+struct Buf
+{
+ char *p;
+ int len;
+ int cap;
+};
+
+// A Vec is a string vector, like Go's []string.
+typedef struct Vec Vec;
+struct Vec
+{
+ char **p;
+ int len;
+ int cap;
+};
+
+// Modes for run.
+enum {
+ CheckExit = 1,
+};
+
+// buf.c
+bool bequal(Buf *s, Buf *t);
+void bfree(Buf *b);
+void bgrow(Buf *b, int n);
+void binit(Buf *b);
+char* bprintf(Buf *b, char *fmt, ...);
+void breset(Buf *b);
+char* bstr(Buf *b);
+char* btake(Buf *b);
+void bwrite(Buf *b, void *v, int n);
+void bwriteb(Buf *dst, Buf *src);
+void bwritestr(Buf *b, char *p);
+void bswap(Buf *b, Buf *b1);
+void vadd(Vec *v, char *p);
+void vcopy(Vec *dst, char **src, int n);
+void vfree(Vec *v);
+void vgrow(Vec *v, int n);
+void vinit(Vec *v);
+void vreset(Vec *v);
+void vuniq(Vec *v);
+void splitlines(Vec*, char*);
+void splitfields(Vec*, char*);
+
+// build.c
+extern char *default_goroot;
+extern char *goarch;
+extern char *gobin;
+extern char *gohostarch;
+extern char *gohostos;
+extern char *goos;
+extern char *goroot;
+extern char *workdir;
+extern char *slash;
+
+void init(void);
+void cmdbootstrap(int, char**);
+void cmdenv(int, char**);
+void cmdinstall(int, char**);
+
+// buildgc.c
+void gcopnames(char*, char*);
+void mkenam(char*, char*);
+
+// main.c
+void xmain(int argc, char **argv);
+
+// portability layer (plan9.c, unix.c, windows.c)
+bool contains(char *p, char *sep);
+void fatal(char *msg, ...);
+bool hasprefix(char *p, char *prefix);
+bool hassuffix(char *p, char *suffix);
+bool isabs(char*);
+bool isdir(char *p);
+bool isfile(char *p);
+char* lastelem(char*);
+Time mtime(char*);
+void readfile(Buf*, char*);
+void run(Buf *b, char *dir, int mode, char *cmd, ...);
+void runv(Buf *b, char *dir, int mode, Vec *argv);
+bool streq(char*, char*);
+void writefile(Buf*, char*);
+void xatexit(void (*f)(void));
+void xexit(int);
+void xfree(void*);
+void xgetenv(Buf *b, char *name);
+void xgetwd(Buf *b);
+void* xmalloc(int n);
+void* xmalloc(int);
+int xmemcmp(void*, void*, int);
+void xmemmove(void*, void*, int);
+void xmkdir(char *p);
+void xmkdirall(char*);
+Time xmtime(char *p);
+void xprintf(char*, ...);
+void xqsort(void*, int, int, int(*)(const void*, const void*));
+void xreaddir(Vec *dst, char *dir);
+void* xrealloc(void*, int);
+void xrealwd(Buf *b, char *path);
+void xremove(char *p);
+void xremoveall(char *p);
+void xsetenv(char*, char*);
+int xstrcmp(char*, char*);
+char* xstrdup(char *p);
+int xstreq(char*, char*);
+int xstrlen(char*);
+char* xstrrchr(char*, int);
+char* xstrstr(char*, char*);
+char* xworkdir(void);
--- /dev/null
+// Copyright 2012 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.
+
+// Byte buffers and string vectors.
+
+#include "a.h"
+
+// binit prepares an uninitialized buffer for use.
+void
+binit(Buf *b)
+{
+ b->p = nil;
+ b->len = 0;
+ b->cap = 0;
+}
+
+// breset truncates the buffer back to zero length.
+void
+breset(Buf *b)
+{
+ b->len = 0;
+}
+
+// bfree frees the storage associated with a buffer.
+void
+bfree(Buf *b)
+{
+ xfree(b->p);
+ binit(b);
+}
+
+// bgrow ensures that the buffer has at least n more bytes
+// between its len and cap.
+void
+bgrow(Buf *b, int n)
+{
+ int want;
+
+ want = b->len+n;
+ if(want > b->cap) {
+ b->cap = 2*want;
+ if(b->cap < 64)
+ b->cap = 64;
+ b->p = xrealloc(b->p, b->cap);
+ }
+}
+
+// bwrite appends the n bytes at v to the buffer.
+void
+bwrite(Buf *b, void *v, int n)
+{
+ bgrow(b, n);
+ xmemmove(b->p+b->len, v, n);
+ b->len += n;
+}
+
+// bwritestr appends the string p to the buffer.
+void
+bwritestr(Buf *b, char *p)
+{
+ bwrite(b, p, xstrlen(p));
+}
+
+// bstr returns a pointer to a NUL-terminated string of the
+// buffer contents. The pointer points into the buffer.
+char*
+bstr(Buf *b)
+{
+ bgrow(b, 1);
+ b->p[b->len] = '\0';
+ return b->p;
+}
+
+// btake takes ownership of the string form of the buffer.
+// After this call, the buffer has zero length and does not
+// refer to the memory that btake returned.
+char*
+btake(Buf *b)
+{
+ char *p;
+
+ p = bstr(b);
+ binit(b);
+ return p;
+}
+
+// bwriteb appends the src buffer to the dst buffer.
+void
+bwriteb(Buf *dst, Buf *src)
+{
+ bwrite(dst, src->p, src->len);
+}
+
+// bequal reports whether the buffers have the same content.
+bool
+bequal(Buf *s, Buf *t)
+{
+ return s->len == t->len && xmemcmp(s->p, t->p, s->len) == 0;
+}
+
+// The invariant with the vectors is that v->p[0:v->len] is allocated
+// strings that are owned by the vector. The data beyond v->len may
+// be garbage.
+
+// vinit prepares an uninitialized vector for use.
+void
+vinit(Vec *v)
+{
+ v->p = nil;
+ v->len = 0;
+ v->cap = 0;
+}
+
+// vreset truncates the vector back to zero length.
+void
+vreset(Vec *v)
+{
+ int i;
+
+ for(i=0; i<v->len; i++) {
+ xfree(v->p[i]);
+ v->p[i] = nil;
+ }
+ v->len = 0;
+}
+
+// vfree frees the storage associated with the vector.
+void
+vfree(Vec *v)
+{
+ vreset(v);
+ xfree(v->p);
+ vinit(v);
+}
+
+
+// vgrow ensures that the vector has room for at least
+// n more entries between len and cap.
+void
+vgrow(Vec *v, int n)
+{
+ int want;
+
+ want = v->len+n;
+ if(want > v->cap) {
+ v->cap = 2*want;
+ if(v->cap < 64)
+ v->cap = 64;
+ v->p = xrealloc(v->p, v->cap*sizeof v->p[0]);
+ }
+}
+
+// vcopy copies the srclen strings at src into the vector.
+void
+vcopy(Vec *dst, char **src, int srclen)
+{
+ int i;
+
+ // use vadd, to make copies of strings
+ for(i=0; i<srclen; i++)
+ vadd(dst, src[i]);
+}
+
+// vadd adds a copy of the string p to the vector.
+void
+vadd(Vec *v, char *p)
+{
+ vgrow(v, 1);
+ if(p != nil)
+ p = xstrdup(p);
+ v->p[v->len++] = p;
+}
+
+// vaddn adds a string consisting of the n bytes at p to the vector.
+void
+vaddn(Vec *v, char *p, int n)
+{
+ char *q;
+
+ vgrow(v, 1);
+ q = xmalloc(n+1);
+ xmemmove(q, p, n);
+ q[n] = '\0';
+ v->p[v->len++] = q;
+}
+
+static int
+strpcmp(const void *a, const void *b)
+{
+ return xstrcmp(*(char**)a, *(char**)b);
+}
+
+// vuniq sorts the vector and then discards duplicates,
+// in the manner of sort | uniq.
+void
+vuniq(Vec *v)
+{
+ int i, n;
+
+ xqsort(v->p, v->len, sizeof(v->p[0]), strpcmp);
+ n = 0;
+ for(i=0; i<v->len; i++) {
+ if(i>0 && streq(v->p[i], v->p[i-1]))
+ xfree(v->p[i]);
+ else
+ v->p[n++] = v->p[i];
+ }
+ v->len = n;
+}
+
+// splitlines replaces the vector v with the result of splitting
+// the input p after each \n.
+void
+splitlines(Vec *v, char *p)
+{
+ int i, c;
+ char *start;
+
+ vreset(v);
+ start = p;
+ for(i=0; p[i]; i++) {
+ if(p[i] == '\n') {
+ vaddn(v, start, (p+i+1)-start);
+ start = p+i+1;
+ }
+ }
+ if(*start != '\0')
+ vadd(v, start);
+}
+
+// splitfields replaces the vector v with the result of splitting
+// the input p into non-empty fields containing no spaces.
+void
+splitfields(Vec *v, char *p)
+{
+ char *start;
+
+ vreset(v);
+ for(;;) {
+ while(*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n')
+ p++;
+ if(*p == '\0')
+ break;
+ start = p;
+ while(*p != ' ' && *p != '\t' && *p != '\r' && *p != '\n' && *p != '\0')
+ p++;
+ vaddn(v, start, p-start);
+ }
+}
--- /dev/null
+// Copyright 2012 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 "a.h"
+
+/*
+ * Initialization for any invocation.
+ */
+
+// The usual variables.
+char *goarch;
+char *gobin;
+char *gohostarch;
+char *gohostos;
+char *goos;
+char *goroot;
+char *workdir;
+char *gochar;
+char *goroot_final;
+char *goversion = "go1"; // TODO: Read correct version
+char *slash; // / for unix, \ for windows
+char *default_goroot;
+
+static void fixslash(Buf*);
+static bool shouldbuild(char*, char*);
+static void copy(char*, char*);
+
+// The known architecture letters.
+static char *gochars = "568";
+
+// The known architectures.
+static char *okgoarch[] = {
+ // same order as gochars
+ "arm",
+ "amd64",
+ "386",
+};
+
+// The known operating systems.
+static char *okgoos[] = {
+ "darwin",
+ "linux",
+ "freebsd",
+ "netbsd",
+ "openbsd",
+ "plan9",
+ "windows",
+};
+
+static void rmworkdir(void);
+
+// find reports the first index of p in l[0:n], or else -1.
+static 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;
+}
+
+// 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(default_goroot == nil)
+ fatal("$GOROOT not set and not available");
+ bwritestr(&b, default_goroot);
+ }
+ 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);
+
+ p = bprintf(&b, "%s/include/u.h", goroot);
+ fixslash(&b);
+ 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);
+
+ if(find(gohostarch, okgoarch, nelem(okgoarch)) < 0)
+ fatal("unknown $GOHOSTARCH %s", gohostarch);
+
+ xgetenv(&b, "GOARCH");
+ if(b.len == 0)
+ bwritestr(&b, gohostarch);
+ goarch = btake(&b);
+ if((i=find(goarch, okgoarch, nelem(okgoarch))) < 0)
+ fatal("unknown $GOARCH %s", goarch);
+ bprintf(&b, "%c", gochars[i]);
+ gochar = btake(&b);
+
+ xgetenv(&b, "GOROOT_FINAL");
+ if(b.len > 0)
+ goroot_final = btake(&b);
+ else
+ goroot_final = goroot;
+
+ xsetenv("GOROOT", goroot);
+ xsetenv("GOARCH", goarch);
+ xsetenv("GOOS", goos);
+
+ // Make the environment more predictable.
+ xsetenv("LANG", "C");
+ xsetenv("LANGUAGE", "en_US.UTF8");
+
+ workdir = xworkdir();
+ xatexit(rmworkdir);
+
+ bfree(&b);
+}
+
+// rmworkdir deletes the work directory.
+static void
+rmworkdir(void)
+{
+ xprintf("rm -rf %s\n", workdir);
+ xremoveall(workdir);
+}
+
+/*
+ * Initial tree setup.
+ */
+
+// The old tools that no longer live in $GOBIN or $GOROOT/bin.
+static char *oldtool[] = {
+ "5a", "5c", "5g", "5l",
+ "6a", "6c", "6g", "6l",
+ "8a", "8c", "8g", "8l",
+ "6cov",
+ "6nm",
+ "cgo",
+ "ebnflint",
+ "goapi",
+ "gofix",
+ "goinstall",
+ "gomake",
+ "gopack",
+ "gopprof",
+ "gotest",
+ "gotype",
+ "govet",
+ "goyacc",
+ "quietgcc",
+};
+
+// setup sets up the tree for the initial build.
+static void
+setup(void)
+{
+ int i;
+ Buf b;
+ char *p;
+
+ binit(&b);
+
+ run(&b, nil, 0, "ld", "--version", nil);
+ if(contains(bstr(&b), "gold") && contains(bstr(&b), " 2.20")) {
+ fatal("Your system has gold 2.20 installed.\n"
+ "This version is shipped by Ubuntu even though\n"
+ "it is known not to work on Ubuntu.\n"
+ "Binaries built with this linker are likely to fail in mysterious ways.\n"
+ "\n"
+ "Run sudo apt-get remove binutils-gold.");
+ }
+
+ // Create tool directory.
+ p = bprintf(&b, "%s/bin", goroot);
+ fixslash(&b);
+ if(!isdir(p))
+ xmkdir(p);
+ p = bprintf(&b, "%s/bin/go-tool", goroot);
+ fixslash(&b);
+ if(!isdir(p))
+ xmkdir(p);
+
+ // Create package directory.
+ p = bprintf(&b, "%s/pkg", goroot);
+ fixslash(&b);
+ if(!isdir(p))
+ xmkdir(p);
+ p = bprintf(&b, "%s/pkg/%s_%s", goroot, goos, goarch);
+ fixslash(&b);
+ xremoveall(p);
+ xmkdir(p);
+
+ // Remove old pre-tool binaries.
+ for(i=0; i<nelem(oldtool); i++)
+ xremove(bprintf(&b, "%s%s%s%s%s", goroot, slash, "bin", slash, oldtool[i]));
+
+ // 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;
+ }
+ }
+
+ bfree(&b);
+}
+
+/*
+ * C library and tool building
+ */
+
+// gccargs is the gcc command line to use for compiling a single C file.
+static char *gccargs[] = {
+ "gcc",
+ "-Wall",
+ "-Wno-sign-compare",
+ "-Wno-missing-braces",
+ "-Wno-parentheses",
+ "-Wno-unknown-pragmas",
+ "-Wno-switch",
+ "-Wno-comment",
+ "-Werror",
+ "-fno-common",
+ "-ggdb",
+ "-O2",
+ "-c",
+};
+
+// 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", {
+ "$GOROOT/include/u.h",
+ "$GOROOT/include/utf.h",
+ "$GOROOT/include/fmt.h",
+ "$GOROOT/include/libc.h",
+ "fmt/*",
+ "utf/*",
+ "-utf/mkrunetype",
+ "-utf\\mkrunetype",
+ "-utf/runetypebody",
+ "-utf\\runetypebody",
+ }},
+ {"libbio", {
+ "$GOROOT/include/u.h",
+ "$GOROOT/include/utf.h",
+ "$GOROOT/include/fmt.h",
+ "$GOROOT/include/libc.h",
+ "$GOROOT/include/bio.h",
+ }},
+ {"libmach", {
+ "$GOROOT/include/u.h",
+ "$GOROOT/include/utf.h",
+ "$GOROOT/include/fmt.h",
+ "$GOROOT/include/libc.h",
+ "$GOROOT/include/bio.h",
+ "$GOROOT/include/ar.h",
+ "$GOROOT/include/bootexec.h",
+ "$GOROOT/include/mach.h",
+ "$GOROOT/include/ureg_amd64.h",
+ "$GOROOT/include/ureg_arm.h",
+ "$GOROOT/include/ureg_x86.h",
+ }},
+ {"cmd/cc", {
+ "-pgen.c",
+ "-pswt.c",
+ }},
+ {"cmd/gc", {
+ "-cplx.c",
+ "-pgen.c",
+ "-y1.tab.c", // makefile dreg
+ "opnames.h",
+ }},
+ {"cmd/5c", {
+ "../cc/pgen.c",
+ "../cc/pswt.c",
+ "../5l/enam.c",
+ "$GOROOT/lib/libcc.a",
+ }},
+ {"cmd/6c", {
+ "../cc/pgen.c",
+ "../cc/pswt.c",
+ "../6l/enam.c",
+ "$GOROOT/lib/libcc.a",
+ }},
+ {"cmd/8c", {
+ "../cc/pgen.c",
+ "../cc/pswt.c",
+ "../8l/enam.c",
+ "$GOROOT/lib/libcc.a",
+ }},
+ {"cmd/5g", {
+ "../gc/cplx.c",
+ "../gc/pgen.c",
+ "../5l/enam.c",
+ "$GOROOT/lib/libgc.a",
+ }},
+ {"cmd/6g", {
+ "../gc/cplx.c",
+ "../gc/pgen.c",
+ "../6l/enam.c",
+ "$GOROOT/lib/libgc.a",
+ }},
+ {"cmd/8g", {
+ "../gc/cplx.c",
+ "../gc/pgen.c",
+ "../8l/enam.c",
+ "$GOROOT/lib/libgc.a",
+ }},
+ {"cmd/5l", {
+ "../ld/*",
+ "enam.c",
+ }},
+ {"cmd/6l", {
+ "../ld/*",
+ "enam.c",
+ }},
+ {"cmd/8l", {
+ "../ld/*",
+ "enam.c",
+ }},
+ {"cmd/", {
+ "$GOROOT/lib/libmach.a",
+ "$GOROOT/lib/libbio.a",
+ "$GOROOT/lib/lib9.a",
+ }},
+};
+
+// depsuffix records the allowed suffixes for source files.
+char *depsuffix[] = {
+ ".c",
+ ".h",
+ ".s",
+ ".go",
+};
+
+// gentab records how to generate some trivial files.
+static struct {
+ char *name;
+ void (*gen)(char*, char*);
+} gentab[] = {
+ {"opnames.h", gcopnames},
+ {"enam.c", mkenam},
+};
+
+// 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;
+ bool islib, ispkg, isgo, stale;
+ Buf b, b1, path;
+ Vec compile, files, link, go, missing, clean, lib, extra;
+ Time ttarg, t;
+ int i, j, k, n;
+
+ binit(&b);
+ binit(&b1);
+ binit(&path);
+ vinit(&compile);
+ vinit(&files);
+ vinit(&link);
+ vinit(&go);
+ vinit(&missing);
+ vinit(&clean);
+ vinit(&lib);
+ vinit(&extra);
+
+ // path = full path to dir.
+ bprintf(&path, "%s/src/%s", goroot, dir);
+ fixslash(&path);
+ name = lastelem(dir);
+
+ islib = hasprefix(dir, "lib") || streq(dir, "cmd/cc") || streq(dir, "cmd/gc");
+ ispkg = hasprefix(dir, "pkg");
+ isgo = ispkg || streq(dir, "cmd/go");
+
+
+ // Start final link command line.
+ // Note: code below knows that link.p[2] is the target.
+ if(islib) {
+ // C library.
+ vadd(&link, "ar");
+ vadd(&link, "rsc");
+ prefix = "";
+ if(!hasprefix(name, "lib"))
+ prefix = "lib";
+ bprintf(&b, "%s/lib/%s%s.a", goroot, prefix, name);
+ fixslash(&b);
+ vadd(&link, bstr(&b));
+ } else if(ispkg) {
+ // Go library (package).
+ bprintf(&b, "%s/bin/go-tool/pack", goroot);
+ fixslash(&b);
+ vadd(&link, bstr(&b));
+ vadd(&link, "grc");
+ p = bprintf(&b, "%s/pkg/%s_%s/%s", goroot, goos, goarch, dir+4);
+ *xstrrchr(p, '/') = '\0';
+ xmkdirall(p);
+ bprintf(&b, "%s/pkg/%s_%s/%s.a", goroot, goos, goarch, dir+4);
+ fixslash(&b);
+ vadd(&link, bstr(&b));
+ } else if(streq(dir, "cmd/go")) {
+ // Go command.
+ bprintf(&b, "%s/bin/go-tool/%sl", goroot, gochar);
+ fixslash(&b);
+ vadd(&link, bstr(&b));
+ vadd(&link, "-o");
+ bprintf(&b, "%s/bin/go-tool/go_bootstrap", goroot);
+ fixslash(&b);
+ vadd(&link, bstr(&b));
+ } else {
+ // C command.
+ vadd(&link, "gcc");
+ vadd(&link, "-o");
+ bprintf(&b, "%s/bin/go-tool/%s", goroot, name);
+ fixslash(&b);
+ vadd(&link, bstr(&b));
+ }
+ ttarg = mtime(link.p[2]);
+
+ // Gather files that are sources for this target.
+ // Everything in that directory, and any target-specific
+ // additions.
+ xreaddir(&files, bstr(&path));
+ for(i=0; i<nelem(deptab); i++) {
+ if(hasprefix(dir, deptab[i].prefix)) {
+ for(j=0; (p=deptab[i].dep[j])!=nil; j++) {
+ if(hasprefix(p, "$GOROOT/")) {
+ bprintf(&b1, "%s/%s", goroot, p+8);
+ p = bstr(&b1);
+ }
+ if(hassuffix(p, ".a")) {
+ vadd(&lib, p);
+ continue;
+ }
+ if(hassuffix(p, "/*")) {
+ bprintf(&b, "%s/%s", bstr(&path), p);
+ b.len -= 2;
+ fixslash(&b);
+ xreaddir(&extra, bstr(&b));
+ bprintf(&b, "%s", p);
+ b.len -= 2;
+ for(k=0; k<extra.len; k++) {
+ bprintf(&b1, "%s/%s", bstr(&b), extra.p[k]);
+ fixslash(&b1);
+ vadd(&files, bstr(&b1));
+ }
+ 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.len = n;
+ continue;
+ }
+ vadd(&files, p);
+ }
+ }
+ }
+ vuniq(&files);
+
+ // Convert to absolute paths.
+ for(i=0; i<files.len; i++) {
+ if(!isabs(files.p[i])) {
+ bprintf(&b, "%s/%s", bstr(&path), files.p[i]);
+ fixslash(&b);
+ xfree(files.p[i]);
+ files.p[i] = btake(&b);
+ }
+ }
+
+ // For package runtime, copy some files into the work space.
+ if(streq(dir, "pkg/runtime")) {
+ copy(bprintf(&b, "%s/arch_GOARCH.h", workdir),
+ bprintf(&b1, "%s/arch_%s.h", bstr(&path), goarch));
+ copy(bprintf(&b, "%s/defs_GOOS_GOARCH.h", workdir),
+ bprintf(&b1, "%s/defs_%s_%s.h", bstr(&path), goos, goarch));
+ copy(bprintf(&b, "%s/os_GOOS.h", workdir),
+ bprintf(&b1, "%s/os_%s.h", bstr(&path), goos));
+ copy(bprintf(&b, "%s/signals_GOOS.h", workdir),
+ bprintf(&b1, "%s/signals_%s.h", bstr(&path), goos));
+ copy(bprintf(&b, "%s/zasm_GOOS_GOARCH.h", workdir),
+ bprintf(&b1, "%s/zasm_%s_%s.h", bstr(&path), goos, goarch));
+ }
+
+
+ // Is the target up-to-date?
+ stale = 1; // TODO: Decide when 0 is okay.
+ 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;
+ ok:
+ t = mtime(p);
+ if(t > ttarg)
+ stale = 1;
+ if(t == 0) {
+ vadd(&missing, p);
+ files.p[n++] = files.p[i];
+ continue;
+ }
+ if(!hassuffix(p, ".a") && !shouldbuild(p, dir)) {
+ xfree(files.p[i]);
+ continue;
+ }
+ if(hassuffix(p, ".go"))
+ vadd(&go, p);
+ files.p[n++] = files.p[i];
+ }
+ files.len = n;
+
+ for(i=0; i<lib.len && !stale; i++)
+ if(mtime(lib.p[i]) > ttarg)
+ stale = 1;
+
+ if(!stale)
+ goto out;
+
+ // Generate any missing files.
+ for(i=0; i<missing.len; i++) {
+ p = missing.p[i];
+ elem = lastelem(p);
+ for(j=0; j<nelem(gentab); j++) {
+ if(streq(gentab[j].name, elem)) {
+ gentab[j].gen(bstr(&path), p);
+ vadd(&clean, p);
+ goto built;
+ }
+ }
+ fatal("missing file %s", p);
+ built:;
+ }
+
+ // 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]);
+
+ vreset(&compile);
+ if(!isgo) {
+ // C library or tool.
+ vcopy(&compile, gccargs, nelem(gccargs));
+ if(streq(gohostarch, "amd64"))
+ vadd(&compile, "-m64");
+ else if(streq(gohostarch, "386"))
+ vadd(&compile, "-m32");
+ if(streq(dir, "lib9"))
+ vadd(&compile, "-DPLAN9PORT");
+
+ bprintf(&b, "%s/include", goroot);
+ fixslash(&b);
+ vadd(&compile, "-I");
+ vadd(&compile, bstr(&b));
+
+ vadd(&compile, "-I");
+ vadd(&compile, bstr(&path));
+
+ // runtime/goos.c gets the default constants hard-coded.
+ if(streq(name, "goos.c")) {
+ vadd(&compile, bprintf(&b, "-DGOOS=\"%s\"", goos));
+ vadd(&compile, bprintf(&b, "-DGOARCH=\"%s\"", goarch));
+ vadd(&compile, bprintf(&b, "-DGOROOT=\"%s\"", goroot));
+ vadd(&compile, bprintf(&b, "-DGOVERSION=\"%s\"", goversion));
+ }
+
+ // gc/lex.c records the GOEXPERIMENT setting used during the build.
+ if(streq(name, "lex.c")) {
+ xgetenv(&b, "GOEXPERIMENT");
+ vadd(&compile, bprintf(&b1, "-DGOEXPERIMENT=\"%s\"", bstr(&b)));
+ }
+ } else {
+ // Supporting files for a Go package.
+ if(hassuffix(files.p[i], ".s")) {
+ bprintf(&b, "%s/bin/go-tool/%sa", goroot, gochar);
+ fixslash(&b);
+ vadd(&compile, bstr(&b));
+ } else {
+ bprintf(&b, "%s/bin/go-tool/%sc", goroot, gochar);
+ fixslash(&b);
+ vadd(&compile, bstr(&b));
+ vadd(&compile, "-FVw");
+ }
+ vadd(&compile, "-I");
+ vadd(&compile, workdir);
+ vadd(&compile, bprintf(&b, "-DGOOS_%s", goos));
+ vadd(&compile, bprintf(&b, "-DGOARCH_%s", goos));
+ }
+
+ bprintf(&b, "%s/%s", workdir, lastelem(files.p[i]));
+ b.p[b.len-1] = 'o'; // was c or s
+ fixslash(&b);
+ vadd(&compile, "-o");
+ vadd(&compile, bstr(&b));
+ vadd(&link, bstr(&b));
+ vadd(&clean, bstr(&b));
+
+ vadd(&compile, files.p[i]);
+
+ runv(nil, bstr(&path), CheckExit, &compile);
+ vreset(&compile);
+ }
+
+ if(isgo) {
+ // The last loop was compiling individual files.
+ // Hand the Go files to the compiler en masse.
+ vreset(&compile);
+ bprintf(&b, "%s/bin/go-tool/%sg", goroot, gochar);
+ fixslash(&b);
+ vadd(&compile, bstr(&b));
+
+ bprintf(&b, "%s/_go_.%s", workdir, gochar);
+ fixslash(&b);
+ vadd(&compile, "-o");
+ vadd(&compile, bstr(&b));
+ vadd(&clean, bstr(&b));
+ vadd(&link, bstr(&b));
+
+ vadd(&compile, "-p");
+ if(hasprefix(dir, "pkg/"))
+ vadd(&compile, dir+4);
+ else
+ vadd(&compile, "main");
+
+ if(streq(dir, "pkg/runtime"))
+ vadd(&compile, "-+");
+
+ vcopy(&compile, go.p, go.len);
+
+ runv(nil, bstr(&path), CheckExit, &compile);
+ }
+
+ if(!islib && !isgo) {
+ // C binaries need the libraries explicitly, and -lm.
+ vcopy(&link, lib.p, lib.len);
+ vadd(&link, "-lm");
+ }
+
+ // Remove target before writing it.
+ xremove(link.p[2]);
+
+ runv(nil, nil, CheckExit, &link);
+
+out:
+ for(i=0; i<clean.len; i++)
+ xremove(clean.p[i]);
+
+ bfree(&b);
+ bfree(&b1);
+ bfree(&path);
+ vfree(&compile);
+ vfree(&files);
+ vfree(&link);
+ vfree(&go);
+ vfree(&missing);
+ vfree(&clean);
+ vfree(&lib);
+ vfree(&extra);
+}
+
+// matchfield reports whether the field matches this build.
+static bool
+matchfield(char *f)
+{
+ return streq(f, goos) || streq(f, goarch) || streq(f, "cmd_go_bootstrap");
+}
+
+// shouldbuild reports whether we should build this file.
+// It applies the same rules that are used with context tags
+// in package go/build, except that the GOOS and GOARCH
+// can appear anywhere in the file name, not just after _.
+// 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, true;
+ Buf b;
+ Vec lines, fields;
+
+ // Check file name for GOOS or GOARCH.
+ name = lastelem(file);
+ for(i=0; i<nelem(okgoos); i++)
+ if(contains(name, okgoos[i]) && !streq(okgoos[i], goos))
+ return 0;
+ for(i=0; i<nelem(okgoarch); i++)
+ if(contains(name, okgoarch[i]) && !streq(okgoarch[i], goarch))
+ return 0;
+
+ // Omit test files.
+ if(contains(name, "_test"))
+ return 0;
+
+ // 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;
+ }
+ if(contains(p, "package main") && !streq(dir, "cmd/go")) {
+ ret = 0;
+ goto out;
+ }
+ 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;
+ }
+ ret = 0;
+ goto out;
+ fieldmatch:;
+ }
+
+out:
+ bfree(&b);
+ vfree(&lines);
+ vfree(&fields);
+
+ return ret;
+}
+
+// fixslash rewrites / to \ when the slash character is \, so that the paths look conventional.
+static void
+fixslash(Buf *b)
+{
+ int i;
+
+ if(slash[0] == '/')
+ return;
+ for(i=0; i<b->len; i++)
+ if(b->p[i] == '/')
+ b->p[i] = '\\';
+}
+
+// copy copies the file src to dst, via memory (so only good for small files).
+static void
+copy(char *dst, char *src)
+{
+ Buf b;
+
+ binit(&b);
+ readfile(&b, src);
+ writefile(&b, dst);
+ bfree(&b);
+}
+
+/*
+ * command implementations
+ */
+
+// The env command prints the default environment.
+void
+cmdenv(int argc, char **argv)
+{
+ USED(argc);
+ USED(argv);
+
+ xprintf("GOROOT=%s\n", goroot);
+ xprintf("GOARCH=%s\n", goarch);
+ xprintf("GOOS=%s\n", goos);
+}
+
+// buildorder records the order of builds for the 'go bootstrap' command.
+static char *buildorder[] = {
+ "lib9",
+ "libbio",
+ "libmach",
+
+ "cmd/cov",
+ "cmd/nm",
+ "cmd/pack",
+ "cmd/prof",
+
+ "cmd/cc", // must be before c
+ "cmd/gc", // must be before g
+ "cmd/%sl", // must be before a, c, g
+ "cmd/%sa",
+ "cmd/%sc",
+ "cmd/%sg",
+
+ // The dependency order here was copied from a buildscript
+ // back when there were build scripts. Will have to
+ // be maintained by hand, but shouldn't change very
+ // often.
+ "pkg/runtime",
+ "pkg/errors",
+ "pkg/sync/atomic",
+ "pkg/sync",
+ "pkg/io",
+ "pkg/unicode",
+ "pkg/unicode/utf8",
+ "pkg/unicode/utf16",
+ "pkg/bytes",
+ "pkg/math",
+ "pkg/strings",
+ "pkg/strconv",
+ "pkg/bufio",
+ "pkg/sort",
+ "pkg/container/heap",
+ "pkg/encoding/base64",
+ "pkg/syscall",
+ "pkg/time",
+ "pkg/os",
+ "pkg/reflect",
+ "pkg/fmt",
+ "pkg/encoding/json",
+ "pkg/encoding/gob",
+ "pkg/flag",
+ "pkg/path/filepath",
+ "pkg/path",
+ "pkg/io/ioutil",
+ "pkg/log",
+ "pkg/regexp/syntax",
+ "pkg/regexp",
+ "pkg/go/token",
+ "pkg/go/scanner",
+ "pkg/go/ast",
+ "pkg/go/parser",
+ "pkg/go/build",
+ "pkg/os/exec",
+ "pkg/net/url",
+ "pkg/text/template/parse",
+ "pkg/text/template",
+
+ "cmd/go",
+};
+
+// 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 *p;
+
+ setup();
+
+ // TODO: nuke();
+
+ binit(&b);
+ for(i=0; i<nelem(buildorder); i++) {
+ p = bprintf(&b, buildorder[i], gochar);
+ xprintf("%s\n", p);
+ install(p);
+ }
+ bfree(&b);
+}
+
+// Install installs the list of packages named on the command line.
+void
+cmdinstall(int argc, char **argv)
+{
+ int i;
+
+ for(i=1; i<argc; i++)
+ install(argv[i]);
+}
--- /dev/null
+// Copyright 2012 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 "a.h"
+#include <stdio.h>
+
+/*
+ * Helpers for building cmd/gc.
+ */
+
+// gcopnames creates opnames.h from go.h.
+// 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));
+ }
+ }
+
+ bwritestr(&out, bprintf(&b, "};\n"));
+
+ writefile(&out, file);
+
+ bfree(&in);
+ bfree(&b);
+ bfree(&out);
+ vfree(&lines);
+ vfree(&fields);
+}
+
+// mkenam reads [568].out.h and writes enam.c
+// The format is much the same as the Go opcodes above.
+void
+mkenam(char *dir, char *file)
+{
+ int i, ch;
+ Buf in, b, out;
+ Vec lines;
+ char *p;
+
+ binit(&b);
+ binit(&in);
+ binit(&out);
+ vinit(&lines);
+
+ ch = dir[xstrlen(dir)-2];
+ bprintf(&b, "%s/../%cl/%c.out.h", dir, ch, ch);
+ readfile(&in, bstr(&b));
+ splitlines(&lines, bstr(&in));
+ bwritestr(&out, "char* anames[] = {\n");
+ 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));
+ }
+ }
+ bwritestr(&out, "};\n");
+ writefile(&out, file);
+
+ bfree(&b);
+ bfree(&in);
+ bfree(&out);
+ vfree(&lines);
+}
--- /dev/null
+// Copyright 2012 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 "a.h"
+
+// cmdtab records the available commands.
+static struct {
+ char *name;
+ void (*f)(int, char**);
+} cmdtab[] = {
+ {"bootstrap", cmdbootstrap},
+ {"env", cmdenv},
+ {"install", cmdinstall},
+};
+
+// The OS-specific main calls into the portable code here.
+void
+xmain(int argc, char **argv)
+{
+ int i;
+
+ if(argc <= 1) {
+ xprintf("go tool dist commands:\n");
+ for(i=0; i<nelem(cmdtab); i++)
+ xprintf("\t%s\n", cmdtab[i].name);
+ xexit(1);
+ }
+
+ for(i=0; i<nelem(cmdtab); i++) {
+ if(streq(cmdtab[i].name, argv[1])) {
+ cmdtab[i].f(argc-1, argv+1);
+ return;
+ }
+ }
+
+ fatal("unknown command %s", argv[1]);
+}
--- /dev/null
+// Copyright 2012 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.
+
+// 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>
+
+// 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);
+}
+
+// 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);
+}
+
+// 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)
+{
+ int i, p[2], pid, status;
+ Buf cmd;
+ char *q;
+
+ // 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);
+ }
+ bwritestr(&cmd, q);
+ }
+ printf("%s\n", bstr(&cmd));
+ bfree(&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]);
+ }
+ if(dir != nil) {
+ if(chdir(dir) < 0) {
+ fprintf(stderr, "chdir %s: %s\n", dir, strerror(errno));
+ _exit(1);
+ }
+ }
+ vadd(argv, nil);
+ execvp(argv->p[0], argv->p);
+ 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]);
+ }
+wait:
+ errno = 0;
+ if(waitpid(pid, &status, 0) != pid) {
+ if(errno == EINTR)
+ goto wait;
+ fatal("waitpid: %s", strerror(errno));
+ }
+ if(mode==CheckExit && (!WIFEXITED(status) || WEXITSTATUS(status) != 0)) {
+ if(b != nil)
+ fwrite(b->p, b->len, 1, stderr);
+ fatal("%s failed", argv->p[0]);
+ }
+}
+
+// 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);
+}
+
+// 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);
+}
+
+// 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);
+}
+
+// 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;
+}
+
+// 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);
+}
+
+// writefile writes b to the named file, creating it if needed.
+void
+writefile(Buf *b, char *file)
+{
+ 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));
+ close(fd);
+}
+
+// xmkdir creates the directory p.
+void
+xmkdir(char *p)
+{
+ if(mkdir(p, 0777) < 0)
+ fatal("mkdir %s: %s", p, strerror(errno));
+}
+
+// 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 = '/';
+ }
+ xmkdir(p);
+}
+
+// xremove removes the file p.
+void
+xremove(char *p)
+{
+ unlink(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));
+ }
+ rmdir(p);
+ } else {
+ unlink(p);
+ }
+
+ bfree(&b);
+ vfree(&dir);
+}
+
+// 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(strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0)
+ continue;
+ vadd(dst, dp->d_name);
+ }
+ closedir(d);
+}
+
+// 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");
+ bwritestr(&b, "/go-cbuild-XXXXXX");
+ if(mkdtemp(bstr(&b)) == nil)
+ fatal("mkdtemp: %s", strerror(errno));
+ p = btake(&b);
+
+ bfree(&b);
+
+ return p;
+}
+
+// fatal prints an error message to standard error and exits.
+void
+fatal(char *msg, ...)
+{
+ va_list arg;
+
+ fprintf(stderr, "go tool dist: ");
+ va_start(arg, msg);
+ vfprintf(stderr, msg, arg);
+ va_end(arg);
+ fprintf(stderr, "\n");
+ 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 && strcmp(p+np-ns, suffix) == 0;
+}
+
+// hasprefix reports whether p begins wtih 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);
+}
+
+// xexit exits the process with return code n.
+void
+xexit(int n)
+{
+ exit(n);
+}
+
+// xatexit schedules the exit-handler f to be run when the program exits.
+void
+xatexit(void (*f)(void))
+{
+ atexit(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);
+}
+
+// xsetenv sets the environment variable $name to the given value.
+void
+xsetenv(char *name, char *value)
+{
+ setenv(name, value, 1);
+}
+
+// main takes care of OS-specific startup and dispatches to xmain.
+int
+main(int argc, char **argv)
+{
+ char *p;
+ Buf b;
+ struct utsname u;
+
+ binit(&b);
+ p = argv[0];
+ if(hassuffix(p, "bin/go-tool/dist")) {
+ default_goroot = xstrdup(p);
+ default_goroot[strlen(p)-strlen("bin/go-tool/dist")] = '\0';
+ }
+
+ 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";
+#else
+ fatal("unknown operating system");
+#endif
+
+ if(gohostarch == nil) {
+ if(uname(&u) < 0)
+ fatal("uname: %s", strerror(errno));
+ if(contains(u.machine, "x86_64"))
+ gohostarch = "amd64";
+ else if(hassuffix(u.machine, "86"))
+ gohostarch = "386";
+ else if(contains(u.machine, "arm"))
+ gohostarch = "arm";
+ else
+ fatal("unknown architecture: %s", u.machine);
+ }
+
+ init();
+ xmain(argc, argv);
+ bfree(&b);
+ return 0;
+}
+
+// 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);
+}
+
+// xstrcmp compares the NUL-terminated strings a and b.
+int
+xstrcmp(char *a, char *b)
+{
+ return strcmp(a, b);
+}
+
+// xstrstr returns a pointer to the first occurrence of b in a.
+char*
+xstrstr(char *a, char *b)
+{
+ return strstr(a, b);
+}
+
+// xstrrchr returns a pointer to the final occurrence of c in p.
+char*
+xstrrchr(char *p, int c)
+{
+ return strrchr(p, c);
+}
+
+#endif // PLAN9
+#endif // __WINDOWS__
--- /dev/null
+// Copyright 2012 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.
+
+// 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
+//
+#ifdef WIN32
+
+// Portability layer implemented for Windows.
+// See unix.c for doc comments about exported functions.
+
+#include "a.h"
+#include <windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+/*
+ * Windows uses 16-bit rune strings in the APIs.
+ * Define conversions between Rune* and UTF-8 char*.
+ */
+
+typedef unsigned char uchar;
+typedef unsigned short Rune; // same as Windows
+
+// encoderune encodes the rune r into buf and returns
+// the number of bytes used.
+static int
+encoderune(char *buf, Rune r)
+{
+ if(r < 0x80) { // 7 bits
+ buf[0] = r;
+ return 1;
+ }
+ if(r < 0x800) { // 5+6 bits
+ buf[0] = 0xc0 | (r>>6);
+ buf[1] = 0x80 | (r&0x3f);
+ return 2;
+ }
+ buf[0] = 0xe0 | (r>>12);
+ buf[1] = 0x80 | ((r>>6)&0x3f);
+ buf[2] = 0x80 | (r&0x3f);
+ return 3;
+}
+
+// decoderune decodes the rune encoding at sbuf into r
+// and returns the number of bytes used.
+static int
+decoderune(Rune *r, char *sbuf)
+{
+ uchar *buf;
+
+ buf = (uchar*)sbuf;
+ if(buf[0] < 0x80) {
+ *r = buf[0];
+ return 1;
+ }
+ if((buf[0]&0xe0) == 0xc0 && (buf[1]&0xc0) == 0x80) {
+ *r = (buf[0]&~0xc0)<<6 | (buf[1]&~0x80);
+ if(*r < 0x80)
+ goto err;
+ return 2;
+ }
+ if((buf[0]&0xf0) == 0xe0 && (buf[1]&0xc0) == 0x80 && (buf[2]&0xc0) == 0x80) {
+ *r = (buf[0]&~0xc0)<<12 | (buf[1]&~0x80)<<6 | (buf[2]&~0x80);
+ if(*r < 0x800)
+ goto err;
+ return 3;
+ }
+err:
+ *r = 0xfffd;
+ return 1;
+}
+
+// toutf replaces b with the UTF-8 encoding of the rune string r.
+static void
+toutf(Buf *b, Rune *r)
+{
+ int i, n;
+ char buf[4];
+
+ breset(b);
+ for(i=0; r[i]; i++) {
+ n = encoderune(buf, r[i]);
+ bwrite(b, buf, n);
+ }
+}
+
+// torune replaces *rp with a pointer to a newly allocated
+// rune string equivalent of the UTF-8 string p.
+static void
+torune(Rune **rp, char *p)
+{
+ int i, n;
+ Rune *r, *w, r1;
+
+ r = xmalloc((strlen(p)+1) * sizeof r[0]);
+ w = r;
+ while(*p)
+ p += decoderune(w++, p);
+ *w = 0;
+ *rp = r;
+}
+
+// errstr returns the most recent Windows error, in string form.
+static char*
+errstr(void)
+{
+ DWORD code;
+ Rune *r;
+ Buf b;
+
+ binit(&b);
+ code = GetLastError();
+ r = nil;
+ FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
+ nil, code, 0, (Rune*)&r, 0, nil);
+ toutf(&b, r);
+ return bstr(&b); // leak but we're dying anyway
+}
+
+void
+xgetenv(Buf *b, char *name)
+{
+ char *p;
+ Rune *buf;
+ int n;
+ Rune *r;
+
+ breset(b);
+ torune(&r, name);
+ n = GetEnvironmentVariableW(r, NULL, 0);
+ if(n > 0) {
+ buf = xmalloc((n+1)*sizeof buf[0]);
+ GetEnvironmentVariableW(r, buf, n+1);
+ buf[n] = '\0';
+ toutf(b, buf);
+ xfree(buf);
+ }
+ xfree(r);
+}
+
+void
+xsetenv(char *name, char *value)
+{
+ Rune *rname, *rvalue;
+
+ torune(&rname, name);
+ torune(&rvalue, value);
+ SetEnvironmentVariableW(rname, rvalue);
+ xfree(rname);
+ xfree(rvalue);
+}
+
+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);
+}
+
+static void
+breadfrom(Buf *b, HANDLE h)
+{
+ DWORD n;
+
+ for(;;) {
+ if(b->len > 1<<22)
+ fatal("unlikely file size in readfrom");
+ bgrow(b, 4096);
+ n = 0;
+ if(!ReadFile(h, b->p+b->len, 4096, &n, nil))
+ fatal("ReadFile: %s", errstr());
+ if(n == 0)
+ break;
+ b->len += n;
+ }
+}
+
+void
+runv(Buf *b, char *dir, int mode, Vec *argv)
+{
+ int i, j, nslash;
+ Buf cmd;
+ char *e, *q;
+ Rune *rcmd, *rexe, *rdir;
+ STARTUPINFOW si;
+ PROCESS_INFORMATION pi;
+ HANDLE p[2];
+ DWORD code;
+
+ 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);
+ }
+ bwritestr(&cmd, q);
+ }
+ //xprintf("%s\n", bstr(&cmd));
+
+ breset(&cmd);
+ for(i=0; i<argv->len; i++) {
+ if(i > 0)
+ bwritestr(&cmd, " ");
+ q = argv->p[i];
+ if(contains(q, " ") || contains(q, "\t") || contains(q, "\\") || contains(q, "\"")) {
+ bwritestr(&cmd, "\"");
+ nslash = 0;
+ for(; *q; q++) {
+ if(*q == '\\') {
+ nslash++;
+ continue;
+ }
+ if(*q == '"') {
+ for(j=0; j<2*nslash+1; j++)
+ bwritestr(&cmd, "\\");
+ nslash = 0;
+ }
+ for(j=0; j<nslash; j++)
+ bwritestr(&cmd, "\\");
+ nslash = 0;
+ bwrite(&cmd, q, 1);
+ }
+ for(j=0; j<2*nslash; j++)
+ bwritestr(&cmd, "\\");
+ bwritestr(&cmd, "\"");
+ } else {
+ bwritestr(&cmd, q);
+ }
+ }
+
+ torune(&rcmd, bstr(&cmd));
+ rexe = nil;
+ rdir = nil;
+ if(dir != nil)
+ torune(&rdir, dir);
+
+ memset(&si, 0, sizeof si);
+ si.cb = sizeof si;
+ si.dwFlags = STARTF_USESTDHANDLES;
+ si.hStdInput = INVALID_HANDLE_VALUE;
+ if(b == nil) {
+ si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
+ si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
+ } else {
+ breset(b);
+ if(!CreatePipe(&p[0], &p[1], nil, 0))
+ fatal("CreatePipe: %s", errstr());
+ si.hStdOutput = p[1];
+ si.hStdError = p[1];
+ }
+
+ if(!CreateProcessW(rexe, rcmd, nil, nil, TRUE, 0, nil, rdir, &si, &pi)) {
+ if(mode!=CheckExit)
+ return;
+ fatal("%s: %s", argv->p[0], errstr());
+ }
+ if(rexe != nil)
+ xfree(rexe);
+ xfree(rcmd);
+ if(rdir != nil)
+ xfree(rdir);
+ if(b != nil) {
+ CloseHandle(p[1]);
+ breadfrom(b, p[0]);
+ CloseHandle(p[0]);
+ }
+ WaitForSingleObject(pi.hProcess, INFINITE);
+
+ if(!GetExitCodeProcess(pi.hProcess, &code))
+ fatal("GetExitCodeProcess: %s", errstr());
+ if(mode==CheckExit && code != 0)
+ fatal("%s failed", argv->p[0]);
+}
+
+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);
+}
+
+// rgetwd returns a rune string form of the current directory's path.
+static Rune*
+rgetwd(void)
+{
+ int n;
+ Rune *r;
+
+ n = GetCurrentDirectory(0, nil);
+ r = xmalloc((n+1)*sizeof r[0]);
+ GetCurrentDirectoryW(n+1, r);
+ r[n] = '\0';
+ return r;
+}
+
+void
+xgetwd(Buf *b)
+{
+ Rune *r;
+
+ r = rgetwd();
+ breset(b);
+ toutf(b, r);
+ xfree(r);
+}
+
+void
+xrealwd(Buf *b, char *path)
+{
+ int n;
+ Rune *old;
+ Rune *rnew;
+
+ old = rgetwd();
+ torune(&rnew, path);
+ if(!SetCurrentDirectoryW(rnew))
+ fatal("chdir %s: %s", path, errstr());
+ free(rnew);
+ xgetwd(b);
+ if(!SetCurrentDirectoryW(old)) {
+ breset(b);
+ toutf(b, old);
+ fatal("chdir %s: %s", bstr(b), errstr());
+ }
+}
+
+bool
+isdir(char *p)
+{
+ int attr;
+ Rune *r;
+
+ torune(&r, p);
+ attr = GetFileAttributesW(r);
+ xfree(r);
+ return attr >= 0 && (attr & FILE_ATTRIBUTE_DIRECTORY);
+}
+
+bool
+isfile(char *p)
+{
+ int attr;
+ Rune *r;
+
+ torune(&r, p);
+ attr = GetFileAttributesW(r);
+ xfree(r);
+ return attr >= 0 && !(attr & FILE_ATTRIBUTE_DIRECTORY);
+}
+
+Time
+mtime(char *p)
+{
+ HANDLE h;
+ WIN32_FIND_DATAW data;
+ Rune *r;
+ Time t;
+ FILETIME *ft;
+
+ torune(&r, p);
+ h = FindFirstFileW(r, &data);
+ xfree(r);
+ if(h == INVALID_HANDLE_VALUE)
+ return 0;
+ ft = &data.ftLastWriteTime;
+ return (Time)ft->dwLowDateTime + ((Time)ft->dwHighDateTime<<32);
+}
+
+bool
+isabs(char *p)
+{
+ // c:/ or c:\
+ if(('A' <= p[0] && p[0] <= 'Z') || ('a' <= p[0] && p[0] <= 'z'))
+ return p[1] == ':' && (p[2] == '/' || p[2] == '\\');
+ // / or \
+ return p[0] == '/' || p[0] == '\\';
+}
+
+void
+readfile(Buf *b, char *file)
+{
+ HANDLE h;
+ Rune *r;
+
+ torune(&r, file);
+ h = CreateFileW(r, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);
+ if(h == INVALID_HANDLE_VALUE)
+ fatal("open %s: %s", file, errstr());
+ breadfrom(b, h);
+ CloseHandle(h);
+}
+
+void
+writefile(Buf *b, char *file)
+{
+ HANDLE h;
+ Rune *r;
+ DWORD n;
+
+ torune(&r, file);
+ h = CreateFileW(r, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, nil, CREATE_ALWAYS, 0, 0);
+ if(h == INVALID_HANDLE_VALUE)
+ fatal("create %s: %s", file, errstr());
+ n = 0;
+ if(!WriteFile(h, b->p, b->len, &n, 0))
+ fatal("write %s: %s", file, errstr());
+ CloseHandle(h);
+}
+
+
+void
+xmkdir(char *p)
+{
+ Rune *r;
+
+ torune(&r, p);
+ if(!CreateDirectoryW(r, nil))
+ fatal("mkdir %s: %s", p, errstr());
+ xfree(r);
+}
+
+void
+xmkdirall(char *p)
+{
+ int c;
+ char *q, *q2;
+
+ if(isdir(p))
+ return;
+ q = strrchr(p, '/');
+ q2 = strrchr(p, '\\');
+ if(q2 != nil && (q == nil || q < q2))
+ q = q2;
+ if(q != nil) {
+ c = *q;
+ *q = '\0';
+ xmkdirall(p);
+ *q = c;
+ }
+ xmkdir(p);
+}
+
+void
+xremove(char *p)
+{
+ int attr;
+ Rune *r;
+
+ torune(&r, p);
+ attr = GetFileAttributesW(r);
+ if(attr >= 0) {
+ if(attr & FILE_ATTRIBUTE_DIRECTORY)
+ RemoveDirectoryW(r);
+ else
+ DeleteFileW(r);
+ }
+ xfree(r);
+}
+
+void
+xreaddir(Vec *dst, char *dir)
+{
+ Rune *r;
+ Buf b;
+ HANDLE h;
+ WIN32_FIND_DATAW data;
+ char *p, *q;
+
+ binit(&b);
+ vreset(dst);
+
+ bwritestr(&b, dir);
+ bwritestr(&b, "\\*");
+ torune(&r, bstr(&b));
+
+ h = FindFirstFileW(r, &data);
+ xfree(r);
+ if(h == INVALID_HANDLE_VALUE)
+ goto out;
+ do{
+ toutf(&b, data.cFileName);
+ p = bstr(&b);
+ q = xstrrchr(p, '\\');
+ if(q != nil)
+ p = q+1;
+ if(!streq(p, ".") && !streq(p, ".."))
+ vadd(dst, p);
+ }while(FindNextFileW(h, &data));
+ FindClose(h);
+
+out:
+ bfree(&b);
+}
+
+char*
+xworkdir(void)
+{
+ Rune buf[1024];
+ Rune tmp[MAX_PATH];
+ Rune go[3] = {'g', 'o', '\0'};
+ int n;
+ Buf b;
+
+ n = GetTempPathW(nelem(buf), buf);
+ if(n <= 0)
+ fatal("GetTempPath: %s", errstr());
+ buf[n] = '\0';
+
+ if(GetTempFileNameW(buf, go, 0, tmp) == 0)
+ fatal("GetTempFileName: %s", errstr());
+ DeleteFileW(tmp);
+ if(!CreateDirectoryW(tmp, nil))
+ fatal("create tempdir: %s", errstr());
+
+ binit(&b);
+ toutf(&b, tmp);
+ return btake(&b);
+}
+
+void
+xremoveall(char *p)
+{
+ int i;
+ Buf b;
+ Vec dir;
+ Rune *r;
+
+ binit(&b);
+ vinit(&dir);
+
+ torune(&r, p);
+ if(isdir(p)) {
+ xreaddir(&dir, p);
+ for(i=0; i<dir.len; i++) {
+ bprintf(&b, "%s/%s", p, dir.p[i]);
+ xremoveall(bstr(&b));
+ }
+ RemoveDirectoryW(r);
+ } else {
+ DeleteFileW(r);
+ }
+ xfree(r);
+
+ bfree(&b);
+ vfree(&dir);
+}
+
+void
+fatal(char *msg, ...)
+{
+ static char buf1[1024];
+ va_list arg;
+
+ va_start(arg, msg);
+ vsnprintf(buf1, sizeof buf1, msg, arg);
+ va_end(arg);
+
+ fprintf(stderr, "cbuild: %s\n", buf1);
+ fflush(stderr);
+ ExitProcess(1);
+}
+
+// HEAP is the persistent handle to the default process heap.
+static HANDLE HEAP = INVALID_HANDLE_VALUE;
+
+void*
+xmalloc(int n)
+{
+ void *p;
+
+ if(HEAP == INVALID_HANDLE_VALUE)
+ HEAP = GetProcessHeap();
+ p = HeapAlloc(HEAP, 0, n);
+ if(p == nil)
+ fatal("out of memory allocating %d: %s", n, errstr());
+ memset(p, 0, n);
+ return p;
+}
+
+char*
+xstrdup(char *p)
+{
+ char *q;
+
+ q = xmalloc(strlen(p)+1);
+ strcpy(q, p);
+ return q;
+}
+
+void
+xfree(void *p)
+{
+ if(HEAP == INVALID_HANDLE_VALUE)
+ HEAP = GetProcessHeap();
+ HeapFree(HEAP, 0, p);
+}
+
+void*
+xrealloc(void *p, int n)
+{
+ if(p == nil)
+ return xmalloc(n);
+ if(HEAP == INVALID_HANDLE_VALUE)
+ HEAP = GetProcessHeap();
+ p = HeapReAlloc(HEAP, HEAP_GENERATE_EXCEPTIONS, p, n);
+ if(p == nil)
+ fatal("out of memory reallocating %d", n);
+ return p;
+}
+
+bool
+hassuffix(char *p, char *suffix)
+{
+ int np, ns;
+
+ np = strlen(p);
+ ns = strlen(suffix);
+ return np >= ns && strcmp(p+np-ns, suffix) == 0;
+}
+
+bool
+hasprefix(char *p, char *prefix)
+{
+ return strncmp(p, prefix, strlen(prefix)) == 0;
+}
+
+bool
+contains(char *p, char *sep)
+{
+ return strstr(p, sep) != nil;
+}
+
+bool
+streq(char *p, char *q)
+{
+ return strcmp(p, q) == 0;
+}
+
+char*
+lastelem(char *p)
+{
+ char *out;
+
+ out = p;
+ for(; *p; p++)
+ if(*p == '/' || *p == '\\')
+ out = p+1;
+ return out;
+}
+
+void
+xmemmove(void *dst, void *src, int n)
+{
+ memmove(dst, src, n);
+}
+
+int
+xmemcmp(void *a, void *b, int n)
+{
+ return memcmp(a, b, n);
+}
+
+int
+xstrlen(char *p)
+{
+ return strlen(p);
+}
+
+void
+xexit(int n)
+{
+ exit(n);
+}
+
+void
+xatexit(void (*f)(void))
+{
+ atexit(f);
+}
+
+void
+xprintf(char *fmt, ...)
+{
+ va_list arg;
+ static char buf[1024];
+ DWORD n;
+
+ va_start(arg, fmt);
+ vsnprintf(buf, sizeof buf, fmt, arg);
+ va_end(arg);
+ n = 0;
+ WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), buf, strlen(buf), &n, 0);
+ fflush(stdout);
+}
+
+int
+main(int argc, char **argv)
+{
+ char *p;
+
+ setvbuf(stdout, nil, _IOLBF, 0);
+ setvbuf(stderr, nil, _IOLBF, 0);
+
+ p = argv[0];
+ if(hassuffix(p, "bin/go-tool/dist.exe") || hassuffix(p, "bin\\go-tool\\dist.exe")) {
+ default_goroot = xstrdup(p);
+ default_goroot[strlen(p)-strlen("bin/go-tool/dist.exe")] = '\0';
+ }
+
+ slash = "\\";
+ gohostos = "windows";
+ init();
+ xmain(argc, argv);
+ return 0;
+}
+
+void
+xqsort(void *data, int n, int elemsize, int (*cmp)(const void*, const void*))
+{
+ qsort(data, n, elemsize, cmp);
+}
+
+int
+xstrcmp(char *a, char *b)
+{
+ return strcmp(a, b);
+}
+
+char*
+xstrstr(char *a, char *b)
+{
+ return strstr(a, b);
+}
+
+char*
+xstrrchr(char *p, int c)
+{
+ char *ep;
+
+ ep = p+strlen(p);
+ for(ep=p+strlen(p); ep >= p; ep--)
+ if(*ep == c)
+ return ep;
+ return nil;
+}
+
+#endif // __WINDOWS__