This source file includes following definitions.
- find
 
- init
 
- rmworkdir
 
- chomp
 
- findgoversion
 
- setup
 
- install
 
- matchfield
 
- shouldbuild
 
- copy
 
- dopack
 
- clean
 
- usage
 
- cmdenv
 
- cmdbootstrap
 
- defaulttarg
 
- cmdinstall
 
- cmdclean
 
- cmdbanner
 
- cmdversion
 
#include "a.h"
#include "arg.h"
char *goarch;
char *gobin;
char *gohostarch;
char *gohostchar;
char *gohostos;
char *goos;
char *goarm;
char *go386;
char *goroot = GOROOT_FINAL;
char *goroot_final = GOROOT_FINAL;
char *goextlinkenabled = "";
char *workdir;
char *tooldir;
char *gochar;
char *goversion;
char *slash;    
char *defaultcc;
char *defaultcflags;
char *defaultldflags;
char *defaultcxxtarget;
char *defaultcctarget;
bool    rebuildall;
bool defaultclang;
static bool shouldbuild(char*, char*);
static void copy(char*, char*, int);
static void dopack(char*, char*, char**, int);
static char *findgoversion(void);
static char *gochars = "5668";
static char *okgoarch[] = {
        
        "arm",
        "amd64",
        "amd64p32",
        "386",
};
static char *okgoos[] = {
        "darwin",
        "dragonfly",
        "linux",
        "solaris",
        "freebsd",
        "nacl",
        "netbsd",
        "openbsd",
        "plan9",
        "windows",
};
static void rmworkdir(void);
int
find(char *p, char **l, int n)
{
        int i;
        for(i=0; i<n; i++)
                if(streq(p, l[i]))
                        return i;
        return -1;
}
void
init(void)
{
        char *p;
        int i;
        Buf b;
        binit(&b);
        xgetenv(&b, "GOROOT");
        if(b.len > 0) {
                
                if(b.len >= 2 && b.p[b.len - 1] == slash[0])
                        b.len--;
                goroot = btake(&b);
        }
        xgetenv(&b, "GOBIN");
        if(b.len == 0)
                bprintf(&b, "%s%sbin", goroot, slash);
        gobin = btake(&b);
        xgetenv(&b, "GOOS");
        if(b.len == 0)
                bwritestr(&b, gohostos);
        goos = btake(&b);
        if(find(goos, okgoos, nelem(okgoos)) < 0)
                fatal("unknown $GOOS %s", goos);
        xgetenv(&b, "GOARM");
        if(b.len == 0)
                bwritestr(&b, xgetgoarm());
        goarm = btake(&b);
        xgetenv(&b, "GO386");
        if(b.len == 0) {
                if(cansse2())
                        bwritestr(&b, "sse2");
                else
                        bwritestr(&b, "387");
        }
        go386 = btake(&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);
        i = find(gohostarch, okgoarch, nelem(okgoarch));
        if(i < 0)
                fatal("unknown $GOHOSTARCH %s", gohostarch);
        bprintf(&b, "%c", gochars[i]);
        gohostchar = btake(&b);
        xgetenv(&b, "GOARCH");
        if(b.len == 0)
                bwritestr(&b, gohostarch);
        goarch = btake(&b);
        i = find(goarch, okgoarch, nelem(okgoarch));
        if(i < 0)
                fatal("unknown $GOARCH %s", goarch);
        bprintf(&b, "%c", gochars[i]);
        gochar = btake(&b);
        xgetenv(&b, "GO_EXTLINK_ENABLED");
        if(b.len > 0) {
                goextlinkenabled = btake(&b);
                if(!streq(goextlinkenabled, "0") && !streq(goextlinkenabled, "1"))
                        fatal("unknown $GO_EXTLINK_ENABLED %s", goextlinkenabled);
        }
        
        xgetenv(&b, "CC");
        if(b.len == 0) {
                
                
                
                
                
                if(defaultclang)
                        bprintf(&b, "clang");
                else
                        bprintf(&b, "gcc");
        }
        defaultcc = btake(&b);
        xgetenv(&b, "CFLAGS");
        defaultcflags = btake(&b);
        xgetenv(&b, "LDFLAGS");
        defaultldflags = btake(&b);
        xgetenv(&b, "CC_FOR_TARGET");
        if(b.len == 0) {
                bprintf(&b, defaultcc);
        }
        defaultcctarget = btake(&b);
        xgetenv(&b, "CXX_FOR_TARGET");
        if(b.len == 0) {
                xgetenv(&b, "CXX");
                if(b.len == 0) {
                        if(defaultclang)
                                bprintf(&b, "clang++");
                        else
                                bprintf(&b, "g++");
                }
        }
        defaultcxxtarget = btake(&b);
        xsetenv("GOROOT", goroot);
        xsetenv("GOARCH", goarch);
        xsetenv("GOOS", goos);
        xsetenv("GOARM", goarm);
        xsetenv("GO386", go386);
        
        xsetenv("LANG", "C");
        xsetenv("LANGUAGE", "en_US.UTF8");
        goversion = findgoversion();
        workdir = xworkdir();
        xatexit(rmworkdir);
        bpathf(&b, "%s/pkg/tool/%s_%s", goroot, gohostos, gohostarch);
        tooldir = btake(&b);
        bfree(&b);
}
static void
rmworkdir(void)
{
        if(vflag > 1)
                errprintf("rm -rf %s\n", workdir);
        xremoveall(workdir);
}
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--;
}
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);
        
        
        bpathf(&path, "%s/VERSION", goroot);
        if(isfile(bstr(&path))) {
                readfile(&b, bstr(&path));
                chomp(&b);
                
                
                
                
                if(b.len > 0)
                        goto done;
        }
        
        
        
        bpathf(&path, "%s/VERSION.cache", goroot);
        if(isfile(bstr(&path))) {
                readfile(&b, bstr(&path));
                chomp(&b);
                goto done;
        }
        
        
        run(&branch, goroot, CheckExit, "hg", "identify", "-b", nil);
        chomp(&branch);
        
        tag = "devel";
        rev = ".";
        run(&b, goroot, CheckExit, "hg", "log", "-b", bstr(&branch), "-r", ".:0", "--template", "{tags} + ", nil);
        splitfields(&tags, bstr(&b));
        nrev = 0;
        for(i=0; i<tags.len; i++) {
                p = tags.p[i];
                if(streq(p, "+"))
                        nrev++;
                
                if(hasprefix(p, "go") && (!contains(p, "beta") || nrev == 0)) {
                        tag = xstrdup(p);
                        
                        
                        
                        if(nrev == 0)
                                rev = "";
                        break;
                }
        }
        if(tag[0] == '\0') {
                
                bprintf(&b, "branch.%s", bstr(&branch));
                tag = btake(&b);
        }
        if(rev[0]) {
                
                
                run(&bmore, goroot, CheckExit, "hg", "log", "--template", " +{node|short} {date|date}", "-r", rev, nil);
                chomp(&bmore);
        }
        bprintf(&b, "%s", tag);
        if(bmore.len > 0)
                bwriteb(&b, &bmore);
        
        writefile(&b, bstr(&path), 0);
done:
        p = btake(&b);
        bfree(&b);
        bfree(&path);
        bfree(&bmore);
        bfree(&branch);
        vfree(&tags);
        return p;
}
static char *oldtool[] = {
        "5a", "5c", "5g", "5l",
        "6a", "6c", "6g", "6l",
        "8a", "8c", "8g", "8l",
        "6cov",
        "6nm",
        "6prof",
        "cgo",
        "ebnflint",
        "goapi",
        "gofix",
        "goinstall",
        "gomake",
        "gopack",
        "gopprof",
        "gotest",
        "gotype",
        "govet",
        "goyacc",
        "quietgcc",
};
static char *unreleased[] = {
        "src/cmd/link",
        "src/pkg/debug/goobj",
        "src/pkg/old",
};
static void
setup(void)
{
        int i;
        Buf b;
        char *p;
        binit(&b);
        
        p = bpathf(&b, "%s/bin", goroot);
        if(!isdir(p))
                xmkdir(p);
        
        p = bpathf(&b, "%s/pkg", goroot);
        if(!isdir(p))
                xmkdir(p);
        p = bpathf(&b, "%s/pkg/%s_%s", goroot, gohostos, gohostarch);
        if(rebuildall)
                xremoveall(p);
        xmkdirall(p);
        if(!streq(goos, gohostos) || !streq(goarch, gohostarch)) {
                p = bpathf(&b, "%s/pkg/%s_%s", goroot, goos, goarch);
                if(rebuildall)
                        xremoveall(p);
                xmkdirall(p);
        }
        
        
        
        
        
        bpathf(&b, "%s/pkg/obj/libgc.a", goroot);
        if(isfile(bstr(&b)))
                xremoveall(bpathf(&b, "%s/pkg/obj", goroot));
        p = bpathf(&b, "%s/pkg/obj/%s_%s", goroot, gohostos, gohostarch);
        if(rebuildall)
                xremoveall(p);
        xmkdirall(p);
        
        
        if(rebuildall)
                xremoveall(tooldir);
        xmkdirall(tooldir);
        
        xremoveall(bpathf(&b, "%s/bin/tool", goroot));
        
        for(i=0; i<nelem(oldtool); i++)
                xremove(bpathf(&b, "%s/bin/%s", goroot, oldtool[i]));
        
        for(i=0; gochars[i]; i++) {
                if(isfile(bprintf(&b, "%s%s%c%s", gobin, slash, gochars[i], "g"))) {
                        for(i=0; i<nelem(oldtool); i++)
                                xremove(bprintf(&b, "%s%s%s", gobin, slash, oldtool[i]));
                        break;
                }
        }
        
        if(hasprefix(goversion, "release.") || (hasprefix(goversion, "go") && !contains(goversion, "beta"))) {
                for(i=0; i<nelem(unreleased); i++)
                        if(isdir(bpathf(&b, "%s/%s", goroot, unreleased[i])))
                                fatal("%s should not exist in release build", bstr(&b));
        }
        bfree(&b);
}
static char *proto_gccargs[] = {
        "-Wall",
        
        
        "-Wstrict-prototypes",
        "-Wextra",
        "-Wunused",
        "-Wno-sign-compare",
        "-Wno-missing-braces",
        "-Wno-parentheses",
        "-Wno-unknown-pragmas",
        "-Wno-switch",
        "-Wno-comment",
        "-Wno-missing-field-initializers",
        "-Werror",
        "-fno-common",
        "-ggdb",
        "-pipe",
};
static char *proto_gccargs2[] = {
        
        
        
        "-Wuninitialized",
#if defined(__NetBSD__) && defined(__arm__)
        
        
        "-O1",
#else
        "-O2",
#endif
};
static Vec gccargs, ldargs;
static struct {
        char *prefix;  
        char *dep[20];  
} deptab[] = {
        {"lib9", {
                "$GOROOT/include/u.h",
                "$GOROOT/include/utf.h",
                "$GOROOT/include/fmt.h",
                "$GOROOT/include/libc.h",
                "fmt/*",
                "utf/*",
        }},
        {"libbio", {
                "$GOROOT/include/u.h",
                "$GOROOT/include/utf.h",
                "$GOROOT/include/fmt.h",
                "$GOROOT/include/libc.h",
                "$GOROOT/include/bio.h",
        }},
        {"liblink", {
                "$GOROOT/include/u.h",
                "$GOROOT/include/utf.h",
                "$GOROOT/include/fmt.h",
                "$GOROOT/include/libc.h",
                "$GOROOT/include/bio.h",
                "$GOROOT/include/ar.h",
                "$GOROOT/include/link.h",
                "anames5.c",
                "anames6.c",
                "anames8.c",
        }},
        {"cmd/cc", {
                "-pgen.c",
                "-pswt.c",
        }},
        {"cmd/gc", {
                "-cplx.c",
                "-pgen.c",
                "-plive.c",
                "-popt.c",
                "-y1.tab.c",  
                "opnames.h",
        }},
        {"cmd/5c", {
                "../cc/pgen.c",
                "../cc/pswt.c",
                "$GOROOT/pkg/obj/$GOHOSTOS_$GOHOSTARCH/libcc.a",
        }},
        {"cmd/6c", {
                "../cc/pgen.c",
                "../cc/pswt.c",
                "$GOROOT/pkg/obj/$GOHOSTOS_$GOHOSTARCH/libcc.a",
        }},
        {"cmd/8c", {
                "../cc/pgen.c",
                "../cc/pswt.c",
                "$GOROOT/pkg/obj/$GOHOSTOS_$GOHOSTARCH/libcc.a",
        }},
        {"cmd/5g", {
                "../gc/cplx.c",
                "../gc/pgen.c",
                "../gc/plive.c",
                "../gc/popt.c",
                "../gc/popt.h",
                "$GOROOT/pkg/obj/$GOHOSTOS_$GOHOSTARCH/libgc.a",
        }},
        {"cmd/6g", {
                "../gc/cplx.c",
                "../gc/pgen.c",
                "../gc/plive.c",
                "../gc/popt.c",
                "../gc/popt.h",
                "$GOROOT/pkg/obj/$GOHOSTOS_$GOHOSTARCH/libgc.a",
        }},
        {"cmd/8g", {
                "../gc/cplx.c",
                "../gc/pgen.c",
                "../gc/plive.c",
                "../gc/popt.c",
                "../gc/popt.h",
                "$GOROOT/pkg/obj/$GOHOSTOS_$GOHOSTARCH/libgc.a",
        }},
        {"cmd/5l", {
                "../ld/*",
        }},
        {"cmd/6l", {
                "../ld/*",
        }},
        {"cmd/8l", {
                "../ld/*",
        }},
        {"cmd/go", {
                "zdefaultcc.go",
        }},
        {"cmd/", {
                "$GOROOT/pkg/obj/$GOHOSTOS_$GOHOSTARCH/liblink.a",
                "$GOROOT/pkg/obj/$GOHOSTOS_$GOHOSTARCH/libbio.a",
                "$GOROOT/pkg/obj/$GOHOSTOS_$GOHOSTARCH/lib9.a",
        }},
        {"pkg/runtime", {
                "zaexperiment.h", 
                "zasm_$GOOS_$GOARCH.h",
                "zsys_$GOOS_$GOARCH.s",
                "zgoarch_$GOARCH.go",
                "zgoos_$GOOS.go",
                "zruntime_defs_$GOOS_$GOARCH.go",
                "zversion.go",
        }},
};
char *depsuffix[] = {
        ".c",
        ".h",
        ".s",
        ".go",
        ".goc",
};
static struct {
        char *nameprefix;
        void (*gen)(char*, char*);
} gentab[] = {
        {"opnames.h", gcopnames},
        {"anames5.c", mkanames},
        {"anames6.c", mkanames},
        {"anames8.c", mkanames},
        {"zasm_", mkzasm},
        {"zdefaultcc.go", mkzdefaultcc},
        {"zsys_", mkzsys},
        {"zgoarch_", mkzgoarch},
        {"zgoos_", mkzgoos},
        {"zruntime_defs_", mkzruntimedefs},
        {"zversion.go", mkzversion},
        {"zaexperiment.h", mkzexperiment},
        
        {"enam.c", nil},
};
static void
install(char *dir)
{
        char *name, *p, *elem, *prefix, *exe;
        bool islib, ispkg, isgo, stale, ispackcmd;
        Buf b, b1, path;
        Vec compile, files, link, go, missing, clean, lib, extra;
        Time ttarg, t;
        int i, j, k, n, doclean, targ;
        if(vflag) {
                if(!streq(goos, gohostos) || !streq(goarch, gohostarch))
                        errprintf("%s (%s/%s)\n", dir, goos, goarch);
                else
                        errprintf("%s\n", dir);
        }
        binit(&b);
        binit(&b1);
        binit(&path);
        vinit(&compile);
        vinit(&files);
        vinit(&link);
        vinit(&go);
        vinit(&missing);
        vinit(&clean);
        vinit(&lib);
        vinit(&extra);
        
        bpathf(&path, "%s/src/%s", goroot, dir);
        name = lastelem(dir);
        
        if(hasprefix(dir, "misc/")) {
                copy(bpathf(&b, "%s/%s", tooldir, name),
                        bpathf(&b1, "%s/misc/%s", goroot, name), 1);
                goto out;
        }
        
        if(gccargs.len == 0) {
                bprintf(&b, "%s %s", defaultcc, defaultcflags);
                splitfields(&gccargs, bstr(&b));
                for(i=0; i<nelem(proto_gccargs); i++)
                        vadd(&gccargs, proto_gccargs[i]);
                if(defaultcflags[0] == '\0') {
                        for(i=0; i<nelem(proto_gccargs2); i++)
                                vadd(&gccargs, proto_gccargs2[i]);
                }
                if(contains(gccargs.p[0], "clang")) {
                        
                        vadd(&gccargs, "-fno-caret-diagnostics");
                        
                        vadd(&gccargs, "-Qunused-arguments");
                }
                
                vadd(&gccargs, "-fmessage-length=0");
                if(streq(gohostos, "darwin")) {
                        
                        vadd(&gccargs, "-mmacosx-version-min=10.6");
                }
        }
        if(ldargs.len == 0 && defaultldflags[0] != '\0') {
                bprintf(&b, "%s", defaultldflags);
                splitfields(&ldargs, bstr(&b));
        }
        islib = hasprefix(dir, "lib") || streq(dir, "cmd/cc") || streq(dir, "cmd/gc");
        ispkg = hasprefix(dir, "pkg");
        isgo = ispkg || streq(dir, "cmd/go") || streq(dir, "cmd/cgo");
        exe = "";
        if(streq(gohostos, "windows"))
                exe = ".exe";
        
        
        ispackcmd = 0;
        if(islib) {
                
                vadd(&link, "ar");
                if(streq(gohostos, "plan9"))
                        vadd(&link, "rc");
                else
                        vadd(&link, "rsc");
                prefix = "";
                if(!hasprefix(name, "lib"))
                        prefix = "lib";
                targ = link.len;
                vadd(&link, bpathf(&b, "%s/pkg/obj/%s_%s/%s%s.a", goroot, gohostos, gohostarch, prefix, name));
        } else if(ispkg) {
                
                ispackcmd = 1;
                vadd(&link, "pack"); 
                p = bprintf(&b, "%s/pkg/%s_%s/%s", goroot, goos, goarch, dir+4);
                *xstrrchr(p, '/') = '\0';
                xmkdirall(p);
                targ = link.len;
                vadd(&link, bpathf(&b, "%s/pkg/%s_%s/%s.a", goroot, goos, goarch, dir+4));
        } else if(streq(dir, "cmd/go") || streq(dir, "cmd/cgo")) {
                
                vadd(&link, bpathf(&b, "%s/%sl", tooldir, gochar));
                vadd(&link, "-o");
                elem = name;
                if(streq(elem, "go"))
                        elem = "go_bootstrap";
                targ = link.len;
                vadd(&link, bpathf(&b, "%s/%s%s", tooldir, elem, exe));
        } else {
                
                if(streq(gohostos, "plan9")) {
                        vadd(&link, bprintf(&b, "%sl", gohostchar));
                        vadd(&link, "-o");
                        targ = link.len;
                        vadd(&link, bpathf(&b, "%s/%s", tooldir, name));
                } else {
                        vcopy(&link, gccargs.p, gccargs.len);
                        vcopy(&link, ldargs.p, ldargs.len);
                        if(sflag)
                                vadd(&link, "-static");
                        vadd(&link, "-o");
                        targ = link.len;
                        vadd(&link, bpathf(&b, "%s/%s%s", tooldir, name, exe));
                        if(streq(gohostarch, "amd64"))
                                vadd(&link, "-m64");
                        else if(streq(gohostarch, "386"))
                                vadd(&link, "-m32");
                }
        }
        ttarg = mtime(link.p[targ]);
        
        
        
        xreaddir(&files, bstr(&path));
        
        
        
        
        
        n = 0;
        for(i=0; i<files.len; i++) {
                p = files.p[i];
                if(hasprefix(p, ".") || (hasprefix(p, "_") && hassuffix(p, ".go")))
                        xfree(p);
                else
                        files.p[n++] = p;
        }
        files.len = n;
        for(i=0; i<nelem(deptab); i++) {
                if(streq(dir, deptab[i].prefix) ||
                   (hassuffix(deptab[i].prefix, "/") && hasprefix(dir, deptab[i].prefix))) {
                        for(j=0; (p=deptab[i].dep[j])!=nil; j++) {
                                breset(&b1);
                                bwritestr(&b1, p);
                                bsubst(&b1, "$GOROOT", goroot);
                                bsubst(&b1, "$GOOS", goos);
                                bsubst(&b1, "$GOARCH", goarch);
                                bsubst(&b1, "$GOHOSTOS", gohostos);
                                bsubst(&b1, "$GOHOSTARCH", gohostarch);
                                p = bstr(&b1);
                                if(hassuffix(p, ".a")) {
                                        vadd(&lib, bpathf(&b, "%s", p));
                                        continue;
                                }
                                if(hassuffix(p, "/*")) {
                                        bpathf(&b, "%s/%s", bstr(&path), p);
                                        b.len -= 2;
                                        xreaddir(&extra, bstr(&b));
                                        bprintf(&b, "%s", p);
                                        b.len -= 2;
                                        for(k=0; k<extra.len; k++)
                                                vadd(&files, bpathf(&b1, "%s/%s", bstr(&b), extra.p[k]));
                                        continue;
                                }
                                if(hasprefix(p, "-")) {
                                        p++;
                                        n = 0;
                                        for(k=0; k<files.len; k++) {
                                                if(hasprefix(files.p[k], p))
                                                        xfree(files.p[k]);
                                                else
                                                        files.p[n++] = files.p[k];
                                        }
                                        files.len = n;
                                        continue;
                                }
                                vadd(&files, p);
                        }
                }
        }
        vuniq(&files);
        
        for(i=0; i<files.len; i++) {
                if(!isabs(files.p[i])) {
                        bpathf(&b, "%s/%s", bstr(&path), files.p[i]);
                        xfree(files.p[i]);
                        files.p[i] = btake(&b);
                }
        }
        
        stale = rebuildall;
        n = 0;
        for(i=0; i<files.len; i++) {
                p = files.p[i];
                for(j=0; j<nelem(depsuffix); j++)
                        if(hassuffix(p, depsuffix[j]))
                                goto ok;
                xfree(files.p[i]);
                continue;
        ok:
                t = mtime(p);
                if(t != 0 && !hassuffix(p, ".a") && !shouldbuild(p, dir)) {
                        xfree(files.p[i]);
                        continue;
                }
                if(hassuffix(p, ".go"))
                        vadd(&go, p);
                if(t > ttarg)
                        stale = 1;
                if(t == 0) {
                        vadd(&missing, p);
                        files.p[n++] = files.p[i];
                        continue;
                }
                files.p[n++] = files.p[i];
        }
        files.len = n;
        
        if(files.len == 0)
                goto out;
        
        for(i=0; i<lib.len && !stale; i++)
                if(mtime(lib.p[i]) > ttarg)
                        stale = 1;
        if(!stale)
                goto out;
        
        if(streq(dir, "pkg/runtime")) {
                copy(bpathf(&b, "%s/arch_GOARCH.h", workdir),
                        bpathf(&b1, "%s/arch_%s.h", bstr(&path), goarch), 0);
                copy(bpathf(&b, "%s/defs_GOOS_GOARCH.h", workdir),
                        bpathf(&b1, "%s/defs_%s_%s.h", bstr(&path), goos, goarch), 0);
                p = bpathf(&b1, "%s/signal_%s_%s.h", bstr(&path), goos, goarch);
                if(isfile(p))
                        copy(bpathf(&b, "%s/signal_GOOS_GOARCH.h", workdir), p, 0);
                copy(bpathf(&b, "%s/os_GOOS.h", workdir),
                        bpathf(&b1, "%s/os_%s.h", bstr(&path), goos), 0);
                copy(bpathf(&b, "%s/signals_GOOS.h", workdir),
                        bpathf(&b1, "%s/signals_%s.h", bstr(&path), goos), 0);
        }
        
        for(i=0; i<files.len; i++) {
                p = files.p[i];
                elem = lastelem(p);
                for(j=0; j<nelem(gentab); j++) {
                        if(gentab[j].gen == nil)
                                continue;
                        if(hasprefix(elem, gentab[j].nameprefix)) {
                                if(vflag > 1)
                                        errprintf("generate %s\n", p);
                                gentab[j].gen(bstr(&path), p);
                                
                                
                                
                                
                                
                                
                                
                                goto built;
                        }
                }
                
                if(find(p, missing.p, missing.len) >= 0)
                        fatal("missing file %s", p);
        built:;
        }
        
        
        
        if(streq(dir, "pkg/runtime")) {
                copy(bpathf(&b, "%s/zasm_GOOS_GOARCH.h", workdir),
                        bpathf(&b1, "%s/zasm_%s_%s.h", bstr(&path), goos, goarch), 0);
        }
        
        if(streq(dir, "pkg/runtime")) {
                for(i=0; i<files.len; i++) {
                        p = files.p[i];
                        if(!hassuffix(p, ".goc"))
                                continue;
                        
                        bprintf(&b, "%s%sz%s", bstr(&path), slash, lastelem(p));
                        b.len -= 4;
                        bwritef(&b, "_%s_%s.c", goos, goarch);
                        goc2c(p, bstr(&b));
                        vadd(&files, bstr(&b));
                }
                vuniq(&files);
        }
        if((!streq(goos, gohostos) || !streq(goarch, gohostarch)) && isgo) {
                
                if(vflag > 1)
                        errprintf("skip build for cross-compile %s\n", dir);
                goto nobuild;
        }
        
        for(i=0; i<files.len; i++) {
                if(!hassuffix(files.p[i], ".c") && !hassuffix(files.p[i], ".s"))
                        continue;
                name = lastelem(files.p[i]);
                vreset(&compile);
                if(!isgo) {
                        
                        if(streq(gohostos, "plan9")) {
                                vadd(&compile, bprintf(&b, "%sc", gohostchar));
                                vadd(&compile, "-FTVwp");
                                vadd(&compile, "-DPLAN9");
                                vadd(&compile, "-D__STDC__=1");
                                vadd(&compile, "-D__SIZE_TYPE__=ulong"); 
                                vadd(&compile, bpathf(&b, "-I%s/include/plan9", goroot));
                                vadd(&compile, bpathf(&b, "-I%s/include/plan9/%s", goroot, gohostarch));
                        } else {
                                vcopy(&compile, gccargs.p, gccargs.len);
                                vadd(&compile, "-c");
                                if(streq(gohostarch, "amd64"))
                                        vadd(&compile, "-m64");
                                else if(streq(gohostarch, "386"))
                                        vadd(&compile, "-m32");
        
                                vadd(&compile, "-I");
                                vadd(&compile, bpathf(&b, "%s/include", goroot));
                        }
                        if(streq(dir, "lib9"))
                                vadd(&compile, "-DPLAN9PORT");
                        vadd(&compile, "-I");
                        vadd(&compile, bstr(&path));
                        
                        if(streq(name, "goos.c")) {
                                vadd(&compile, "-D");
                                vadd(&compile, bprintf(&b, "GOOS=\"%s\"", goos));
                                vadd(&compile, "-D");
                                vadd(&compile, bprintf(&b, "GOARCH=\"%s\"", goarch));
                                bprintf(&b1, "%s", goroot_final);
                                bsubst(&b1, "\\", "\\\\");  
                                vadd(&compile, "-D");
                                vadd(&compile, bprintf(&b, "GOROOT=\"%s\"", bstr(&b1)));
                                vadd(&compile, "-D");
                                vadd(&compile, bprintf(&b, "GOVERSION=\"%s\"", goversion));
                                vadd(&compile, "-D");
                                vadd(&compile, bprintf(&b, "GOARM=\"%s\"", goarm));
                                vadd(&compile, "-D");
                                vadd(&compile, bprintf(&b, "GO386=\"%s\"", go386));
                                vadd(&compile, "-D");
                                vadd(&compile, bprintf(&b, "GO_EXTLINK_ENABLED=\"%s\"", goextlinkenabled));
                        }
                        
                        if(streq(name, "lex.c")) {
                                xgetenv(&b, "GOEXPERIMENT");
                                vadd(&compile, "-D");
                                vadd(&compile, bprintf(&b1, "GOEXPERIMENT=\"%s\"", bstr(&b)));
                        }
                } else {
                        
                        if(hassuffix(files.p[i], ".s"))
                                vadd(&compile, bpathf(&b, "%s/%sa", tooldir, gochar));
                        else {
                                vadd(&compile, bpathf(&b, "%s/%sc", tooldir, gochar));
                                vadd(&compile, "-F");
                                vadd(&compile, "-V");
                                vadd(&compile, "-w");
                        }
                        vadd(&compile, "-I");
                        vadd(&compile, workdir);
                        vadd(&compile, "-I");
                        vadd(&compile, bprintf(&b, "%s/pkg/%s_%s", goroot, goos, goarch));
                        vadd(&compile, "-D");
                        vadd(&compile, bprintf(&b, "GOOS_%s", goos));
                        vadd(&compile, "-D");
                        vadd(&compile, bprintf(&b, "GOARCH_%s", goarch));
                        vadd(&compile, "-D");
                        vadd(&compile, bprintf(&b, "GOOS_GOARCH_%s_%s", goos, goarch));
                }
                bpathf(&b, "%s/%s", workdir, lastelem(files.p[i]));
                doclean = 1;
                if(!isgo && streq(gohostos, "darwin")) {
                        
                        
                        
                        
                        bpathf(&b1, "%s/pkg/obj/%s", goroot, dir);
                        xmkdirall(bstr(&b1));
                        bpathf(&b, "%s/%s", bstr(&b1), lastelem(files.p[i]));
                        doclean = 0;
                }
                
                if(streq(gohostos, "plan9"))
                        b.p[b.len-1] = gohostchar[0];
                else
                        b.p[b.len-1] = 'o';
                vadd(&compile, "-o");
                vadd(&compile, bstr(&b));
                vadd(&compile, files.p[i]);
                bgrunv(bstr(&path), CheckExit, &compile);
                vadd(&link, bstr(&b));
                if(doclean)
                        vadd(&clean, bstr(&b));
        }
        bgwait();
        if(isgo) {
                
                
                vreset(&compile);
                vadd(&compile, bpathf(&b, "%s/%sg", tooldir, gochar));
                bpathf(&b, "%s/_go_.a", workdir);
                vadd(&compile, "-pack");
                vadd(&compile, "-o");
                vadd(&compile, bstr(&b));
                vadd(&clean, bstr(&b));
                if(!ispackcmd)
                        vadd(&link, bstr(&b));
                vadd(&compile, "-p");
                if(hasprefix(dir, "pkg/"))
                        vadd(&compile, dir+4);
                else
                        vadd(&compile, "main");
                if(streq(dir, "pkg/runtime"))
                        vadd(&compile, "-+");
                vcopy(&compile, go.p, go.len);
                runv(nil, bstr(&path), CheckExit, &compile);
                if(ispackcmd) {
                        xremove(link.p[targ]);
                        dopack(link.p[targ], bstr(&b), &link.p[targ+1], link.len - (targ+1));
                        goto nobuild;
                }
        }
        if(!islib && !isgo) {
                
                vcopy(&link, lib.p, lib.len);
                if(!streq(gohostos, "plan9"))
                        vadd(&link, "-lm");
        }
        
        xremove(link.p[targ]);
        runv(nil, nil, CheckExit, &link);
nobuild:
        
        
        if(streq(dir, "pkg/runtime")) {
                copy(bpathf(&b, "%s/pkg/%s_%s/cgocall.h", goroot, goos, goarch),
                        bpathf(&b1, "%s/src/pkg/runtime/cgocall.h", goroot), 0);
                copy(bpathf(&b, "%s/pkg/%s_%s/runtime.h", goroot, goos, goarch),
                        bpathf(&b1, "%s/src/pkg/runtime/runtime.h", goroot), 0);
        }
out:
        for(i=0; i<clean.len; i++)
                xremove(clean.p[i]);
        bfree(&b);
        bfree(&b1);
        bfree(&path);
        vfree(&compile);
        vfree(&files);
        vfree(&link);
        vfree(&go);
        vfree(&missing);
        vfree(&clean);
        vfree(&lib);
        vfree(&extra);
}
static bool
matchfield(char *f)
{
        char *p;
        bool res;
        p = xstrrchr(f, ',');
        if(p == nil)
                return streq(f, goos) || streq(f, goarch) || streq(f, "cmd_go_bootstrap") || streq(f, "go1.1");
        *p = 0;
        res = matchfield(f) && matchfield(p+1);
        *p = ',';
        return res;
}
static bool
shouldbuild(char *file, char *dir)
{
        char *name, *p;
        int i, j, ret;
        Buf b;
        Vec lines, fields;
        
        
        name = lastelem(file);
        for(i=0; i<nelem(okgoos); i++)
                if(contains(name, okgoos[i]) && !streq(okgoos[i], goos))
                        return 0;
        for(i=0; i<nelem(okgoarch); i++)
                if(contains(name, okgoarch[i]) && !streq(okgoarch[i], goarch))
                        return 0;
        
        if(contains(name, "_test"))
                return 0;
        
        
        
        
        if(hassuffix(file, "cmd/go/doc.go") || hassuffix(file, "cmd\\go\\doc.go"))
                return 0;
        if(hassuffix(file, "cmd/cgo/doc.go") || hassuffix(file, "cmd\\cgo\\doc.go"))
                return 0;
        
        binit(&b);
        vinit(&lines);
        vinit(&fields);
        ret = 1;
        readfile(&b, file);
        splitlines(&lines, bstr(&b));
        for(i=0; i<lines.len; i++) {
                p = lines.p[i];
                while(*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n')
                        p++;
                if(*p == '\0')
                        continue;
                if(contains(p, "package documentation")) {
                        ret = 0;
                        goto out;
                }
                if(contains(p, "package main") && !streq(dir, "cmd/go") && !streq(dir, "cmd/cgo")) {
                        ret = 0;
                        goto out;
                }
                if(!hasprefix(p, "//"))
                        break;
                if(!contains(p, "+build"))
                        continue;
                splitfields(&fields, lines.p[i]);
                if(fields.len < 2 || !streq(fields.p[1], "+build"))
                        continue;
                for(j=2; j<fields.len; j++) {
                        p = fields.p[j];
                        if((*p == '!' && !matchfield(p+1)) || matchfield(p))
                                goto fieldmatch;
                }
                ret = 0;
                goto out;
        fieldmatch:;
        }
out:
        bfree(&b);
        vfree(&lines);
        vfree(&fields);
        return ret;
}
static void
copy(char *dst, char *src, int exec)
{
        Buf b;
        if(vflag > 1)
                errprintf("cp %s %s\n", src, dst);
        binit(&b);
        readfile(&b, src);
        writefile(&b, dst, exec);
        bfree(&b);
}
static void
dopack(char *dst, char *src, char **extra, int nextra)
{
        int i;
        char c, *p, *q;
        Buf b, bdst;
        
        binit(&b);
        binit(&bdst);
        readfile(&bdst, src);
        for(i=0; i<nextra; i++) {
                readfile(&b, extra[i]);
                
                p = xstrrchr(extra[i], '/');
                if(p)
                        p++;
                q = xstrrchr(extra[i], '\\');
                if(q) {
                        q++;
                        if(p == nil || q > p)
                                p = q;
                }
                if(p == nil)
                        p = extra[i];
                bwritef(&bdst, "%-16.16s%-12d%-6d%-6d%-8o%-10d`\n", p, 0, 0, 0, 0644, b.len);
                bwriteb(&bdst, &b);
                if(b.len&1) {
                        c = 0;
                        bwrite(&bdst, &c, 1);
                }
        }
        writefile(&bdst, dst, 0);
        bfree(&b);
        bfree(&bdst);
}
static char *buildorder[] = {
        "lib9",
        "libbio",
        "liblink",
        "misc/pprof",
        "cmd/cc",  
        "cmd/gc",  
        "cmd/%sl",  
        "cmd/%sa",
        "cmd/%sc",
        "cmd/%sg",
        
        
        
        
        "pkg/runtime",
        "pkg/errors",
        "pkg/sync/atomic",
        "pkg/sync",
        "pkg/io",
        "pkg/unicode",
        "pkg/unicode/utf8",
        "pkg/unicode/utf16",
        "pkg/bytes",
        "pkg/math",
        "pkg/strings",
        "pkg/strconv",
        "pkg/bufio",
        "pkg/sort",
        "pkg/container/heap",
        "pkg/encoding/base64",
        "pkg/syscall",
        "pkg/time",
        "pkg/os",
        "pkg/reflect",
        "pkg/fmt",
        "pkg/encoding",
        "pkg/encoding/json",
        "pkg/flag",
        "pkg/path/filepath",
        "pkg/path",
        "pkg/io/ioutil",
        "pkg/log",
        "pkg/regexp/syntax",
        "pkg/regexp",
        "pkg/go/token",
        "pkg/go/scanner",
        "pkg/go/ast",
        "pkg/go/parser",
        "pkg/os/exec",
        "pkg/os/signal",
        "pkg/net/url",
        "pkg/text/template/parse",
        "pkg/text/template",
        "pkg/go/doc",
        "pkg/go/build",
        "cmd/go",
};
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/gc",
        "cmd/go",       
        "lib9",
        "libbio",
        "liblink",
        "pkg/bufio",
        "pkg/bytes",
        "pkg/container/heap",
        "pkg/encoding",
        "pkg/encoding/base64",
        "pkg/encoding/json",
        "pkg/errors",
        "pkg/flag",
        "pkg/fmt",
        "pkg/go/ast",
        "pkg/go/build",
        "pkg/go/doc",
        "pkg/go/parser",
        "pkg/go/scanner",
        "pkg/go/token",
        "pkg/io",
        "pkg/io/ioutil",
        "pkg/log",
        "pkg/math",
        "pkg/net/url",
        "pkg/os",
        "pkg/os/exec",
        "pkg/path",
        "pkg/path/filepath",
        "pkg/reflect",
        "pkg/regexp",
        "pkg/regexp/syntax",
        "pkg/runtime",
        "pkg/sort",
        "pkg/strconv",
        "pkg/strings",
        "pkg/sync",
        "pkg/sync/atomic",
        "pkg/syscall",
        "pkg/text/template",
        "pkg/text/template/parse",
        "pkg/time",
        "pkg/unicode",
        "pkg/unicode/utf16",
        "pkg/unicode/utf8",
};
static void
clean(void)
{
        int i, j, k;
        Buf b, path;
        Vec dir;
        binit(&b);
        binit(&path);
        vinit(&dir);
        for(i=0; i<nelem(cleantab); i++) {
                bpathf(&path, "%s/src/%s", goroot, cleantab[i]);
                xreaddir(&dir, bstr(&path));
                
                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]));
                        }
                }
                
                if(hasprefix(cleantab[i], "cmd/"))
                        xremove(bpathf(&b, "%s/%s", bstr(&path), cleantab[i]+4));
        }
        
        vreset(&dir);
        bpathf(&path, "%s/src/pkg/runtime", goroot);
        xreaddir(&dir, bstr(&path));
        for(j=0; j<dir.len; j++) {
                if(hasprefix(dir.p[j], "z"))
                        xremove(bpathf(&b, "%s/%s", bstr(&path), dir.p[j]));
        }
        if(rebuildall) {
                
                xremoveall(bpathf(&b, "%s/pkg/obj/%s_%s", goroot, gohostos, gohostarch));
                
                xremoveall(bpathf(&b, "%s/pkg/%s_%s", goroot, gohostos, gohostarch));
                xremoveall(bpathf(&b, "%s/pkg/%s_%s", goroot, goos, goarch));
                xremoveall(tooldir);
                
                xremove(bpathf(&b, "%s/VERSION.cache", goroot));
        }
        bfree(&b);
        bfree(&path);
        vfree(&dir);
}
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);
}
void
cmdenv(int argc, char **argv)
{
        bool pflag;
        char *sep;
        Buf b, b1;
        char *format;
        binit(&b);
        binit(&b1);
        format = "%s=\"%s\"\n";
        pflag = 0;
        ARGBEGIN{
        case '9':
                format = "%s='%s'\n";
                break;
        case 'p':
                pflag = 1;
                break;
        case 'v':
                vflag++;
                break;
        case 'w':
                format = "set %s=%s\r\n";
                break;
        default:
                usage();
        }ARGEND
        if(argc > 0)
                usage();
        xprintf(format, "CC", defaultcc);
        xprintf(format, "CC_FOR_TARGET", defaultcctarget);
        xprintf(format, "GOROOT", goroot);
        xprintf(format, "GOBIN", gobin);
        xprintf(format, "GOARCH", goarch);
        xprintf(format, "GOOS", goos);
        xprintf(format, "GOHOSTARCH", gohostarch);
        xprintf(format, "GOHOSTOS", gohostos);
        xprintf(format, "GOTOOLDIR", tooldir);
        xprintf(format, "GOCHAR", gochar);
        if(streq(goarch, "arm"))
                xprintf(format, "GOARM", goarm);
        if(streq(goarch, "386"))
                xprintf(format, "GO386", go386);
        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);
}
void
cmdbootstrap(int argc, char **argv)
{
        int i;
        Buf b;
        char *oldgoos, *oldgoarch, *oldgochar;
        binit(&b);
        ARGBEGIN{
        case 'a':
                rebuildall = 1;
                break;
        case 's':
                sflag++;
                break;
        case 'v':
                vflag++;
                break;
        default:
                usage();
        }ARGEND
        if(argc > 0)
                usage();
        if(rebuildall)
                clean();
        goversion = findgoversion();
        setup();
        xsetenv("GOROOT", goroot);
        xsetenv("GOROOT_FINAL", goroot_final);
        
        oldgoos = goos;
        oldgoarch = goarch;
        oldgochar = gochar;
        goos = gohostos;
        goarch = gohostarch;
        gochar = gohostchar;
        xsetenv("GOARCH", goarch);
        xsetenv("GOOS", goos);
        for(i=0; i<nelem(buildorder); i++) {
                install(bprintf(&b, buildorder[i], gohostchar));
                if(!streq(oldgochar, gohostchar) && xstrstr(buildorder[i], "%s"))
                        install(bprintf(&b, buildorder[i], oldgochar));
        }
        goos = oldgoos;
        goarch = oldgoarch;
        gochar = oldgochar;
        xsetenv("GOARCH", goarch);
        xsetenv("GOOS", goos);
        
        if(!streq(goos, gohostos) || !streq(goarch, gohostarch))
                install("pkg/runtime");
        bfree(&b);
}
static char*
defaulttarg(void)
{
        char *p;
        Buf pwd, src, real_src;
        binit(&pwd);
        binit(&src);
        binit(&real_src);
        
        
        
        
        xgetwd(&pwd);
        p = btake(&pwd);
        bpathf(&src, "%s/src/", goroot);
        xrealwd(&real_src, bstr(&src));
        if(!hasprefix(p, bstr(&real_src)))
                fatal("current directory %s is not under %s", p, bstr(&real_src));
        p += real_src.len;
        
        if(*p == slash[0])
                p++;
        bfree(&pwd);
        bfree(&src);
        bfree(&real_src);
        return p;
}
void
cmdinstall(int argc, char **argv)
{
        int i;
        ARGBEGIN{
        case 's':
                sflag++;
                break;
        case 'v':
                vflag++;
                break;
        default:
                usage();
        }ARGEND
        if(argc == 0)
                install(defaulttarg());
        for(i=0; i<argc; i++)
                install(argv[i]);
}
void
cmdclean(int argc, char **argv)
{
        ARGBEGIN{
        case 'v':
                vflag++;
                break;
        default:
                usage();
        }ARGEND
        if(argc > 0)
                usage();
        clean();
}
void
cmdbanner(int argc, char **argv)
{
        char *pathsep, *pid, *ns;
        Buf b, b1, search, path;
        ARGBEGIN{
        case 'v':
                vflag++;
                break;
        default:
                usage();
        }ARGEND
        if(argc > 0)
                usage();
        binit(&b);
        binit(&b1);
        binit(&search);
        binit(&path);
        xprintf("\n");
        xprintf("---\n");
        xprintf("Installed Go for %s/%s in %s\n", goos, goarch, goroot);
        xprintf("Installed commands in %s\n", gobin);
        if(!xsamefile(goroot_final, goroot)) {
                
                
        } else if(streq(gohostos, "plan9")) {
                
                readfile(&b, "#c/pid");
                bsubst(&b, " ", "");
                pid = btake(&b);
                bprintf(&b, "/proc/%s/ns", pid);
                ns = btake(&b);
                readfile(&b, ns);
                bprintf(&search, "bind -b %s /bin\n", gobin);
                if(xstrstr(bstr(&b), bstr(&search)) == nil)
                        xprintf("*** You need to bind %s before /bin.\n", gobin);
        } else {
                
                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")) {
                if(isfile(bpathf(&path, "%s/cov", tooldir)))
                        xprintf("\n"
                                "On OS X the debuggers must be installed setgid procmod.\n"
                                "Read and run ./sudo.bash to install the debuggers.\n");
        }
        if(!xsamefile(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);
        bfree(&path);
}
void
cmdversion(int argc, char **argv)
{
        ARGBEGIN{
        case 'v':
                vflag++;
                break;
        default:
                usage();
        }ARGEND
        if(argc > 0)
                usage();
        xprintf("%s\n", goversion);
}