summaryrefslogtreecommitdiff
path: root/src/cmd/dist
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/dist')
-rw-r--r--src/cmd/dist/README45
-rw-r--r--src/cmd/dist/a.h149
-rw-r--r--src/cmd/dist/arg.h50
-rw-r--r--src/cmd/dist/buf.c279
-rw-r--r--src/cmd/dist/build.c1530
-rw-r--r--src/cmd/dist/buildgc.c106
-rw-r--r--src/cmd/dist/buildruntime.c346
-rw-r--r--src/cmd/dist/goc2c.c735
-rw-r--r--src/cmd/dist/main.c41
-rw-r--r--src/cmd/dist/unix.c720
-rw-r--r--src/cmd/dist/windows.c910
11 files changed, 4911 insertions, 0 deletions
diff --git a/src/cmd/dist/README b/src/cmd/dist/README
new file mode 100644
index 000000000..cf194faf0
--- /dev/null
+++ b/src/cmd/dist/README
@@ -0,0 +1,45 @@
+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.
+
diff --git a/src/cmd/dist/a.h b/src/cmd/dist/a.h
new file mode 100644
index 000000000..c19b1f468
--- /dev/null
+++ b/src/cmd/dist/a.h
@@ -0,0 +1,149 @@
+// 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 bsubst(Buf *b, char *x, char *y);
+void bfree(Buf *b);
+void bgrow(Buf *b, int n);
+void binit(Buf *b);
+char* bpathf(Buf *b, char *fmt, ...);
+char* bprintf(Buf *b, char *fmt, ...);
+void bwritef(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 *goarch;
+extern char *gobin;
+extern char *gochar;
+extern char *gohostarch;
+extern char *gohostos;
+extern char *goos;
+extern char *goroot;
+extern char *goroot_final;
+extern char *goversion;
+extern char *workdir;
+extern char *tooldir;
+extern char *slash;
+extern bool rebuildall;
+
+int find(char*, char**, int);
+void init(void);
+void cmdbanner(int, char**);
+void cmdbootstrap(int, char**);
+void cmdclean(int, char**);
+void cmdenv(int, char**);
+void cmdinstall(int, char**);
+void cmdversion(int, char**);
+
+// buildgc.c
+void gcopnames(char*, char*);
+void mkenam(char*, char*);
+
+// buildruntime.c
+void mkzasm(char*, char*);
+void mkzgoarch(char*, char*);
+void mkzgoos(char*, char*);
+void mkzruntimedefs(char*, char*);
+void mkzversion(char*, char*);
+
+// goc2c.c
+void goc2c(char*, char*);
+
+// main.c
+extern int vflag;
+void usage(void);
+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);
+void bgrunv(char *dir, int mode, Vec *argv);
+void bgwait(void);
+bool streq(char*, char*);
+void writefile(Buf*, char*, int);
+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 xstrlen(char*);
+char* xstrrchr(char*, int);
+char* xstrstr(char*, char*);
+char* xworkdir(void);
diff --git a/src/cmd/dist/arg.h b/src/cmd/dist/arg.h
new file mode 100644
index 000000000..6eef0353b
--- /dev/null
+++ b/src/cmd/dist/arg.h
@@ -0,0 +1,50 @@
+/*
+Derived from Inferno include/kern.h.
+
+http://code.google.com/p/inferno-os/source/browse/include/kern.h
+
+ Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+ Revisions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com). All rights reserved.
+ Portions Copyright © 2009 The Go Authors. All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+/* command line */
+extern char *argv0;
+#define ARGBEGIN for((argv0?0:(argv0=(*argv))),argv++,argc--;\
+ argv[0] && argv[0][0]=='-' && argv[0][1];\
+ argc--, argv++) {\
+ char *_args, *_argt;\
+ char _argc;\
+ _args = &argv[0][1];\
+ if(_args[0]=='-' && _args[1]==0){\
+ argc--; argv++; break;\
+ }\
+ _argc = 0;\
+ while((_argc = *_args++) != 0)\
+ switch(_argc)
+#define ARGEND _argt=0;USED(_argt);USED(_argc);USED(_args);}USED(argv);USED(argc);
+#define ARGF() (_argt=_args, _args="",\
+ (*_argt? _argt: argv[1]? (argc--, *++argv): 0))
+#define EARGF(x) (_argt=_args, _args="",\
+ (*_argt? _argt: argv[1]? (argc--, *++argv): ((x), fatal("usage"), (char*)0)))
+
+#define ARGC() _argc
+
diff --git a/src/cmd/dist/buf.c b/src/cmd/dist/buf.c
new file mode 100644
index 000000000..45fb1954d
--- /dev/null
+++ b/src/cmd/dist/buf.c
@@ -0,0 +1,279 @@
+// 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;
+}
+
+// bsubst rewites b to replace all occurrences of x with y.
+void
+bsubst(Buf *b, char *x, char *y)
+{
+ char *p;
+ int nx, ny, pos;
+
+ nx = xstrlen(x);
+ ny = xstrlen(y);
+
+ pos = 0;
+ for(;;) {
+ p = xstrstr(bstr(b)+pos, x);
+ if(p == nil)
+ break;
+ if(nx != ny) {
+ if(nx < ny) {
+ pos = p - b->p;
+ bgrow(b, ny-nx);
+ p = b->p + pos;
+ }
+ xmemmove(p+ny, p+nx, (b->p+b->len)-(p+nx));
+ }
+ xmemmove(p, y, ny);
+ pos = p+ny - b->p;
+ b->len += ny - nx;
+ }
+}
+
+// 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(n>0 && streq(v->p[i], v->p[n-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;
+ 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);
+ }
+}
diff --git a/src/cmd/dist/build.c b/src/cmd/dist/build.c
new file mode 100644
index 000000000..567c9f336
--- /dev/null
+++ b/src/cmd/dist/build.c
@@ -0,0 +1,1530 @@
+// 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 "arg.h"
+
+/*
+ * Initialization for any invocation.
+ */
+
+// The usual variables.
+char *goarch;
+char *gobin;
+char *gohostarch;
+char *gohostchar;
+char *gohostos;
+char *goos;
+char *goroot = GOROOT_FINAL;
+char *goroot_final = GOROOT_FINAL;
+char *workdir;
+char *tooldir;
+char *gochar;
+char *goversion;
+char *slash; // / for unix, \ for windows
+
+bool rebuildall = 0;
+
+static bool shouldbuild(char*, char*);
+static void copy(char*, char*, int);
+static char *findgoversion(void);
+
+// 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.
+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 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);
+
+ 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);
+
+ xsetenv("GOROOT", goroot);
+ xsetenv("GOARCH", goarch);
+ xsetenv("GOOS", goos);
+
+ // Make the environment more predictable.
+ xsetenv("LANG", "C");
+ xsetenv("LANGUAGE", "en_US.UTF8");
+
+ goversion = findgoversion();
+
+ workdir = xworkdir();
+ xatexit(rmworkdir);
+
+ bpathf(&b, "%s/pkg/tool/%s_%s", goroot, gohostos, gohostarch);
+ tooldir = btake(&b);
+
+ bfree(&b);
+}
+
+// rmworkdir deletes the work directory.
+static void
+rmworkdir(void)
+{
+ if(vflag > 1)
+ xprintf("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--;
+}
+
+
+// findgoversion determines the Go version to use in the version string.
+static char*
+findgoversion(void)
+{
+ char *tag, *rev, *p;
+ int i, nrev;
+ Buf b, path, bmore, branch;
+ Vec tags;
+
+ binit(&b);
+ binit(&path);
+ binit(&bmore);
+ binit(&branch);
+ vinit(&tags);
+
+ // The $GOROOT/VERSION file takes priority, for distributions
+ // without the Mercurial repo.
+ bpathf(&path, "%s/VERSION", goroot);
+ if(isfile(bstr(&path))) {
+ readfile(&b, bstr(&path));
+ chomp(&b);
+ // 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;
+ }
+
+ // The $GOROOT/VERSION.cache file is a cache to avoid invoking
+ // hg 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;
+ }
+
+ // Otherwise, use Mercurial.
+ // What is the current branch?
+ run(&branch, goroot, CheckExit, "hg", "identify", "-b", nil);
+ chomp(&branch);
+
+ // What are the tags along the current branch?
+ tag = "";
+ rev = ".";
+ run(&b, goroot, CheckExit, "hg", "log", "-b", bstr(&branch), "-r", ".:0", "--template", "{tags} + ", nil);
+ splitfields(&tags, bstr(&b));
+ nrev = 0;
+ for(i=0; i<tags.len; i++) {
+ p = tags.p[i];
+ if(streq(p, "+"))
+ nrev++;
+ if(hasprefix(p, "release.") || hasprefix(p, "weekly.") || hasprefix(p, "go")) {
+ tag = xstrdup(p);
+ // If this tag matches the current checkout
+ // exactly (no "+" yet), don't show extra
+ // revision information.
+ if(nrev == 0)
+ rev = "";
+ break;
+ }
+ }
+
+ if(tag[0] == '\0') {
+ // Did not find a tag; use branch name.
+ bprintf(&b, "branch.%s", bstr(&branch));
+ tag = btake(&b);
+ }
+
+ if(rev[0]) {
+ // Tag is before the revision we're building.
+ // Add extra information.
+ run(&bmore, goroot, CheckExit, "hg", "log", "--template", " +{node|short}", "-r", rev, nil);
+ chomp(&bmore);
+ }
+
+ bprintf(&b, "%s", tag);
+ if(bmore.len > 0)
+ bwriteb(&b, &bmore);
+
+ // Cache version.
+ writefile(&b, bstr(&path), 0);
+
+done:
+ p = btake(&b);
+
+
+ bfree(&b);
+ bfree(&path);
+ bfree(&bmore);
+ bfree(&branch);
+ vfree(&tags);
+
+ return p;
+}
+
+/*
+ * 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",
+ "6prof",
+ "cgo",
+ "ebnflint",
+ "goapi",
+ "gofix",
+ "goinstall",
+ "gomake",
+ "gopack",
+ "gopprof",
+ "gotest",
+ "gotype",
+ "govet",
+ "goyacc",
+ "quietgcc",
+};
+
+// Unreleased directories (relative to $GOROOT) that should
+// not be in release branches.
+static char *unreleased[] = {
+ "src/cmd/cov",
+ "src/cmd/prof",
+ "src/pkg/old",
+ "src/pkg/exp",
+};
+
+// setup sets up the tree for the initial build.
+static void
+setup(void)
+{
+ int i;
+ Buf b;
+ char *p;
+
+ binit(&b);
+
+ // Create bin directory.
+ p = bpathf(&b, "%s/bin", goroot);
+ if(!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);
+ }
+
+ // Create object directory.
+ // We keep it in pkg/ so that all the generated binaries
+ // 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);
+
+ // Create tool directory.
+ // We keep it in pkg/, just like the object directory above.
+ if(rebuildall)
+ xremoveall(tooldir);
+ xmkdirall(tooldir);
+
+ // Remove tool binaries from before the tool/gohostos_gohostarch
+ xremoveall(bpathf(&b, "%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]));
+
+ // 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 release, make sure excluded things are excluded.
+ if(hasprefix(goversion, "release.") || hasprefix(goversion, "go")) {
+ 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));
+ }
+
+ bfree(&b);
+}
+
+/*
+ * C library and tool building
+ */
+
+// gccargs is the gcc command line to use for compiling a single C file.
+static char *proto_gccargs[] = {
+ "-Wall",
+ "-Wno-sign-compare",
+ "-Wno-missing-braces",
+ "-Wno-parentheses",
+ "-Wno-unknown-pragmas",
+ "-Wno-switch",
+ "-Wno-comment",
+ "-Werror",
+ "-fno-common",
+ "-ggdb",
+ "-O2",
+};
+
+static Vec gccargs;
+
+// 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/*",
+ }},
+ {"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/pkg/obj/$GOOS_$GOARCH/libcc.a",
+ }},
+ {"cmd/6c", {
+ "../cc/pgen.c",
+ "../cc/pswt.c",
+ "../6l/enam.c",
+ "$GOROOT/pkg/obj/$GOOS_$GOARCH/libcc.a",
+ }},
+ {"cmd/8c", {
+ "../cc/pgen.c",
+ "../cc/pswt.c",
+ "../8l/enam.c",
+ "$GOROOT/pkg/obj/$GOOS_$GOARCH/libcc.a",
+ }},
+ {"cmd/5g", {
+ "../gc/cplx.c",
+ "../gc/pgen.c",
+ "../5l/enam.c",
+ "$GOROOT/pkg/obj/$GOOS_$GOARCH/libgc.a",
+ }},
+ {"cmd/6g", {
+ "../gc/cplx.c",
+ "../gc/pgen.c",
+ "../6l/enam.c",
+ "$GOROOT/pkg/obj/$GOOS_$GOARCH/libgc.a",
+ }},
+ {"cmd/8g", {
+ "../gc/cplx.c",
+ "../gc/pgen.c",
+ "../8l/enam.c",
+ "$GOROOT/pkg/obj/$GOOS_$GOARCH/libgc.a",
+ }},
+ {"cmd/5l", {
+ "../ld/data.c",
+ "../ld/elf.c",
+ "../ld/go.c",
+ "../ld/ldelf.c",
+ "../ld/ldmacho.c",
+ "../ld/ldpe.c",
+ "../ld/lib.c",
+ "../ld/symtab.c",
+ "enam.c",
+ }},
+ {"cmd/6l", {
+ "../ld/*",
+ "enam.c",
+ }},
+ {"cmd/8l", {
+ "../ld/*",
+ "enam.c",
+ }},
+ {"cmd/", {
+ "$GOROOT/pkg/obj/$GOOS_$GOARCH/libmach.a",
+ "$GOROOT/pkg/obj/$GOOS_$GOARCH/libbio.a",
+ "$GOROOT/pkg/obj/$GOOS_$GOARCH/lib9.a",
+ }},
+ {"pkg/runtime", {
+ "zasm_$GOOS_$GOARCH.h",
+ "zgoarch_$GOARCH.go",
+ "zgoos_$GOOS.go",
+ "zruntime_defs_$GOOS_$GOARCH.go",
+ "zversion.go",
+ }},
+};
+
+// depsuffix records the allowed suffixes for source files.
+char *depsuffix[] = {
+ ".c",
+ ".h",
+ ".s",
+ ".go",
+ ".goc",
+};
+
+// gentab records how to generate some trivial files.
+static struct {
+ char *nameprefix;
+ void (*gen)(char*, char*);
+} gentab[] = {
+ {"opnames.h", gcopnames},
+ {"enam.c", mkenam},
+ {"zasm_", mkzasm},
+ {"zgoarch_", mkzgoarch},
+ {"zgoos_", mkzgoos},
+ {"zruntime_defs_", mkzruntimedefs},
+ {"zversion.go", mkzversion},
+};
+
+// 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;
+ Buf b, b1, path;
+ 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))
+ xprintf("%s (%s/%s)\n", dir, goos, goarch);
+ else
+ xprintf("%s\n", dir);
+ }
+
+ 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.
+ bpathf(&path, "%s/src/%s", goroot, dir);
+ name = lastelem(dir);
+
+ // For misc/prof, copy into the tool directory and we're done.
+ if(hasprefix(dir, "misc/")) {
+ copy(bpathf(&b, "%s/%s", tooldir, name),
+ bpathf(&b1, "%s/misc/%s", goroot, name), 1);
+ goto out;
+ }
+
+ // For release, cmd/prof and cmd/cov are not included.
+ if((streq(dir, "cmd/cov") || streq(dir, "cmd/prof")) && !isdir(bstr(&path))) {
+ if(vflag > 1)
+ xprintf("skipping %s - does not exist\n", dir);
+ goto out;
+ }
+
+ // set up gcc command line on first run.
+ if(gccargs.len == 0) {
+ xgetenv(&b, "CC");
+ if(b.len == 0)
+ bprintf(&b, "gcc");
+ splitfields(&gccargs, bstr(&b));
+ for(i=0; i<nelem(proto_gccargs); i++)
+ vadd(&gccargs, proto_gccargs[i]);
+ if(xstrstr(bstr(&b), "clang") != nil) {
+ vadd(&gccargs, "-Wno-dangling-else");
+ vadd(&gccargs, "-Wno-unused-value");
+ }
+ }
+
+ islib = hasprefix(dir, "lib") || streq(dir, "cmd/cc") || streq(dir, "cmd/gc");
+ ispkg = hasprefix(dir, "pkg");
+ isgo = ispkg || streq(dir, "cmd/go") || streq(dir, "cmd/cgo");
+
+ exe = "";
+ if(streq(gohostos, "windows"))
+ exe = ".exe";
+
+ // Start final link command line.
+ // Note: code below knows that link.p[targ] is the target.
+ if(islib) {
+ // C library.
+ vadd(&link, "ar");
+ 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) {
+ // Go library (package).
+ vadd(&link, bpathf(&b, "%s/pack", tooldir));
+ vadd(&link, "grc");
+ p = bprintf(&b, "%s/pkg/%s_%s/%s", goroot, goos, goarch, dir+4);
+ *xstrrchr(p, '/') = '\0';
+ xmkdirall(p);
+ targ = link.len;
+ vadd(&link, bpathf(&b, "%s/pkg/%s_%s/%s.a", goroot, goos, goarch, dir+4));
+ } else if(streq(dir, "cmd/go") || streq(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 {
+ // C command. Use gccargs.
+ vcopy(&link, gccargs.p, gccargs.len);
+ 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");
+ }
+ ttarg = mtime(link.p[targ]);
+
+ // Gather files that are sources for this target.
+ // Everything in that directory, and any target-specific
+ // additions.
+ xreaddir(&files, bstr(&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(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);
+ 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.len = n;
+ continue;
+ }
+ vadd(&files, p);
+ }
+ }
+ }
+ vuniq(&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);
+ }
+ }
+
+ // 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;
+ ok:
+ t = mtime(p);
+ if(t != 0 && !hassuffix(p, ".a") && !shouldbuild(p, dir)) {
+ xfree(files.p[i]);
+ continue;
+ }
+ 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;
+ }
+ 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;
+
+ // For package runtime, copy some files into the work space.
+ if(streq(dir, "pkg/runtime")) {
+ copy(bpathf(&b, "%s/arch_GOARCH.h", workdir),
+ bpathf(&b1, "%s/arch_%s.h", bstr(&path), goarch), 0);
+ copy(bpathf(&b, "%s/defs_GOOS_GOARCH.h", workdir),
+ bpathf(&b1, "%s/defs_%s_%s.h", bstr(&path), goos, goarch), 0);
+ copy(bpathf(&b, "%s/os_GOOS.h", workdir),
+ bpathf(&b1, "%s/os_%s.h", bstr(&path), goos), 0);
+ copy(bpathf(&b, "%s/signals_GOOS.h", workdir),
+ bpathf(&b1, "%s/signals_%s.h", bstr(&path), goos), 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(hasprefix(elem, gentab[j].nameprefix)) {
+ if(vflag > 1)
+ xprintf("generate %s\n", p);
+ gentab[j].gen(bstr(&path), p);
+ // Do not add generated file to clean list.
+ // In pkg/runtime, we want to be able to
+ // build the package with the go tool,
+ // and it assumes these generated files already
+ // exist (it does not know how to build them).
+ // The 'clean' command can remove
+ // the generated files.
+ goto built;
+ }
+ }
+ // Did not rebuild p.
+ if(find(p, missing.p, missing.len) >= 0)
+ fatal("missing file %s", p);
+ built:;
+ }
+
+ // One more copy for package runtime.
+ // The last batch was required for the generators.
+ // This one is generated.
+ if(streq(dir, "pkg/runtime")) {
+ copy(bpathf(&b, "%s/zasm_GOOS_GOARCH.h", workdir),
+ bpathf(&b1, "%s/zasm_%s_%s.h", bstr(&path), goos, goarch), 0);
+ }
+
+ // Generate .c files from .goc files.
+ if(streq(dir, "pkg/runtime")) {
+ for(i=0; i<files.len; i++) {
+ p = files.p[i];
+ if(!hassuffix(p, ".goc"))
+ continue;
+ // b = path/zp but with _goarch.c instead of .goc
+ bprintf(&b, "%s%sz%s", bstr(&path), slash, lastelem(p));
+ b.len -= 4;
+ bwritef(&b, "_%s.c", goarch);
+ goc2c(p, bstr(&b));
+ vadd(&files, bstr(&b));
+ }
+ vuniq(&files);
+ }
+
+ if((!streq(goos, gohostos) || !streq(goarch, gohostarch)) && isgo) {
+ // We've generated the right files; the go command can do the build.
+ if(vflag > 1)
+ xprintf("skip build for cross-compile %s\n", dir);
+ goto nobuild;
+ }
+
+ // 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.p, gccargs.len);
+ vadd(&compile, "-c");
+ if(streq(gohostarch, "amd64"))
+ vadd(&compile, "-m64");
+ else if(streq(gohostarch, "386"))
+ vadd(&compile, "-m32");
+ if(streq(dir, "lib9"))
+ vadd(&compile, "-DPLAN9PORT");
+
+ vadd(&compile, "-I");
+ vadd(&compile, bpathf(&b, "%s/include", goroot));
+
+ vadd(&compile, "-I");
+ vadd(&compile, bstr(&path));
+
+ // lib9/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));
+ bprintf(&b1, "%s", goroot_final);
+ bsubst(&b1, "\\", "\\\\"); // turn into C string
+ vadd(&compile, bprintf(&b, "-DGOROOT=\"%s\"", bstr(&b1)));
+ 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"))
+ vadd(&compile, bpathf(&b, "%s/%sa", tooldir, gochar));
+ else {
+ vadd(&compile, bpathf(&b, "%s/%sc", tooldir, gochar));
+ vadd(&compile, "-FVw");
+ }
+ vadd(&compile, "-I");
+ vadd(&compile, workdir);
+ vadd(&compile, bprintf(&b, "-DGOOS_%s", goos));
+ vadd(&compile, bprintf(&b, "-DGOARCH_%s", goarch));
+ }
+
+ bpathf(&b, "%s/%s", workdir, lastelem(files.p[i]));
+ doclean = 1;
+ if(!isgo && streq(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;
+ }
+
+ b.p[b.len-1] = 'o'; // was c or s
+ vadd(&compile, "-o");
+ vadd(&compile, bstr(&b));
+ vadd(&compile, files.p[i]);
+ bgrunv(bstr(&path), CheckExit, &compile);
+
+ vadd(&link, bstr(&b));
+ if(doclean)
+ vadd(&clean, bstr(&b));
+ }
+ bgwait();
+
+ if(isgo) {
+ // The last loop was compiling individual files.
+ // Hand the Go files to the compiler en masse.
+ vreset(&compile);
+ vadd(&compile, bpathf(&b, "%s/%sg", tooldir, gochar));
+
+ bpathf(&b, "%s/_go_.%s", workdir, gochar);
+ 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[targ]);
+
+ runv(nil, nil, CheckExit, &link);
+
+nobuild:
+ // In package runtime, we install runtime.h and cgocall.h too,
+ // for use by cgo compilation.
+ if(streq(dir, "pkg/runtime")) {
+ copy(bpathf(&b, "%s/pkg/%s_%s/cgocall.h", goroot, goos, goarch),
+ bpathf(&b1, "%s/src/pkg/runtime/cgocall.h", goroot), 0);
+ copy(bpathf(&b, "%s/pkg/%s_%s/runtime.h", goroot, goos, goarch),
+ bpathf(&b1, "%s/src/pkg/runtime/runtime.h", goroot), 0);
+ }
+
+
+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;
+ 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;
+
+ // 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;
+
+ // 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") && !streq(dir, "cmd/cgo")) {
+ 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;
+}
+
+// copy copies the file src to dst, via memory (so only good for small files).
+static void
+copy(char *dst, char *src, int exec)
+{
+ Buf b;
+
+ if(vflag > 1)
+ xprintf("cp %s %s\n", src, dst);
+
+ binit(&b);
+ readfile(&b, src);
+ writefile(&b, dst, exec);
+ bfree(&b);
+}
+
+// buildorder records the order of builds for the 'go bootstrap' command.
+static char *buildorder[] = {
+ "lib9",
+ "libbio",
+ "libmach",
+
+ "misc/pprof",
+
+ "cmd/addr2line",
+ "cmd/cov",
+ "cmd/nm",
+ "cmd/objdump",
+ "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/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/os/exec",
+ "pkg/net/url",
+ "pkg/text/template/parse",
+ "pkg/text/template",
+ "pkg/go/doc",
+ "pkg/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[] = {
+ "cmd/5a",
+ "cmd/5c",
+ "cmd/5g",
+ "cmd/5l",
+ "cmd/6a",
+ "cmd/6c",
+ "cmd/6g",
+ "cmd/6l",
+ "cmd/8a",
+ "cmd/8c",
+ "cmd/8g",
+ "cmd/8l",
+ "cmd/addr2line",
+ "cmd/cc",
+ "cmd/cov",
+ "cmd/gc",
+ "cmd/go",
+ "cmd/nm",
+ "cmd/objdump",
+ "cmd/pack",
+ "cmd/prof",
+ "lib9",
+ "libbio",
+ "libmach",
+ "pkg/bufio",
+ "pkg/bytes",
+ "pkg/container/heap",
+ "pkg/encoding/base64",
+ "pkg/encoding/json",
+ "pkg/errors",
+ "pkg/flag",
+ "pkg/fmt",
+ "pkg/go/ast",
+ "pkg/go/build",
+ "pkg/go/doc",
+ "pkg/go/parser",
+ "pkg/go/scanner",
+ "pkg/go/token",
+ "pkg/io",
+ "pkg/io/ioutil",
+ "pkg/log",
+ "pkg/math",
+ "pkg/net/url",
+ "pkg/os",
+ "pkg/os/exec",
+ "pkg/path",
+ "pkg/path/filepath",
+ "pkg/reflect",
+ "pkg/regexp",
+ "pkg/regexp/syntax",
+ "pkg/runtime",
+ "pkg/sort",
+ "pkg/strconv",
+ "pkg/strings",
+ "pkg/sync",
+ "pkg/sync/atomic",
+ "pkg/syscall",
+ "pkg/text/template",
+ "pkg/text/template/parse",
+ "pkg/time",
+ "pkg/unicode",
+ "pkg/unicode/utf16",
+ "pkg/unicode/utf8",
+};
+
+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++) {
+ if((streq(cleantab[i], "cmd/cov") || streq(cleantab[i], "cmd/prof")) && !isdir(cleantab[i]))
+ continue;
+ bpathf(&path, "%s/src/%s", goroot, cleantab[i]);
+ xreaddir(&dir, bstr(&path));
+ // 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]));
+ }
+ }
+ // Remove generated binary named for directory.
+ if(hasprefix(cleantab[i], "cmd/"))
+ xremove(bpathf(&b, "%s/%s", bstr(&path), cleantab[i]+4));
+ }
+
+ if(rebuildall) {
+ // Remove object tree.
+ xremoveall(bpathf(&b, "%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);
+
+ // Remove cached version info.
+ xremove(bpathf(&b, "%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);
+}
+
+// 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 'p':
+ pflag = 1;
+ break;
+ case 'v':
+ vflag++;
+ break;
+ case 'w':
+ format = "set %s=%s\n";
+ break;
+ default:
+ usage();
+ }ARGEND
+
+ if(argc > 0)
+ usage();
+
+ 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(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);
+}
+
+// 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 'v':
+ vflag++;
+ break;
+ default:
+ usage();
+ }ARGEND
+
+ if(argc > 0)
+ usage();
+
+ if(rebuildall)
+ clean();
+ goversion = findgoversion();
+ setup();
+
+ xsetenv("GOROOT", goroot);
+ xsetenv("GOROOT_FINAL", goroot_final);
+
+ // 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);
+
+ // Build pkg/runtime for actual goos/goarch too.
+ if(!streq(goos, gohostos) || !streq(goarch, gohostarch))
+ install("pkg/runtime");
+
+ bfree(&b);
+}
+
+static char*
+defaulttarg(void)
+{
+ char *p;
+ Buf pwd, src, real_src;
+
+ binit(&pwd);
+ binit(&src);
+ binit(&real_src);
+
+ // 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;
+ // guard againt xrealwd return the directory without the trailing /
+ if(*p == slash[0])
+ p++;
+
+ bfree(&pwd);
+ bfree(&src);
+ bfree(&real_src);
+
+ return p;
+}
+
+// Install installs the list of packages named on the command line.
+void
+cmdinstall(int argc, char **argv)
+{
+ int i;
+
+ ARGBEGIN{
+ case 'v':
+ vflag++;
+ break;
+ default:
+ usage();
+ }ARGEND
+
+ if(argc == 0)
+ install(defaulttarg());
+
+ for(i=0; i<argc; i++)
+ install(argv[i]);
+}
+
+// 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();
+}
+
+// Banner prints the 'now you've installed Go' banner.
+void
+cmdbanner(int argc, char **argv)
+{
+ char *pathsep;
+ Buf b, b1, search;
+
+ ARGBEGIN{
+ case 'v':
+ vflag++;
+ break;
+ default:
+ usage();
+ }ARGEND
+
+ if(argc > 0)
+ usage();
+
+ binit(&b);
+ binit(&b1);
+ binit(&search);
+
+ xprintf("\n");
+ xprintf("---\n");
+ xprintf("Installed Go for %s/%s in %s\n", goos, goarch, goroot);
+ xprintf("Installed commands in %s\n", gobin);
+
+ // 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")) {
+ xprintf("\n"
+ "On OS X the debuggers must be installed setgid procmod.\n"
+ "Read and run ./sudo.bash to install the debuggers.\n");
+ }
+
+ if(!streq(goroot_final, goroot)) {
+ xprintf("\n"
+ "The binaries expect %s to be copied or moved to %s\n",
+ goroot, goroot_final);
+ }
+
+ bfree(&b);
+ bfree(&b1);
+ bfree(&search);
+}
+
+// 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);
+}
diff --git a/src/cmd/dist/buildgc.c b/src/cmd/dist/buildgc.c
new file mode 100644
index 000000000..da38760c6
--- /dev/null
+++ b/src/cmd/dist/buildgc.c
@@ -0,0 +1,106 @@
+// 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, 0);
+
+ 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, 0);
+
+ bfree(&b);
+ bfree(&in);
+ bfree(&out);
+ vfree(&lines);
+}
diff --git a/src/cmd/dist/buildruntime.c b/src/cmd/dist/buildruntime.c
new file mode 100644
index 000000000..a0c62010d
--- /dev/null
+++ b/src/cmd/dist/buildruntime.c
@@ -0,0 +1,346 @@
+// 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 pkg/runtime.
+ */
+
+// mkzversion writes zversion.go:
+//
+// package runtime
+// const defaultGoroot = <goroot>
+// const theVersion = <version>
+//
+void
+mkzversion(char *dir, char *file)
+{
+ Buf b, out;
+
+ 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", goroot_final, goversion));
+
+ writefile(&out, file, 0);
+
+ bfree(&b);
+ bfree(&out);
+}
+
+// mkzgoarch writes zgoarch_$GOARCH.go:
+//
+// package runtime
+// const theGoarch = <goarch>
+//
+void
+mkzgoarch(char *dir, char *file)
+{
+ Buf b, out;
+
+ binit(&b);
+ binit(&out);
+
+ bwritestr(&out, bprintf(&b,
+ "// auto generated by go tool dist\n"
+ "\n"
+ "package runtime\n"
+ "\n"
+ "const theGoarch = `%s`\n", goarch));
+
+ writefile(&out, file, 0);
+
+ bfree(&b);
+ bfree(&out);
+}
+
+// mkzgoos writes zgoos_$GOOS.go:
+//
+// package runtime
+// const theGoos = <goos>
+//
+void
+mkzgoos(char *dir, char *file)
+{
+ Buf b, out;
+
+ binit(&b);
+ binit(&out);
+
+ bwritestr(&out, bprintf(&b,
+ "// auto generated by go tool dist\n"
+ "\n"
+ "package runtime\n"
+ "\n"
+ "const theGoos = `%s`\n", goos));
+
+ writefile(&out, file, 0);
+
+ bfree(&b);
+ bfree(&out);
+}
+
+static struct {
+ char *goarch;
+ char *goos;
+ char *hdr;
+} zasmhdr[] = {
+ {"386", "windows",
+ "#define get_tls(r) MOVL 0x14(FS), r\n"
+ "#define g(r) 0(r)\n"
+ "#define m(r) 4(r)\n"
+ },
+ {"386", "plan9",
+ "#define get_tls(r) MOVL _tos(SB), r \n"
+ "#define g(r) -8(r)\n"
+ "#define m(r) -4(r)\n"
+ },
+ {"386", "linux",
+ "// On Linux systems, what we call 0(GS) and 4(GS) for g and m\n"
+ "// turn into %gs:-8 and %gs:-4 (using gcc syntax to denote\n"
+ "// what the machine sees as opposed to 8l input).\n"
+ "// 8l rewrites 0(GS) and 4(GS) into these.\n"
+ "//\n"
+ "// On Linux Xen, it is not allowed to use %gs:-8 and %gs:-4\n"
+ "// directly. Instead, we have to store %gs:0 into a temporary\n"
+ "// register and then use -8(%reg) and -4(%reg). This kind\n"
+ "// of addressing is correct even when not running Xen.\n"
+ "//\n"
+ "// 8l can rewrite MOVL 0(GS), CX into the appropriate pair\n"
+ "// of mov instructions, using CX as the intermediate register\n"
+ "// (safe because CX is about to be written to anyway).\n"
+ "// But 8l cannot handle other instructions, like storing into 0(GS),\n"
+ "// which is where these macros come into play.\n"
+ "// get_tls sets up the temporary and then g and r use it.\n"
+ "//\n"
+ "// The final wrinkle is that get_tls needs to read from %gs:0,\n"
+ "// but in 8l input it's called 8(GS), because 8l is going to\n"
+ "// subtract 8 from all the offsets, as described above.\n"
+ "#define get_tls(r) MOVL 8(GS), r\n"
+ "#define g(r) -8(r)\n"
+ "#define m(r) -4(r)\n"
+ },
+ {"386", "",
+ "#define get_tls(r)\n"
+ "#define g(r) 0(GS)\n"
+ "#define m(r) 4(GS)\n"
+ },
+
+ {"amd64", "windows",
+ "#define get_tls(r) MOVQ 0x28(GS), r\n"
+ "#define g(r) 0(r)\n"
+ "#define m(r) 8(r)\n"
+ },
+ {"amd64", "",
+ "// The offsets 0 and 8 are known to:\n"
+ "// ../../cmd/6l/pass.c:/D_GS\n"
+ "// cgo/gcc_linux_amd64.c:/^threadentry\n"
+ "// cgo/gcc_darwin_amd64.c:/^threadentry\n"
+ "//\n"
+ "#define get_tls(r)\n"
+ "#define g(r) 0(GS)\n"
+ "#define m(r) 8(GS)\n"
+ },
+
+ {"arm", "",
+ "#define g R10\n"
+ "#define m R9\n"
+ "#define LR R14\n"
+ },
+};
+
+// mkzasm writes zasm_$GOOS_$GOARCH.h,
+// which contains struct offsets for use by
+// assembly files. It also writes a copy to the work space
+// under the name zasm_GOOS_GOARCH.h (no expansion).
+//
+void
+mkzasm(char *dir, char *file)
+{
+ int i, n;
+ char *aggr, *p;
+ Buf in, b, out;
+ Vec argv, lines, fields;
+
+ binit(&in);
+ binit(&b);
+ binit(&out);
+ vinit(&argv);
+ vinit(&lines);
+ vinit(&fields);
+
+ bwritestr(&out, "// auto generated by go tool dist\n\n");
+ for(i=0; i<nelem(zasmhdr); i++) {
+ if(hasprefix(goarch, zasmhdr[i].goarch) && hasprefix(goos, zasmhdr[i].goos)) {
+ bwritestr(&out, zasmhdr[i].hdr);
+ goto ok;
+ }
+ }
+ fatal("unknown $GOOS/$GOARCH in mkzasm");
+ok:
+
+ // Run 6c -DGOOS_goos -DGOARCH_goarch -Iworkdir -a proc.c
+ // to get acid [sic] output.
+ vreset(&argv);
+ vadd(&argv, bpathf(&b, "%s/%sc", tooldir, gochar));
+ vadd(&argv, bprintf(&b, "-DGOOS_%s", goos));
+ vadd(&argv, bprintf(&b, "-DGOARCH_%s", goarch));
+ vadd(&argv, bprintf(&b, "-I%s", workdir));
+ vadd(&argv, "-a");
+ vadd(&argv, "proc.c");
+ runv(&in, dir, CheckExit, &argv);
+
+ // Convert input like
+ // aggr G
+ // {
+ // Gobuf 24 sched;
+ // 'Y' 48 stack0;
+ // }
+ // into output like
+ // #define g_sched 24
+ // #define g_stack0 48
+ //
+ aggr = nil;
+ splitlines(&lines, bstr(&in));
+ for(i=0; i<lines.len; i++) {
+ splitfields(&fields, lines.p[i]);
+ if(fields.len == 2 && streq(fields.p[0], "aggr")) {
+ if(streq(fields.p[1], "G"))
+ aggr = "g";
+ else if(streq(fields.p[1], "M"))
+ aggr = "m";
+ else if(streq(fields.p[1], "Gobuf"))
+ aggr = "gobuf";
+ else if(streq(fields.p[1], "WinCall"))
+ aggr = "wincall";
+ }
+ if(hasprefix(lines.p[i], "}"))
+ aggr = nil;
+ if(aggr && hasprefix(lines.p[i], "\t") && fields.len >= 2) {
+ n = fields.len;
+ p = fields.p[n-1];
+ if(p[xstrlen(p)-1] == ';')
+ p[xstrlen(p)-1] = '\0';
+ bwritestr(&out, bprintf(&b, "#define %s_%s %s\n", aggr, fields.p[n-1], fields.p[n-2]));
+ }
+ }
+
+ // Write both to file and to workdir/zasm_GOOS_GOARCH.h.
+ writefile(&out, file, 0);
+ writefile(&out, bprintf(&b, "%s/zasm_GOOS_GOARCH.h", workdir), 0);
+
+ bfree(&in);
+ bfree(&b);
+ bfree(&out);
+ vfree(&argv);
+ vfree(&lines);
+ vfree(&fields);
+}
+
+static char *runtimedefs[] = {
+ "proc.c",
+ "iface.c",
+ "hashmap.c",
+ "chan.c",
+};
+
+// mkzruntimedefs writes zruntime_defs_$GOOS_$GOARCH.h,
+// which contains Go struct definitions equivalent to the C ones.
+// Mostly we just write the output of 6c -q to the file.
+// However, we run it on multiple files, so we have to delete
+// the duplicated definitions, and we don't care about the funcs
+// and consts, so we delete those too.
+//
+void
+mkzruntimedefs(char *dir, char *file)
+{
+ int i, skip;
+ char *p;
+ Buf in, b, out;
+ Vec argv, lines, fields, seen;
+
+ binit(&in);
+ binit(&b);
+ binit(&out);
+ vinit(&argv);
+ vinit(&lines);
+ vinit(&fields);
+ vinit(&seen);
+
+ bwritestr(&out, "// auto generated by go tool dist\n"
+ "\n"
+ "package runtime\n"
+ "import \"unsafe\"\n"
+ "var _ unsafe.Pointer\n"
+ "\n"
+ );
+
+
+ // Run 6c -DGOOS_goos -DGOARCH_goarch -Iworkdir -q
+ // on each of the runtimedefs C files.
+ vadd(&argv, bpathf(&b, "%s/%sc", tooldir, gochar));
+ vadd(&argv, bprintf(&b, "-DGOOS_%s", goos));
+ vadd(&argv, bprintf(&b, "-DGOARCH_%s", goarch));
+ vadd(&argv, bprintf(&b, "-I%s", workdir));
+ vadd(&argv, "-q");
+ vadd(&argv, "");
+ p = argv.p[argv.len-1];
+ for(i=0; i<nelem(runtimedefs); i++) {
+ argv.p[argv.len-1] = runtimedefs[i];
+ runv(&b, dir, CheckExit, &argv);
+ bwriteb(&in, &b);
+ }
+ argv.p[argv.len-1] = p;
+
+ // Process the aggregate output.
+ skip = 0;
+ splitlines(&lines, bstr(&in));
+ for(i=0; i<lines.len; i++) {
+ p = lines.p[i];
+ // Drop comment, func, and const lines.
+ if(hasprefix(p, "//") || hasprefix(p, "const") || hasprefix(p, "func"))
+ continue;
+
+ // Note beginning of type or var decl, which can be multiline.
+ // Remove duplicates. The linear check of seen here makes the
+ // whole processing quadratic in aggregate, but there are only
+ // about 100 declarations, so this is okay (and simple).
+ if(hasprefix(p, "type ") || hasprefix(p, "var ")) {
+ splitfields(&fields, p);
+ if(fields.len < 2)
+ continue;
+ if(find(fields.p[1], seen.p, seen.len) >= 0) {
+ if(streq(fields.p[fields.len-1], "{"))
+ skip = 1; // skip until }
+ continue;
+ }
+ vadd(&seen, fields.p[1]);
+ }
+ if(skip) {
+ if(hasprefix(p, "}"))
+ skip = 0;
+ continue;
+ }
+
+ bwritestr(&out, p);
+ }
+
+ writefile(&out, file, 0);
+
+ bfree(&in);
+ bfree(&b);
+ bfree(&out);
+ vfree(&argv);
+ vfree(&lines);
+ vfree(&fields);
+ vfree(&seen);
+}
diff --git a/src/cmd/dist/goc2c.c b/src/cmd/dist/goc2c.c
new file mode 100644
index 000000000..22f72f8b5
--- /dev/null
+++ b/src/cmd/dist/goc2c.c
@@ -0,0 +1,735 @@
+// 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.
+
+#include "a.h"
+
+/*
+ * Translate a .goc file into a .c file. A .goc file is a combination
+ * of a limited form of Go with C.
+ */
+
+/*
+ package PACKAGENAME
+ {# line}
+ func NAME([NAME TYPE { , NAME TYPE }]) [(NAME TYPE { , NAME TYPE })] \{
+ C code with proper brace nesting
+ \}
+*/
+
+/*
+ * We generate C code which implements the function such that it can
+ * be called from Go and executes the C code.
+ */
+
+static char *input;
+static Buf *output;
+#define EOF -1
+
+static int
+xgetchar(void)
+{
+ int c;
+
+ c = *input;
+ if(c == 0)
+ return EOF;
+ input++;
+ return c;
+}
+
+static void
+xungetc(void)
+{
+ input--;
+}
+
+static void
+xputchar(char c)
+{
+ bwrite(output, &c, 1);
+}
+
+static int
+xisspace(int c)
+{
+ return c == ' ' || c == '\t' || c == '\r' || c == '\n';
+}
+
+/* Whether we're emitting for gcc */
+static int gcc;
+
+/* File and line number */
+static const char *file;
+static unsigned int lineno = 1;
+
+/* List of names and types. */
+struct params {
+ struct params *next;
+ char *name;
+ char *type;
+};
+
+/* index into type_table */
+enum {
+ Bool,
+ Float,
+ Int,
+ Uint,
+ Uintptr,
+ String,
+ Slice,
+ Eface,
+};
+
+static struct {
+ char *name;
+ int size;
+} type_table[] = {
+ /* variable sized first, for easy replacement */
+ /* order matches enum above */
+ /* default is 32-bit architecture sizes */
+ {"bool", 1},
+ {"float", 4},
+ {"int", 4},
+ {"uint", 4},
+ {"uintptr", 4},
+ {"String", 8},
+ {"Slice", 12},
+ {"Eface", 8},
+
+ /* fixed size */
+ {"float32", 4},
+ {"float64", 8},
+ {"byte", 1},
+ {"int8", 1},
+ {"uint8", 1},
+ {"int16", 2},
+ {"uint16", 2},
+ {"int32", 4},
+ {"uint32", 4},
+ {"int64", 8},
+ {"uint64", 8},
+
+ {nil},
+};
+
+/* Fixed structure alignment (non-gcc only) */
+int structround = 4;
+
+/* Unexpected EOF. */
+static void
+bad_eof(void)
+{
+ fatal("%s:%ud: unexpected EOF\n", file, lineno);
+}
+
+/* Free a list of parameters. */
+static void
+free_params(struct params *p)
+{
+ while (p != nil) {
+ struct params *next;
+
+ next = p->next;
+ xfree(p->name);
+ xfree(p->type);
+ xfree(p);
+ p = next;
+ }
+}
+
+/* Read a character, tracking lineno. */
+static int
+getchar_update_lineno(void)
+{
+ int c;
+
+ c = xgetchar();
+ if (c == '\n')
+ ++lineno;
+ return c;
+}
+
+/* Read a character, giving an error on EOF, tracking lineno. */
+static int
+getchar_no_eof(void)
+{
+ int c;
+
+ c = getchar_update_lineno();
+ if (c == EOF)
+ bad_eof();
+ return c;
+}
+
+/* Read a character, skipping comments. */
+static int
+getchar_skipping_comments(void)
+{
+ int c;
+
+ while (1) {
+ c = getchar_update_lineno();
+ if (c != '/')
+ return c;
+
+ c = xgetchar();
+ if (c == '/') {
+ do {
+ c = getchar_update_lineno();
+ } while (c != EOF && c != '\n');
+ return c;
+ } else if (c == '*') {
+ while (1) {
+ c = getchar_update_lineno();
+ if (c == EOF)
+ return EOF;
+ if (c == '*') {
+ do {
+ c = getchar_update_lineno();
+ } while (c == '*');
+ if (c == '/')
+ break;
+ }
+ }
+ } else {
+ xungetc();
+ return '/';
+ }
+ }
+}
+
+/*
+ * Read and return a token. Tokens are string or character literals
+ * or else delimited by whitespace or by [(),{}].
+ * The latter are all returned as single characters.
+ */
+static char *
+read_token(void)
+{
+ int c, q;
+ char *buf;
+ unsigned int alc, off;
+ char* delims = "(),{}";
+
+ while (1) {
+ c = getchar_skipping_comments();
+ if (c == EOF)
+ return nil;
+ if (!xisspace(c))
+ break;
+ }
+ alc = 16;
+ buf = xmalloc(alc + 1);
+ off = 0;
+ if(c == '"' || c == '\'') {
+ q = c;
+ buf[off] = c;
+ ++off;
+ while (1) {
+ if (off+2 >= alc) { // room for c and maybe next char
+ alc *= 2;
+ buf = xrealloc(buf, alc + 1);
+ }
+ c = getchar_no_eof();
+ buf[off] = c;
+ ++off;
+ if(c == q)
+ break;
+ if(c == '\\') {
+ buf[off] = getchar_no_eof();
+ ++off;
+ }
+ }
+ } else if (xstrrchr(delims, c) != nil) {
+ buf[off] = c;
+ ++off;
+ } else {
+ while (1) {
+ if (off >= alc) {
+ alc *= 2;
+ buf = xrealloc(buf, alc + 1);
+ }
+ buf[off] = c;
+ ++off;
+ c = getchar_skipping_comments();
+ if (c == EOF)
+ break;
+ if (xisspace(c) || xstrrchr(delims, c) != nil) {
+ if (c == '\n')
+ lineno--;
+ xungetc();
+ break;
+ }
+ }
+ }
+ buf[off] = '\0';
+ return buf;
+}
+
+/* Read a token, giving an error on EOF. */
+static char *
+read_token_no_eof(void)
+{
+ char *token = read_token();
+ if (token == nil)
+ bad_eof();
+ return token;
+}
+
+/* Read the package clause, and return the package name. */
+static char *
+read_package(void)
+{
+ char *token;
+
+ token = read_token_no_eof();
+ if (token == nil)
+ fatal("%s:%ud: no token\n", file, lineno);
+ if (!streq(token, "package")) {
+ fatal("%s:%ud: expected \"package\", got \"%s\"\n",
+ file, lineno, token);
+ }
+ return read_token_no_eof();
+}
+
+/* Read and copy preprocessor lines. */
+static void
+read_preprocessor_lines(void)
+{
+ while (1) {
+ int c;
+
+ do {
+ c = getchar_skipping_comments();
+ } while (xisspace(c));
+ if (c != '#') {
+ xungetc();
+ break;
+ }
+ xputchar(c);
+ do {
+ c = getchar_update_lineno();
+ xputchar(c);
+ } while (c != '\n');
+ }
+}
+
+/*
+ * Read a type in Go syntax and return a type in C syntax. We only
+ * permit basic types and pointers.
+ */
+static char *
+read_type(void)
+{
+ char *p, *op, *q;
+ int pointer_count;
+ unsigned int len;
+
+ p = read_token_no_eof();
+ if (*p != '*')
+ return p;
+ op = p;
+ pointer_count = 0;
+ while (*p == '*') {
+ ++pointer_count;
+ ++p;
+ }
+ len = xstrlen(p);
+ q = xmalloc(len + pointer_count + 1);
+ xmemmove(q, p, len);
+ while (pointer_count > 0) {
+ q[len] = '*';
+ ++len;
+ --pointer_count;
+ }
+ q[len] = '\0';
+ xfree(op);
+ return q;
+}
+
+/* Return the size of the given type. */
+static int
+type_size(char *p)
+{
+ int i;
+
+ if(p[xstrlen(p)-1] == '*')
+ return type_table[Uintptr].size;
+
+ for(i=0; type_table[i].name; i++)
+ if(streq(type_table[i].name, p))
+ return type_table[i].size;
+ fatal("%s:%ud: unknown type %s\n", file, lineno, p);
+ return 0;
+}
+
+/*
+ * Read a list of parameters. Each parameter is a name and a type.
+ * The list ends with a ')'. We have already read the '('.
+ */
+static struct params *
+read_params(int *poffset)
+{
+ char *token;
+ struct params *ret, **pp, *p;
+ int offset, size, rnd;
+
+ ret = nil;
+ pp = &ret;
+ token = read_token_no_eof();
+ offset = 0;
+ if (!streq(token, ")")) {
+ while (1) {
+ p = xmalloc(sizeof(struct params));
+ p->name = token;
+ p->type = read_type();
+ p->next = nil;
+ *pp = p;
+ pp = &p->next;
+
+ size = type_size(p->type);
+ rnd = size;
+ if(rnd > structround)
+ rnd = structround;
+ if(offset%rnd)
+ offset += rnd - offset%rnd;
+ offset += size;
+
+ token = read_token_no_eof();
+ if (!streq(token, ","))
+ break;
+ token = read_token_no_eof();
+ }
+ }
+ if (!streq(token, ")")) {
+ fatal("%s:%ud: expected '('\n",
+ file, lineno);
+ }
+ if (poffset != nil)
+ *poffset = offset;
+ return ret;
+}
+
+/*
+ * Read a function header. This reads up to and including the initial
+ * '{' character. Returns 1 if it read a header, 0 at EOF.
+ */
+static int
+read_func_header(char **name, struct params **params, int *paramwid, struct params **rets)
+{
+ int lastline;
+ char *token;
+
+ lastline = -1;
+ while (1) {
+ token = read_token();
+ if (token == nil)
+ return 0;
+ if (streq(token, "func")) {
+ if(lastline != -1)
+ bwritef(output, "\n");
+ break;
+ }
+ if (lastline != lineno) {
+ if (lastline == lineno-1)
+ bwritef(output, "\n");
+ else
+ bwritef(output, "\n#line %d \"%s\"\n", lineno, file);
+ lastline = lineno;
+ }
+ bwritef(output, "%s ", token);
+ }
+
+ *name = read_token_no_eof();
+
+ token = read_token();
+ if (token == nil || !streq(token, "(")) {
+ fatal("%s:%ud: expected \"(\"\n",
+ file, lineno);
+ }
+ *params = read_params(paramwid);
+
+ token = read_token();
+ if (token == nil || !streq(token, "("))
+ *rets = nil;
+ else {
+ *rets = read_params(nil);
+ token = read_token();
+ }
+ if (token == nil || !streq(token, "{")) {
+ fatal("%s:%ud: expected \"{\"\n",
+ file, lineno);
+ }
+ return 1;
+}
+
+/* Write out parameters. */
+static void
+write_params(struct params *params, int *first)
+{
+ struct params *p;
+
+ for (p = params; p != nil; p = p->next) {
+ if (*first)
+ *first = 0;
+ else
+ bwritef(output, ", ");
+ bwritef(output, "%s %s", p->type, p->name);
+ }
+}
+
+/* Write a 6g function header. */
+static void
+write_6g_func_header(char *package, char *name, struct params *params,
+ int paramwid, struct params *rets)
+{
+ int first, n;
+
+ bwritef(output, "void\n%s·%s(", package, name);
+ first = 1;
+ write_params(params, &first);
+
+ /* insert padding to align output struct */
+ if(rets != nil && paramwid%structround != 0) {
+ n = structround - paramwid%structround;
+ if(n & 1)
+ bwritef(output, ", uint8");
+ if(n & 2)
+ bwritef(output, ", uint16");
+ if(n & 4)
+ bwritef(output, ", uint32");
+ }
+
+ write_params(rets, &first);
+ bwritef(output, ")\n{\n");
+}
+
+/* Write a 6g function trailer. */
+static void
+write_6g_func_trailer(struct params *rets)
+{
+ struct params *p;
+
+ for (p = rets; p != nil; p = p->next)
+ bwritef(output, "\tFLUSH(&%s);\n", p->name);
+ bwritef(output, "}\n");
+}
+
+/* Define the gcc function return type if necessary. */
+static void
+define_gcc_return_type(char *package, char *name, struct params *rets)
+{
+ struct params *p;
+
+ if (rets == nil || rets->next == nil)
+ return;
+ bwritef(output, "struct %s_%s_ret {\n", package, name);
+ for (p = rets; p != nil; p = p->next)
+ bwritef(output, " %s %s;\n", p->type, p->name);
+ bwritef(output, "};\n");
+}
+
+/* Write out the gcc function return type. */
+static void
+write_gcc_return_type(char *package, char *name, struct params *rets)
+{
+ if (rets == nil)
+ bwritef(output, "void");
+ else if (rets->next == nil)
+ bwritef(output, "%s", rets->type);
+ else
+ bwritef(output, "struct %s_%s_ret", package, name);
+}
+
+/* Write out a gcc function header. */
+static void
+write_gcc_func_header(char *package, char *name, struct params *params,
+ struct params *rets)
+{
+ int first;
+ struct params *p;
+
+ define_gcc_return_type(package, name, rets);
+ write_gcc_return_type(package, name, rets);
+ bwritef(output, " %s_%s(", package, name);
+ first = 1;
+ write_params(params, &first);
+ bwritef(output, ") asm (\"%s.%s\");\n", package, name);
+ write_gcc_return_type(package, name, rets);
+ bwritef(output, " %s_%s(", package, name);
+ first = 1;
+ write_params(params, &first);
+ bwritef(output, ")\n{\n");
+ for (p = rets; p != nil; p = p->next)
+ bwritef(output, " %s %s;\n", p->type, p->name);
+}
+
+/* Write out a gcc function trailer. */
+static void
+write_gcc_func_trailer(char *package, char *name, struct params *rets)
+{
+ if (rets == nil)
+ ;
+ else if (rets->next == nil)
+ bwritef(output, "return %s;\n", rets->name);
+ else {
+ struct params *p;
+
+ bwritef(output, " {\n struct %s_%s_ret __ret;\n", package, name);
+ for (p = rets; p != nil; p = p->next)
+ bwritef(output, " __ret.%s = %s;\n", p->name, p->name);
+ bwritef(output, " return __ret;\n }\n");
+ }
+ bwritef(output, "}\n");
+}
+
+/* Write out a function header. */
+static void
+write_func_header(char *package, char *name,
+ struct params *params, int paramwid,
+ struct params *rets)
+{
+ if (gcc)
+ write_gcc_func_header(package, name, params, rets);
+ else
+ write_6g_func_header(package, name, params, paramwid, rets);
+ bwritef(output, "#line %d \"%s\"\n", lineno, file);
+}
+
+/* Write out a function trailer. */
+static void
+write_func_trailer(char *package, char *name,
+ struct params *rets)
+{
+ if (gcc)
+ write_gcc_func_trailer(package, name, rets);
+ else
+ write_6g_func_trailer(rets);
+}
+
+/*
+ * Read and write the body of the function, ending in an unnested }
+ * (which is read but not written).
+ */
+static void
+copy_body(void)
+{
+ int nesting = 0;
+ while (1) {
+ int c;
+
+ c = getchar_no_eof();
+ if (c == '}' && nesting == 0)
+ return;
+ xputchar(c);
+ switch (c) {
+ default:
+ break;
+ case '{':
+ ++nesting;
+ break;
+ case '}':
+ --nesting;
+ break;
+ case '/':
+ c = getchar_update_lineno();
+ xputchar(c);
+ if (c == '/') {
+ do {
+ c = getchar_no_eof();
+ xputchar(c);
+ } while (c != '\n');
+ } else if (c == '*') {
+ while (1) {
+ c = getchar_no_eof();
+ xputchar(c);
+ if (c == '*') {
+ do {
+ c = getchar_no_eof();
+ xputchar(c);
+ } while (c == '*');
+ if (c == '/')
+ break;
+ }
+ }
+ }
+ break;
+ case '"':
+ case '\'':
+ {
+ int delim = c;
+ do {
+ c = getchar_no_eof();
+ xputchar(c);
+ if (c == '\\') {
+ c = getchar_no_eof();
+ xputchar(c);
+ c = '\0';
+ }
+ } while (c != delim);
+ }
+ break;
+ }
+ }
+}
+
+/* Process the entire file. */
+static void
+process_file(void)
+{
+ char *package, *name;
+ struct params *params, *rets;
+ int paramwid;
+
+ package = read_package();
+ read_preprocessor_lines();
+ while (read_func_header(&name, &params, &paramwid, &rets)) {
+ write_func_header(package, name, params, paramwid, rets);
+ copy_body();
+ write_func_trailer(package, name, rets);
+ xfree(name);
+ free_params(params);
+ free_params(rets);
+ }
+ xfree(package);
+}
+
+void
+goc2c(char *goc, char *c)
+{
+ Buf in, out;
+
+ binit(&in);
+ binit(&out);
+
+ file = goc;
+ readfile(&in, goc);
+
+ // TODO: set gcc=1 when using gcc
+
+ if(!gcc) {
+ if(streq(goarch, "amd64")) {
+ type_table[Uintptr].size = 8;
+ type_table[String].size = 16;
+ type_table[Slice].size = 8+4+4;
+ type_table[Eface].size = 8+8;
+ structround = 8;
+ } else {
+ type_table[Uintptr].size = 4;
+ type_table[String].size = 8;
+ type_table[Slice].size = 16;
+ type_table[Eface].size = 4+4;
+ structround = 4;
+ }
+ }
+
+ bprintf(&out, "// auto generated by go tool dist\n// goos=%s goarch=%s\n\n", goos, goarch);
+ input = bstr(&in);
+ output = &out;
+
+ process_file();
+
+ writefile(&out, c, 0);
+}
diff --git a/src/cmd/dist/main.c b/src/cmd/dist/main.c
new file mode 100644
index 000000000..72a7579d1
--- /dev/null
+++ b/src/cmd/dist/main.c
@@ -0,0 +1,41 @@
+// 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"
+
+int vflag;
+char *argv0;
+
+// cmdtab records the available commands.
+static struct {
+ char *name;
+ void (*f)(int, char**);
+} cmdtab[] = {
+ {"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;
+ }
+ }
+
+ xprintf("unknown command %s\n", argv[1]);
+ usage();
+}
diff --git a/src/cmd/dist/unix.c b/src/cmd/dist/unix.c
new file mode 100644
index 000000000..e6d82e14e
--- /dev/null
+++ b/src/cmd/dist/unix.c
@@ -0,0 +1,720 @@
+// 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);
+}
+
+// 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);
+ }
+ bwritestr(&cmd, q);
+ }
+ if(vflag > 1)
+ xprintf("%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]);
+ }
+ 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, "%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);
+}
+
+// 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. 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);
+}
+
+// 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)
+{
+ if(vflag > 2)
+ xprintf("rm %s\n", 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));
+ }
+ if(vflag > 2)
+ xprintf("rm %s\n", p);
+ rmdir(p);
+ } else {
+ if(vflag > 2)
+ xprintf("rm %s\n", p);
+ 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;
+
+ 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 && 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)
+{
+ Buf b;
+ struct utsname u;
+
+ setvbuf(stdout, nil, _IOLBF, 0);
+ setvbuf(stderr, nil, _IOLBF, 0);
+
+ 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(__FreeBSD__)
+ gohostos = "freebsd";
+#elif defined(__OpenBSD__)
+ gohostos = "openbsd";
+#elif defined(__NetBSD__)
+ gohostos = "netbsd";
+#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
+ fatal("unknown architecture: %s", u.machine);
+ }
+
+ if(strcmp(gohostarch, "arm") == 0)
+ maxnbg = 1;
+
+ 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__
diff --git a/src/cmd/dist/windows.c b/src/cmd/dist/windows.c
new file mode 100644
index 000000000..557e4b003
--- /dev/null
+++ b/src/cmd/dist/windows.c
@@ -0,0 +1,910 @@
+// 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)
+{
+ Rune *r, *w;
+
+ 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)
+{
+ 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);
+}
+
+void
+bwritef(Buf *b, char *fmt, ...)
+{
+ va_list arg;
+ char buf[4096];
+
+ // no reset
+ va_start(arg, fmt);
+ vsnprintf(buf, sizeof buf, fmt, arg);
+ va_end(arg);
+ bwritestr(b, buf);
+}
+
+// bpathf is like bprintf but replaces / with \ in the result,
+// to make it a canonical windows file path.
+char*
+bpathf(Buf *b, char *fmt, ...)
+{
+ int i;
+ va_list arg;
+ char buf[4096];
+
+ breset(b);
+ va_start(arg, fmt);
+ vsnprintf(buf, sizeof buf, fmt, arg);
+ va_end(arg);
+ bwritestr(b, buf);
+
+ for(i=0; i<b->len; i++)
+ if(b->p[i] == '/')
+ b->p[i] = '\\';
+
+ 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)) {
+ // Happens for pipe reads.
+ break;
+ }
+ if(n == 0)
+ break;
+ b->len += n;
+ }
+}
+
+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);
+}
+
+static void genrun(Buf*, char*, int, Vec*, int);
+
+void
+runv(Buf *b, char *dir, int mode, Vec *argv)
+{
+ genrun(b, dir, mode, argv, 1);
+}
+
+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 {
+ PROCESS_INFORMATION pi;
+ int mode;
+ char *cmd;
+} bg[MAXBG];
+
+static int nbg;
+
+static void bgwait1(void);
+
+static void
+genrun(Buf *b, char *dir, int mode, Vec *argv, int wait)
+{
+ int i, j, nslash;
+ Buf cmd;
+ char *q;
+ Rune *rcmd, *rexe, *rdir;
+ STARTUPINFOW si;
+ PROCESS_INFORMATION pi;
+ HANDLE p[2];
+
+ while(nbg >= nelem(bg))
+ bgwait1();
+
+ binit(&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, "\\\\") || hassuffix(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);
+ }
+ }
+ if(vflag > 1)
+ xprintf("%s\n", bstr(&cmd));
+
+ 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 {
+ SECURITY_ATTRIBUTES seci;
+
+ memset(&seci, 0, sizeof seci);
+ seci.nLength = sizeof seci;
+ seci.bInheritHandle = 1;
+ breset(b);
+ if(!CreatePipe(&p[0], &p[1], &seci, 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]);
+ }
+
+ if(nbg < 0)
+ fatal("bad bookkeeping");
+ bg[nbg].pi = pi;
+ bg[nbg].mode = mode;
+ bg[nbg].cmd = btake(&cmd);
+ nbg++;
+
+ if(wait)
+ bgwait();
+
+ bfree(&cmd);
+}
+
+// closes the background job for bgwait1
+static void
+bgwaitclose(int i)
+{
+ if(i < 0 || i >= nbg)
+ return;
+
+ CloseHandle(bg[i].pi.hProcess);
+ CloseHandle(bg[i].pi.hThread);
+
+ bg[i] = bg[--nbg];
+}
+
+// bgwait1 waits for a single background job
+static void
+bgwait1(void)
+{
+ int i, mode;
+ char *cmd;
+ HANDLE bgh[MAXBG];
+ DWORD code;
+
+ if(nbg == 0)
+ fatal("bgwait1: nothing left");
+
+ for(i=0; i<nbg; i++)
+ bgh[i] = bg[i].pi.hProcess;
+ i = WaitForMultipleObjects(nbg, bgh, FALSE, INFINITE);
+ if(i < 0 || i >= nbg)
+ fatal("WaitForMultipleObjects: %s", errstr());
+
+ cmd = bg[i].cmd;
+ mode = bg[i].mode;
+ if(!GetExitCodeProcess(bg[i].pi.hProcess, &code)) {
+ bgwaitclose(i);
+ fatal("GetExitCodeProcess: %s", errstr());
+ return;
+ }
+
+ if(mode==CheckExit && code != 0) {
+ bgwaitclose(i);
+ fatal("FAILED: %s", cmd);
+ return;
+ }
+
+ bgwaitclose(i);
+}
+
+void
+bgwait(void)
+{
+ while(nbg > 0)
+ bgwait1();
+}
+
+// rgetwd returns a rune string form of the current directory's path.
+static Rune*
+rgetwd(void)
+{
+ int n;
+ Rune *r;
+
+ n = GetCurrentDirectoryW(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)
+{
+ 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)
+{
+ DWORD attr;
+ Rune *r;
+
+ torune(&r, p);
+ attr = GetFileAttributesW(r);
+ xfree(r);
+ return attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY);
+}
+
+bool
+isfile(char *p)
+{
+ DWORD attr;
+ Rune *r;
+
+ torune(&r, p);
+ attr = GetFileAttributesW(r);
+ xfree(r);
+ return attr != INVALID_FILE_ATTRIBUTES && !(attr & FILE_ATTRIBUTE_DIRECTORY);
+}
+
+Time
+mtime(char *p)
+{
+ HANDLE h;
+ WIN32_FIND_DATAW data;
+ Rune *r;
+ FILETIME *ft;
+
+ torune(&r, p);
+ h = FindFirstFileW(r, &data);
+ xfree(r);
+ if(h == INVALID_HANDLE_VALUE)
+ return 0;
+ FindClose(h);
+ ft = &data.ftLastWriteTime;
+ return (Time)ft->dwLowDateTime + ((Time)ft->dwHighDateTime<<32);
+}
+
+bool
+isabs(char *p)
+{
+ // c:/ or c:\ at beginning
+ if(('A' <= p[0] && p[0] <= 'Z') || ('a' <= p[0] && p[0] <= 'z'))
+ return p[1] == ':' && (p[2] == '/' || p[2] == '\\');
+ // / or \ at beginning
+ return p[0] == '/' || p[0] == '\\';
+}
+
+void
+readfile(Buf *b, char *file)
+{
+ HANDLE h;
+ Rune *r;
+
+ if(vflag > 2)
+ xprintf("read %s\n", file);
+ 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, int exec)
+{
+ HANDLE h;
+ Rune *r;
+ DWORD n;
+
+ USED(exec);
+
+ if(vflag > 2)
+ xprintf("write %s\n", file);
+ 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);
+
+ xprintf("go tool dist: %s\n", buf1);
+
+ bgwait();
+ 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, 0, 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)
+{
+ ExitProcess(n);
+}
+
+void
+xatexit(void (*f)(void))
+{
+ atexit(f);
+}
+
+void
+xprintf(char *fmt, ...)
+{
+ va_list arg;
+ char *p;
+ DWORD n, w;
+
+ va_start(arg, fmt);
+ n = vsnprintf(NULL, 0, fmt, arg);
+ p = xmalloc(n+1);
+ vsnprintf(p, n+1, fmt, arg);
+ va_end(arg);
+ w = 0;
+ WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), p, n, &w, 0);
+ xfree(p);
+}
+
+int
+main(int argc, char **argv)
+{
+ SYSTEM_INFO si;
+
+ setvbuf(stdout, nil, _IOLBF, 0);
+ setvbuf(stderr, nil, _IOLBF, 0);
+
+ slash = "\\";
+ gohostos = "windows";
+
+ GetSystemInfo(&si);
+ switch(si.wProcessorArchitecture) {
+ case PROCESSOR_ARCHITECTURE_AMD64:
+ gohostarch = "amd64";
+ break;
+ case PROCESSOR_ARCHITECTURE_INTEL:
+ gohostarch = "386";
+ break;
+ default:
+ fatal("unknown processor architecture");
+ }
+
+ 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__