// 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);
extern char *default_goroot;
extern char *goarch;
extern char *gobin;
+extern char *gochar;
extern char *gohostarch;
extern char *gohostos;
extern char *goos;
extern char *goroot;
+extern char *goversion;
extern char *workdir;
extern char *slash;
+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)
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*);
void xatexit(void (*f)(void));
void xsetenv(char*, char*);
int xstrcmp(char*, char*);
char* xstrdup(char *p);
-int xstreq(char*, char*);
int xstrlen(char*);
char* xstrrchr(char*, int);
char* xstrstr(char*, char*);
--- /dev/null
+/*
+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
+
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)
+ bgrow(b, ny-nx);
+ 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.
void
splitlines(Vec *v, char *p)
{
- int i, c;
+ int i;
char *start;
vreset(v);
// license that can be found in the LICENSE file.
#include "a.h"
+#include "arg.h"
/*
* Initialization for any invocation.
char *workdir;
char *gochar;
char *goroot_final;
-char *goversion = "go1"; // TODO: Read correct version
+char *goversion;
char *slash; // / for unix, \ for windows
char *default_goroot;
-static void fixslash(Buf*);
static bool shouldbuild(char*, char*);
static void copy(char*, char*);
+static char *findgoversion(void);
// The known architecture letters.
static char *gochars = "568";
static void rmworkdir(void);
// find reports the first index of p in l[0:n], or else -1.
-static int
+int
find(char *p, char **l, int n)
{
int i;
if(find(goos, okgoos, nelem(okgoos)) < 0)
fatal("unknown $GOOS %s", goos);
- p = bprintf(&b, "%s/include/u.h", goroot);
- fixslash(&b);
+ p = bpathf(&b, "%s/include/u.h", goroot);
if(!isfile(p)) {
fatal("$GOROOT is not set correctly or not exported\n"
"\tGOROOT=%s\n"
"\t%s does not exist", goroot, p);
}
-
+
xgetenv(&b, "GOHOSTARCH");
if(b.len > 0)
gohostarch = btake(&b);
xsetenv("LANG", "C");
xsetenv("LANGUAGE", "en_US.UTF8");
+ goversion = findgoversion();
+
workdir = xworkdir();
xatexit(rmworkdir);
static void
rmworkdir(void)
{
- xprintf("rm -rf %s\n", workdir);
+ 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);
+ 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), "--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));
+
+done:
+ p = btake(&b);
+
+
+ bfree(&b);
+ bfree(&path);
+ bfree(&bmore);
+ bfree(&branch);
+ vfree(&tags);
+
+ return p;
+}
+
/*
* Initial tree setup.
*/
binit(&b);
- run(&b, nil, 0, "ld", "--version", nil);
- if(contains(bstr(&b), "gold") && contains(bstr(&b), " 2.20")) {
- fatal("Your system has gold 2.20 installed.\n"
- "This version is shipped by Ubuntu even though\n"
- "it is known not to work on Ubuntu.\n"
- "Binaries built with this linker are likely to fail in mysterious ways.\n"
- "\n"
- "Run sudo apt-get remove binutils-gold.");
- }
-
// Create tool directory.
- p = bprintf(&b, "%s/bin", goroot);
- fixslash(&b);
+ p = bpathf(&b, "%s/bin", goroot);
if(!isdir(p))
xmkdir(p);
- p = bprintf(&b, "%s/bin/go-tool", goroot);
- fixslash(&b);
+ p = bpathf(&b, "%s/bin/tool", goroot);
if(!isdir(p))
xmkdir(p);
// Create package directory.
- p = bprintf(&b, "%s/pkg", goroot);
- fixslash(&b);
+ p = bpathf(&b, "%s/pkg", goroot);
if(!isdir(p))
xmkdir(p);
- p = bprintf(&b, "%s/pkg/%s_%s", goroot, goos, goarch);
- fixslash(&b);
+ p = bpathf(&b, "%s/pkg/%s_%s", goroot, goos, goarch);
+ xremoveall(p);
+ xmkdir(p);
+
+ // Create object directory.
+ // We keep it in pkg/ so that all the generated binaries
+ // are in one tree.
+ p = bpathf(&b, "%s/pkg/obj", goroot);
xremoveall(p);
xmkdir(p);
"$GOROOT/include/libc.h",
"fmt/*",
"utf/*",
- "-utf/mkrunetype",
- "-utf\\mkrunetype",
- "-utf/runetypebody",
- "-utf\\runetypebody",
}},
{"libbio", {
"$GOROOT/include/u.h",
"../cc/pgen.c",
"../cc/pswt.c",
"../5l/enam.c",
- "$GOROOT/lib/libcc.a",
+ "$GOROOT/pkg/obj/libcc.a",
}},
{"cmd/6c", {
"../cc/pgen.c",
"../cc/pswt.c",
"../6l/enam.c",
- "$GOROOT/lib/libcc.a",
+ "$GOROOT/pkg/obj/libcc.a",
}},
{"cmd/8c", {
"../cc/pgen.c",
"../cc/pswt.c",
"../8l/enam.c",
- "$GOROOT/lib/libcc.a",
+ "$GOROOT/pkg/obj/libcc.a",
}},
{"cmd/5g", {
"../gc/cplx.c",
"../gc/pgen.c",
"../5l/enam.c",
- "$GOROOT/lib/libgc.a",
+ "$GOROOT/pkg/obj/libgc.a",
}},
{"cmd/6g", {
"../gc/cplx.c",
"../gc/pgen.c",
"../6l/enam.c",
- "$GOROOT/lib/libgc.a",
+ "$GOROOT/pkg/obj/libgc.a",
}},
{"cmd/8g", {
"../gc/cplx.c",
"../gc/pgen.c",
"../8l/enam.c",
- "$GOROOT/lib/libgc.a",
+ "$GOROOT/pkg/obj/libgc.a",
}},
{"cmd/5l", {
"../ld/*",
"enam.c",
}},
{"cmd/", {
- "$GOROOT/lib/libmach.a",
- "$GOROOT/lib/libbio.a",
- "$GOROOT/lib/lib9.a",
+ "$GOROOT/pkg/obj/libmach.a",
+ "$GOROOT/pkg/obj/libbio.a",
+ "$GOROOT/pkg/obj/lib9.a",
+ }},
+ {"pkg/runtime", {
+ "zasm_$GOOS_$GOARCH.h",
+ "zgoarch_$GOARCH.go",
+ "zgoos_$GOOS.go",
+ "zruntime_defs_$GOOS_$GOARCH.go",
+ "zversion.go",
}},
};
".h",
".s",
".go",
+ ".goc",
};
// gentab records how to generate some trivial files.
static struct {
- char *name;
+ 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,
static void
install(char *dir)
{
- char *name, *p, *elem, *prefix;
+ char *name, *p, *elem, *prefix, *exe;
bool islib, ispkg, isgo, stale;
Buf b, b1, path;
Vec compile, files, link, go, missing, clean, lib, extra;
vinit(&extra);
// path = full path to dir.
- bprintf(&path, "%s/src/%s", goroot, dir);
- fixslash(&path);
+ bpathf(&path, "%s/src/%s", goroot, dir);
name = lastelem(dir);
islib = hasprefix(dir, "lib") || streq(dir, "cmd/cc") || streq(dir, "cmd/gc");
ispkg = hasprefix(dir, "pkg");
isgo = ispkg || streq(dir, "cmd/go");
+ exe = "";
+ if(streq(gohostos, "windows"))
+ exe = ".exe";
// Start final link command line.
// Note: code below knows that link.p[2] is the target.
prefix = "";
if(!hasprefix(name, "lib"))
prefix = "lib";
- bprintf(&b, "%s/lib/%s%s.a", goroot, prefix, name);
- fixslash(&b);
- vadd(&link, bstr(&b));
+ vadd(&link, bpathf(&b, "%s/pkg/obj/%s%s.a", goroot, prefix, name));
} else if(ispkg) {
// Go library (package).
- bprintf(&b, "%s/bin/go-tool/pack", goroot);
- fixslash(&b);
- vadd(&link, bstr(&b));
+ vadd(&link, bpathf(&b, "%s/bin/tool/pack", goroot));
vadd(&link, "grc");
p = bprintf(&b, "%s/pkg/%s_%s/%s", goroot, goos, goarch, dir+4);
*xstrrchr(p, '/') = '\0';
xmkdirall(p);
- bprintf(&b, "%s/pkg/%s_%s/%s.a", goroot, goos, goarch, dir+4);
- fixslash(&b);
- vadd(&link, bstr(&b));
+ vadd(&link, bpathf(&b, "%s/pkg/%s_%s/%s.a", goroot, goos, goarch, dir+4));
} else if(streq(dir, "cmd/go")) {
// Go command.
- bprintf(&b, "%s/bin/go-tool/%sl", goroot, gochar);
- fixslash(&b);
- vadd(&link, bstr(&b));
+ vadd(&link, bpathf(&b, "%s/bin/tool/%sl", goroot, gochar));
vadd(&link, "-o");
- bprintf(&b, "%s/bin/go-tool/go_bootstrap", goroot);
- fixslash(&b);
- vadd(&link, bstr(&b));
+ vadd(&link, bpathf(&b, "%s/bin/tool/go_bootstrap%s", goroot, exe));
} else {
// C command.
vadd(&link, "gcc");
vadd(&link, "-o");
- bprintf(&b, "%s/bin/go-tool/%s", goroot, name);
- fixslash(&b);
- vadd(&link, bstr(&b));
+ vadd(&link, bpathf(&b, "%s/bin/tool/%s%s", goroot, name, exe));
}
ttarg = mtime(link.p[2]);
for(i=0; i<nelem(deptab); i++) {
if(hasprefix(dir, deptab[i].prefix)) {
for(j=0; (p=deptab[i].dep[j])!=nil; j++) {
- if(hasprefix(p, "$GOROOT/")) {
- bprintf(&b1, "%s/%s", goroot, p+8);
- p = bstr(&b1);
- }
+ 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, p);
+ vadd(&lib, bpathf(&b, "%s", p));
continue;
}
if(hassuffix(p, "/*")) {
- bprintf(&b, "%s/%s", bstr(&path), p);
+ bpathf(&b, "%s/%s", bstr(&path), p);
b.len -= 2;
- fixslash(&b);
xreaddir(&extra, bstr(&b));
bprintf(&b, "%s", p);
b.len -= 2;
- for(k=0; k<extra.len; k++) {
- bprintf(&b1, "%s/%s", bstr(&b), extra.p[k]);
- fixslash(&b1);
- vadd(&files, bstr(&b1));
- }
+ for(k=0; k<extra.len; k++)
+ vadd(&files, bpathf(&b1, "%s/%s", bstr(&b), extra.p[k]));
continue;
}
if(hasprefix(p, "-")) {
// Convert to absolute paths.
for(i=0; i<files.len; i++) {
if(!isabs(files.p[i])) {
- bprintf(&b, "%s/%s", bstr(&path), files.p[i]);
- fixslash(&b);
+ bpathf(&b, "%s/%s", bstr(&path), files.p[i]);
xfree(files.p[i]);
files.p[i] = btake(&b);
}
}
- // For package runtime, copy some files into the work space.
- if(streq(dir, "pkg/runtime")) {
- copy(bprintf(&b, "%s/arch_GOARCH.h", workdir),
- bprintf(&b1, "%s/arch_%s.h", bstr(&path), goarch));
- copy(bprintf(&b, "%s/defs_GOOS_GOARCH.h", workdir),
- bprintf(&b1, "%s/defs_%s_%s.h", bstr(&path), goos, goarch));
- copy(bprintf(&b, "%s/os_GOOS.h", workdir),
- bprintf(&b1, "%s/os_%s.h", bstr(&path), goos));
- copy(bprintf(&b, "%s/signals_GOOS.h", workdir),
- bprintf(&b1, "%s/signals_%s.h", bstr(&path), goos));
- copy(bprintf(&b, "%s/zasm_GOOS_GOARCH.h", workdir),
- bprintf(&b1, "%s/zasm_%s_%s.h", bstr(&path), goos, goarch));
- }
-
-
// Is the target up-to-date?
stale = 1; // TODO: Decide when 0 is okay.
n = 0;
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) {
files.p[n++] = files.p[i];
continue;
}
- if(!hassuffix(p, ".a") && !shouldbuild(p, dir)) {
- xfree(files.p[i]);
- continue;
- }
- if(hassuffix(p, ".go"))
- vadd(&go, p);
files.p[n++] = files.p[i];
}
files.len = n;
if(!stale)
goto out;
- // Generate any missing files.
- for(i=0; i<missing.len; i++) {
- p = missing.p[i];
+ // 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));
+ copy(bpathf(&b, "%s/defs_GOOS_GOARCH.h", workdir),
+ bpathf(&b1, "%s/defs_%s_%s.h", bstr(&path), goos, goarch));
+ copy(bpathf(&b, "%s/os_GOOS.h", workdir),
+ bpathf(&b1, "%s/os_%s.h", bstr(&path), goos));
+ copy(bpathf(&b, "%s/signals_GOOS.h", workdir),
+ bpathf(&b1, "%s/signals_%s.h", bstr(&path), goos));
+ }
+
+ // 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(streq(gentab[j].name, elem)) {
+ if(hasprefix(elem, gentab[j].nameprefix)) {
+ if(vflag > 1)
+ xprintf("generate %s\n", p);
gentab[j].gen(bstr(&path), p);
- vadd(&clean, 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;
}
}
- fatal("missing file %s", p);
+ // 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));
+ }
+
+ // 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);
+ }
+
// Compile the files.
for(i=0; i<files.len; i++) {
if(!hassuffix(files.p[i], ".c") && !hassuffix(files.p[i], ".s"))
if(streq(dir, "lib9"))
vadd(&compile, "-DPLAN9PORT");
- bprintf(&b, "%s/include", goroot);
- fixslash(&b);
vadd(&compile, "-I");
- vadd(&compile, bstr(&b));
+ vadd(&compile, bpathf(&b, "%s/include", goroot));
vadd(&compile, "-I");
vadd(&compile, bstr(&path));
if(streq(name, "goos.c")) {
vadd(&compile, bprintf(&b, "-DGOOS=\"%s\"", goos));
vadd(&compile, bprintf(&b, "-DGOARCH=\"%s\"", goarch));
- vadd(&compile, bprintf(&b, "-DGOROOT=\"%s\"", goroot));
+ bprintf(&b1, "%s", goroot);
+ bsubst(&b1, "\\", "\\\\"); // turn into C string
+ vadd(&compile, bprintf(&b, "-DGOROOT=\"%s\"", bstr(&b1)));
vadd(&compile, bprintf(&b, "-DGOVERSION=\"%s\"", goversion));
}
}
} else {
// Supporting files for a Go package.
- if(hassuffix(files.p[i], ".s")) {
- bprintf(&b, "%s/bin/go-tool/%sa", goroot, gochar);
- fixslash(&b);
- vadd(&compile, bstr(&b));
- } else {
- bprintf(&b, "%s/bin/go-tool/%sc", goroot, gochar);
- fixslash(&b);
- vadd(&compile, bstr(&b));
+ if(hassuffix(files.p[i], ".s"))
+ vadd(&compile, bpathf(&b, "%s/bin/tool/%sa", goroot, gochar));
+ else {
+ vadd(&compile, bpathf(&b, "%s/bin/tool/%sc", goroot, gochar));
vadd(&compile, "-FVw");
}
vadd(&compile, "-I");
vadd(&compile, bprintf(&b, "-DGOARCH_%s", goos));
}
- bprintf(&b, "%s/%s", workdir, lastelem(files.p[i]));
+ 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]));
+ } else
+ bpathf(&b, "%s/%s", workdir, lastelem(files.p[i]));
+
b.p[b.len-1] = 'o'; // was c or s
- fixslash(&b);
vadd(&compile, "-o");
vadd(&compile, bstr(&b));
- vadd(&link, bstr(&b));
- vadd(&clean, bstr(&b));
-
vadd(&compile, files.p[i]);
+ bgrunv(bstr(&path), CheckExit, &compile);
- runv(nil, bstr(&path), CheckExit, &compile);
- vreset(&compile);
+ vadd(&link, bstr(&b));
+ 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);
- bprintf(&b, "%s/bin/go-tool/%sg", goroot, gochar);
- fixslash(&b);
- vadd(&compile, bstr(&b));
+ vadd(&compile, bpathf(&b, "%s/bin/tool/%sg", goroot, gochar));
- bprintf(&b, "%s/_go_.%s", workdir, gochar);
- fixslash(&b);
+ bpathf(&b, "%s/_go_.%s", workdir, gochar);
vadd(&compile, "-o");
vadd(&compile, bstr(&b));
vadd(&clean, bstr(&b));
runv(nil, nil, CheckExit, &link);
+ // 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));
+ copy(bpathf(&b, "%s/pkg/%s_%s/runtime.h", goroot, goos, goarch),
+ bpathf(&b1, "%s/src/pkg/runtime/runtime.h", goroot));
+ }
+
+
out:
for(i=0; i<clean.len; i++)
xremove(clean.p[i]);
shouldbuild(char *file, char *dir)
{
char *name, *p;
- int i, j, ret, true;
+ int i, j, ret;
Buf b;
Vec lines, fields;
// 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;
// Check file contents for // +build lines.
binit(&b);
return ret;
}
-// fixslash rewrites / to \ when the slash character is \, so that the paths look conventional.
-static void
-fixslash(Buf *b)
-{
- int i;
-
- if(slash[0] == '/')
- return;
- for(i=0; i<b->len; i++)
- if(b->p[i] == '/')
- b->p[i] = '\\';
-}
-
// copy copies the file src to dst, via memory (so only good for small files).
static void
copy(char *dst, char *src)
{
Buf b;
+ if(vflag > 1)
+ xprintf("cp %s %s\n", src, dst);
+
binit(&b);
readfile(&b, src);
writefile(&b, dst);
bfree(&b);
}
-/*
- * command implementations
- */
-
-// The env command prints the default environment.
-void
-cmdenv(int argc, char **argv)
-{
- USED(argc);
- USED(argv);
-
- xprintf("GOROOT=%s\n", goroot);
- xprintf("GOARCH=%s\n", goarch);
- xprintf("GOOS=%s\n", goos);
-}
-
// buildorder records the order of builds for the 'go bootstrap' command.
static char *buildorder[] = {
"lib9",
"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/cc",
+ "cmd/cov",
+ "cmd/gc",
+ "cmd/go",
+ "cmd/nm",
+ "cmd/pack",
+ "cmd/prof",
+ "lib9",
+ "libbio",
+ "libmach",
+ "pkg/bufio",
+ "pkg/bytes",
+ "pkg/container/heap",
+ "pkg/encoding/base64",
+ "pkg/encoding/gob",
+ "pkg/encoding/json",
+ "pkg/errors",
+ "pkg/flag",
+ "pkg/fmt",
+ "pkg/go/ast",
+ "pkg/go/build",
+ "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++) {
+ 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));
+ }
+
+ // Remove object tree.
+ xremoveall(bpathf(&b, "%s/pkg/obj", goroot));
+
+ // Remove installed packages and tools.
+ xremoveall(bpathf(&b, "%s/pkg/%s_%s", goroot, goos, goarch));
+ xremove(bpathf(&b, "%s/bin/tool", goroot));
+
+ // 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\"";
+ 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, "GOARCH", goarch);
+ xprintf(format, "GOOS", goos);
+ 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
Buf b;
char *p;
+ ARGBEGIN{
+ case 'v':
+ vflag++;
+ break;
+ default:
+ usage();
+ }ARGEND
+
+ if(argc > 0)
+ usage();
+
+ clean();
setup();
- // TODO: nuke();
-
binit(&b);
for(i=0; i<nelem(buildorder); i++) {
p = bprintf(&b, buildorder[i], gochar);
- xprintf("%s\n", p);
+ if(vflag > 0)
+ xprintf("%s\n", p);
install(p);
}
bfree(&b);
}
+static char*
+defaulttarg(void)
+{
+ char *p;
+ Buf pwd, src;
+
+ binit(&pwd);
+ binit(&src);
+
+ xgetwd(&pwd);
+ p = btake(&pwd);
+ bpathf(&src, "%s/src/", goroot);
+ if(!hasprefix(p, bstr(&src)))
+ fatal("current directory %s is not under %s", p, bstr(&src));
+ p += src.len;
+
+ bfree(&pwd);
+ bfree(&src);
+
+ return p;
+}
+
// Install installs the list of packages named on the command line.
void
cmdinstall(int argc, char **argv)
{
int i;
- for(i=1; i<argc; i++)
+ 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 setgrp 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", findgoversion());
+}
--- /dev/null
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "a.h"
+#include <stdio.h>
+
+/*
+ * Helpers for building 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, goversion));
+
+ writefile(&out, file);
+
+ 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);
+
+ 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);
+
+ 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/bin/tool/%sc", goroot, 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);
+ writefile(&out, bprintf(&b, "%s/zasm_GOOS_GOARCH.h", workdir));
+
+ 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/bin/tool/%sc", goroot, 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);
+
+ bfree(&in);
+ bfree(&b);
+ bfree(&out);
+ vfree(&argv);
+ vfree(&lines);
+ vfree(&fields);
+ vfree(&seen);
+}
--- /dev/null
+// 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 && 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;
+ }
+
+ bprintf(&out, "// auto generated by go tool dist\n\n");
+ input = bstr(&in);
+ output = &out;
+
+ process_file();
+
+ writefile(&out, c);
+}
#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.
{
int i;
- if(argc <= 1) {
- xprintf("go tool dist commands:\n");
- for(i=0; i<nelem(cmdtab); i++)
- xprintf("\t%s\n", cmdtab[i].name);
- xexit(1);
- }
+ if(argc <= 1)
+ usage();
for(i=0; i<nelem(cmdtab); i++) {
if(streq(cmdtab[i].name, argv[1])) {
}
}
- fatal("unknown command %s", argv[1]);
+ xprintf("unknown command %s\n", argv[1]);
+ usage();
}
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)
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.
vfree(&argv);
}
-
// runv is like run but takes a vector.
void
runv(Buf *b, char *dir, int mode, Vec *argv)
{
- int i, p[2], pid, status;
+ 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;
+} bg[MAXBG];
+static int nbg;
+
+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 >= nelem(bg))
+ bgwait1();
+
// Generate a copy of the command to show in a log.
// Substitute $WORK for the work directory.
binit(&cmd);
}
bwritestr(&cmd, q);
}
- printf("%s\n", bstr(&cmd));
- bfree(&cmd);
+ if(vflag > 1)
+ xprintf("%s\n", bstr(&cmd));
if(b != nil) {
breset(b);
}
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);
}
breadfrom(b, p[0]);
close(p[0]);
}
-wait:
+
+ if(nbg < 0)
+ fatal("bad bookkeeping");
+ bg[nbg].pid = pid;
+ bg[nbg].mode = mode;
+ bg[nbg].cmd = btake(&cmd);
+ nbg++;
+
+ if(wait)
+ bgwait();
+
+ bfree(&cmd);
+}
+
+// bgwait1 waits for a single background job.
+static void
+bgwait1(void)
+{
+ int i, pid, status, mode;
+ char *cmd;
+
errno = 0;
- if(waitpid(pid, &status, 0) != pid) {
- if(errno == EINTR)
- goto wait;
- fatal("waitpid: %s", strerror(errno));
+ while((pid = wait(&status)) < 0) {
+ if(errno != EINTR)
+ fatal("waitpid: %s", strerror(errno));
}
- if(mode==CheckExit && (!WIFEXITED(status) || WEXITSTATUS(status) != 0)) {
- if(b != nil)
- fwrite(b->p, b->len, 1, stderr);
- fatal("%s failed", argv->p[0]);
+ 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;
+ bg[i] = bg[--nbg];
+
+ if(mode == CheckExit && (!WIFEXITED(status) || WEXITSTATUS(status) != 0)) {
+ 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
xremove(char *p)
{
+ if(vflag > 1)
+ xprintf("rm %s\n", p);
unlink(p);
}
bprintf(&b, "%s/%s", p, dir.p[i]);
xremoveall(bstr(&b));
}
+ if(vflag > 1)
+ xprintf("rm %s\n", p);
rmdir(p);
} else {
+ if(vflag > 1)
+ xprintf("rm %s\n", p);
unlink(p);
}
binit(&b);
p = argv[0];
- if(hassuffix(p, "bin/go-tool/dist")) {
+ if(hassuffix(p, "bin/tool/dist")) {
default_goroot = xstrdup(p);
- default_goroot[strlen(p)-strlen("bin/go-tool/dist")] = '\0';
+ default_goroot[strlen(p)-strlen("bin/tool/dist")] = '\0';
}
slash = "/";