]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/dist: generate files for package runtime
authorRuss Cox <rsc@golang.org>
Fri, 3 Feb 2012 23:16:42 +0000 (18:16 -0500)
committerRuss Cox <rsc@golang.org>
Fri, 3 Feb 2012 23:16:42 +0000 (18:16 -0500)
goc2c moves here.
parallel builds like old makefiles (-j4).
add clean command.
add banner command.
implement Go version check.
real argument parsing (same as 6g etc)

Windows changes will be a separate CL.

R=golang-dev, bradfitz, iant
CC=golang-dev
https://golang.org/cl/5622058

src/cmd/dist/a.h
src/cmd/dist/arg.h [new file with mode: 0644]
src/cmd/dist/buf.c
src/cmd/dist/build.c
src/cmd/dist/buildruntime.c [new file with mode: 0644]
src/cmd/dist/goc2c.c [new file with mode: 0644]
src/cmd/dist/main.c
src/cmd/dist/unix.c

index 65b483fb227a36ebee868c9875095c596707d092..39048e37d710380bc14efa6c2ba38db9ae603c55 100644 (file)
@@ -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 (file)
index 0000000..6eef035
--- /dev/null
@@ -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
+
index b05561d3826a6c58d8ad25cdc1aea2590e9a3204..c7a7c1277c78a7931ffc74a003d6d6ebc5c18c90 100644 (file)
@@ -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);
index 17f491af8a16fcc6b682632b0196589340f54925..f2b25d991ebfdc2ac115d5b5011f0c7f234cc385 100644 (file)
@@ -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 (file)
index 0000000..7cbff3f
--- /dev/null
@@ -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 (file)
index 0000000..6829ded
--- /dev/null
@@ -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, &params, &paramwid, &rets)) {
+               write_func_header(package, name, params, paramwid, rets);
+               copy_body();
+               write_func_trailer(package, name, rets);
+               xfree(name);
+               free_params(params);
+               free_params(rets);
+       }
+       xfree(package);
+}
+
+void
+goc2c(char *goc, char *c)
+{
+       Buf in, out;
+       
+       binit(&in);
+       binit(&out);
+       
+       file = goc;
+       readfile(&in, goc);
+
+       // TODO: set gcc=1 when using gcc
+
+       if(!gcc && 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);
+}
index adfdd2d4a6d9b587e388134db1aa25b05af305b2..72a7579d14230f132e9480678153733e9a6847f9 100644 (file)
@@ -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();
 }
index 92d644876d8cc6c4cd37f44c4573b91e486dff6f..465a86c0df82d4d128d8db665d7209a584d38500 100644 (file)
@@ -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 = "/";