diff --git a/src/cmd/dist/a.h b/src/cmd/dist/a.h index 65b483fb227a36ebee868c9875095c596707d092..39048e37d710380bc14efa6c2ba38db9ae603c55 100644 --- a/src/cmd/dist/a.h +++ b/src/cmd/dist/a.h @@ -37,10 +37,13 @@ enum { // 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); @@ -62,23 +65,41 @@ void splitfields(Vec*, char*); 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) @@ -94,6 +115,8 @@ 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*); void xatexit(void (*f)(void)); @@ -118,7 +141,6 @@ void xremoveall(char *p); void xsetenv(char*, char*); int xstrcmp(char*, char*); char* xstrdup(char *p); -int xstreq(char*, char*); int xstrlen(char*); char* xstrrchr(char*, int); char* xstrstr(char*, char*); diff --git a/src/cmd/dist/arg.h b/src/cmd/dist/arg.h new file mode 100644 index 0000000000000000000000000000000000000000..6eef0353be6c146c8a1ab394fa85f57a409a62ef --- /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 index b05561d3826a6c58d8ad25cdc1aea2590e9a3204..c7a7c1277c78a7931ffc74a003d6d6ebc5c18c90 100644 --- a/src/cmd/dist/buf.c +++ b/src/cmd/dist/buf.c @@ -99,6 +99,32 @@ 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) + 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. @@ -214,7 +240,7 @@ vuniq(Vec *v) void splitlines(Vec *v, char *p) { - int i, c; + int i; char *start; vreset(v); diff --git a/src/cmd/dist/build.c b/src/cmd/dist/build.c index 17f491af8a16fcc6b682632b0196589340f54925..f2b25d991ebfdc2ac115d5b5011f0c7f234cc385 100644 --- a/src/cmd/dist/build.c +++ b/src/cmd/dist/build.c @@ -3,6 +3,7 @@ // license that can be found in the LICENSE file. #include "a.h" +#include "arg.h" /* * Initialization for any invocation. @@ -18,13 +19,13 @@ char *goroot; 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"; @@ -51,7 +52,7 @@ static char *okgoos[] = { 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; @@ -92,14 +93,13 @@ init(void) 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); @@ -130,6 +130,8 @@ init(void) xsetenv("LANG", "C"); xsetenv("LANGUAGE", "en_US.UTF8"); + goversion = findgoversion(); + workdir = xworkdir(); xatexit(rmworkdir); @@ -140,10 +142,115 @@ init(void) 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. */ @@ -180,33 +287,26 @@ setup(void) 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); @@ -261,10 +361,6 @@ static struct { "$GOROOT/include/libc.h", "fmt/*", "utf/*", - "-utf/mkrunetype", - "-utf\\mkrunetype", - "-utf/runetypebody", - "-utf\\runetypebody", }}, {"libbio", { "$GOROOT/include/u.h", @@ -300,37 +396,37 @@ static struct { "../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/*", @@ -345,9 +441,16 @@ static struct { "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", }}, }; @@ -357,15 +460,21 @@ char *depsuffix[] = { ".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, @@ -373,7 +482,7 @@ static struct { 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; @@ -393,14 +502,16 @@ install(char *dir) 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. @@ -411,37 +522,25 @@ install(char *dir) 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]); @@ -452,26 +551,24 @@ install(char *dir) 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, "-")) { @@ -495,28 +592,12 @@ install(char *dir) // 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; @@ -529,6 +610,12 @@ install(char *dir) 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) { @@ -536,12 +623,6 @@ install(char *dir) 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; @@ -553,21 +634,67 @@ install(char *dir) 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")) @@ -585,10 +712,8 @@ install(char *dir) 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)); @@ -597,7 +722,9 @@ install(char *dir) 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)); } @@ -608,14 +735,10 @@ install(char *dir) } } 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"); @@ -624,30 +747,35 @@ install(char *dir) 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)); @@ -678,6 +806,16 @@ install(char *dir) 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]); @@ -713,7 +851,7 @@ static bool shouldbuild(char *file, char *dir) { char *name, *p; - int i, j, ret, true; + int i, j, ret; Buf b; Vec lines, fields; @@ -729,6 +867,13 @@ shouldbuild(char *file, char *dir) // 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); @@ -777,47 +922,21 @@ out: 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", @@ -883,6 +1002,183 @@ static char *buildorder[] = { "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 @@ -892,25 +1188,160 @@ cmdbootstrap(int argc, char **argv) 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()); +} diff --git a/src/cmd/dist/buildruntime.c b/src/cmd/dist/buildruntime.c new file mode 100644 index 0000000000000000000000000000000000000000..7cbff3fb87c1be324914138d9347fee3460987d7 --- /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, 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); +} diff --git a/src/cmd/dist/goc2c.c b/src/cmd/dist/goc2c.c new file mode 100644 index 0000000000000000000000000000000000000000..6829dedc78c6642bc35336d08bfbb99ddac08d24 --- /dev/null +++ b/src/cmd/dist/goc2c.c @@ -0,0 +1,727 @@ +// 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); +} diff --git a/src/cmd/dist/main.c b/src/cmd/dist/main.c index adfdd2d4a6d9b587e388134db1aa25b05af305b2..72a7579d14230f132e9480678153733e9a6847f9 100644 --- a/src/cmd/dist/main.c +++ b/src/cmd/dist/main.c @@ -4,14 +4,20 @@ #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. @@ -20,12 +26,8 @@ xmain(int argc, char **argv) { int i; - if(argc <= 1) { - xprintf("go tool dist commands:\n"); - for(i=0; i<nelem(cmdtab); i++) - xprintf("\t%s\n", cmdtab[i].name); - xexit(1); - } + if(argc <= 1) + usage(); for(i=0; i<nelem(cmdtab); i++) { if(streq(cmdtab[i].name, argv[1])) { @@ -34,5 +36,6 @@ xmain(int argc, char **argv) } } - fatal("unknown command %s", argv[1]); + xprintf("unknown command %s\n", argv[1]); + usage(); } diff --git a/src/cmd/dist/unix.c b/src/cmd/dist/unix.c index 92d644876d8cc6c4cd37f44c4573b91e486dff6f..465a86c0df82d4d128d8db665d7209a584d38500 100644 --- a/src/cmd/dist/unix.c +++ b/src/cmd/dist/unix.c @@ -40,6 +40,36 @@ bprintf(Buf *b, char *fmt, ...) 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) @@ -69,6 +99,8 @@ xgetenv(Buf *b, char *name) 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. @@ -92,15 +124,43 @@ run(Buf *b, char *dir, int mode, char *cmd, ...) 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); @@ -114,8 +174,8 @@ runv(Buf *b, char *dir, int mode, Vec *argv) } bwritestr(&cmd, q); } - printf("%s\n", bstr(&cmd)); - bfree(&cmd); + if(vflag > 1) + xprintf("%s\n", bstr(&cmd)); if(b != nil) { breset(b); @@ -143,6 +203,7 @@ runv(Buf *b, char *dir, int mode, Vec *argv) } 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); } @@ -151,18 +212,55 @@ runv(Buf *b, char *dir, int mode, Vec *argv) 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. @@ -288,6 +386,8 @@ xmkdirall(char *p) void xremove(char *p) { + if(vflag > 1) + xprintf("rm %s\n", p); unlink(p); } @@ -308,8 +408,12 @@ xremoveall(char *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); } @@ -526,9 +630,9 @@ main(int argc, char **argv) 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 = "/";