diff options
Diffstat (limited to 'src/cmd/dist')
-rw-r--r-- | src/cmd/dist/README | 45 | ||||
-rw-r--r-- | src/cmd/dist/a.h | 149 | ||||
-rw-r--r-- | src/cmd/dist/arg.h | 50 | ||||
-rw-r--r-- | src/cmd/dist/buf.c | 279 | ||||
-rw-r--r-- | src/cmd/dist/build.c | 1530 | ||||
-rw-r--r-- | src/cmd/dist/buildgc.c | 106 | ||||
-rw-r--r-- | src/cmd/dist/buildruntime.c | 346 | ||||
-rw-r--r-- | src/cmd/dist/goc2c.c | 735 | ||||
-rw-r--r-- | src/cmd/dist/main.c | 41 | ||||
-rw-r--r-- | src/cmd/dist/unix.c | 720 | ||||
-rw-r--r-- | src/cmd/dist/windows.c | 910 |
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, ¶ms, ¶mwid, &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__ |