// Copyright 2012 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. #include "a.h" #include "arg.h" /* * Initialization for any invocation. */ // The usual variables. char *goarch; char *gobin; char *gohostarch; char *gohostos; char *goos; char *goroot = GOROOT_FINAL; char *goroot_final = GOROOT_FINAL; char *workdir; char *gochar; char *goversion; char *slash; // / for unix, \ for windows static bool shouldbuild(char*, char*); static void copy(char*, char*); static char *findgoversion(void); // The known architecture letters. static char *gochars = "568"; // The known architectures. static char *okgoarch[] = { // same order as gochars "arm", "amd64", "386", }; // The known operating systems. static char *okgoos[] = { "darwin", "linux", "freebsd", "netbsd", "openbsd", "plan9", "windows", }; static void rmworkdir(void); // find reports the first index of p in l[0:n], or else -1. int find(char *p, char **l, int n) { int i; for(i=0; i 0) goroot = btake(&b); xgetenv(&b, "GOBIN"); if(b.len == 0) bprintf(&b, "%s%sbin", goroot, slash); gobin = btake(&b); xgetenv(&b, "GOOS"); if(b.len == 0) bwritestr(&b, gohostos); goos = btake(&b); if(find(goos, okgoos, nelem(okgoos)) < 0) fatal("unknown $GOOS %s", goos); p = bpathf(&b, "%s/include/u.h", goroot); if(!isfile(p)) { fatal("$GOROOT is not set correctly or not exported\n" "\tGOROOT=%s\n" "\t%s does not exist", goroot, p); } xgetenv(&b, "GOHOSTARCH"); if(b.len > 0) gohostarch = btake(&b); if(find(gohostarch, okgoarch, nelem(okgoarch)) < 0) fatal("unknown $GOHOSTARCH %s", gohostarch); xgetenv(&b, "GOARCH"); if(b.len == 0) bwritestr(&b, gohostarch); goarch = btake(&b); if((i=find(goarch, okgoarch, nelem(okgoarch))) < 0) fatal("unknown $GOARCH %s", goarch); bprintf(&b, "%c", gochars[i]); gochar = btake(&b); xsetenv("GOROOT", goroot); xsetenv("GOARCH", goarch); xsetenv("GOOS", goos); // Make the environment more predictable. xsetenv("LANG", "C"); xsetenv("LANGUAGE", "en_US.UTF8"); goversion = findgoversion(); workdir = xworkdir(); xatexit(rmworkdir); bfree(&b); } // rmworkdir deletes the work directory. static void rmworkdir(void) { if(vflag > 1) xprintf("rm -rf %s\n", workdir); xremoveall(workdir); } // Remove trailing spaces. static void chomp(Buf *b) { int c; while(b->len > 0 && ((c=b->p[b->len-1]) == ' ' || c == '\t' || c == '\r' || c == '\n')) b->len--; } // findgoversion determines the Go version to use in the version string. static char* findgoversion(void) { char *tag, *rev, *p; int i, nrev; Buf b, path, bmore, branch; Vec tags; binit(&b); binit(&path); binit(&bmore); binit(&branch); vinit(&tags); // The $GOROOT/VERSION file takes priority, for distributions // without the Mercurial repo. bpathf(&path, "%s/VERSION", goroot); if(isfile(bstr(&path))) { readfile(&b, bstr(&path)); chomp(&b); // Commands such as "dist version > VERSION" will cause // the shell to create an empty VERSION file and set dist's // stdout to its fd. dist in turn looks at VERSION and uses // its content if available, which is empty at this point. if(b.len > 0) goto done; } // The $GOROOT/VERSION.cache file is a cache to avoid invoking // hg every time we run this command. Unlike VERSION, it gets // deleted by the clean command. bpathf(&path, "%s/VERSION.cache", goroot); if(isfile(bstr(&path))) { readfile(&b, bstr(&path)); chomp(&b); goto done; } // Otherwise, use Mercurial. // What is the current branch? run(&branch, goroot, CheckExit, "hg", "identify", "-b", nil); chomp(&branch); // What are the tags along the current branch? tag = ""; rev = "."; run(&b, goroot, CheckExit, "hg", "log", "-b", bstr(&branch), "--template", "{tags} + ", nil); splitfields(&tags, bstr(&b)); nrev = 0; for(i=0; i 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. */ // The old tools that no longer live in $GOBIN or $GOROOT/bin. static char *oldtool[] = { "5a", "5c", "5g", "5l", "6a", "6c", "6g", "6l", "8a", "8c", "8g", "8l", "6cov", "6nm", "cgo", "ebnflint", "goapi", "gofix", "goinstall", "gomake", "gopack", "gopprof", "gotest", "gotype", "govet", "goyacc", "quietgcc", }; // setup sets up the tree for the initial build. static void setup(void) { int i; Buf b; char *p; binit(&b); // Create tool directory. p = bpathf(&b, "%s/bin", goroot); if(!isdir(p)) xmkdir(p); p = bpathf(&b, "%s/bin/tool", goroot); if(!isdir(p)) xmkdir(p); // Create package directory. p = bpathf(&b, "%s/pkg", goroot); if(!isdir(p)) xmkdir(p); p = bpathf(&b, "%s/pkg/%s_%s", goroot, 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); // Remove old pre-tool binaries. for(i=0; i ttarg) stale = 1; if(t == 0) { vadd(&missing, p); files.p[n++] = files.p[i]; continue; } files.p[n++] = files.p[i]; } files.len = n; for(i=0; i ttarg) stale = 1; if(!stale) goto out; // For package runtime, copy some files into the work space. if(streq(dir, "pkg/runtime")) { copy(bpathf(&b, "%s/arch_GOARCH.h", workdir), bpathf(&b1, "%s/arch_%s.h", bstr(&path), goarch)); 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)); } // For cmd/prof, copy pprof into the tool directory. if(streq(dir, "cmd/prof")) { copy(bpathf(&b, "%s/bin/tool/pprof", goroot), bpathf(&b1, "%s/src/cmd/prof/pprof", goroot)); } // Generate any missing files; regenerate existing ones. for(i=0; i 1) xprintf("generate %s\n", p); gentab[j].gen(bstr(&path), p); // Do not add generated file to clean list. // In pkg/runtime, we want to be able to // build the package with the go tool, // and it assumes these generated files already // exist (it does not know how to build them). // The 'clean' command can remove // the generated files. goto built; } } // Did not rebuild p. if(find(p, missing.p, missing.len) >= 0) fatal("missing file %s", p); built:; } // One more copy for package runtime. // The last batch was required for the generators. // This one is generated. if(streq(dir, "pkg/runtime")) { copy(bpathf(&b, "%s/zasm_GOOS_GOARCH.h", workdir), bpathf(&b1, "%s/zasm_%s_%s.h", bstr(&path), goos, goarch)); } // Generate .c files from .goc files. if(streq(dir, "pkg/runtime")) { for(i=0; i 1) xprintf("cp %s %s\n", src, dst); binit(&b); readfile(&b, src); writefile(&b, dst); bfree(&b); } // buildorder records the order of builds for the 'go bootstrap' command. static char *buildorder[] = { "lib9", "libbio", "libmach", "cmd/cov", "cmd/nm", "cmd/pack", "cmd/prof", "cmd/cc", // must be before c "cmd/gc", // must be before g "cmd/%sl", // must be before a, c, g "cmd/%sa", "cmd/%sc", "cmd/%sg", // The dependency order here was copied from a buildscript // back when there were build scripts. Will have to // be maintained by hand, but shouldn't change very // often. "pkg/runtime", "pkg/errors", "pkg/sync/atomic", "pkg/sync", "pkg/io", "pkg/unicode", "pkg/unicode/utf8", "pkg/unicode/utf16", "pkg/bytes", "pkg/math", "pkg/strings", "pkg/strconv", "pkg/bufio", "pkg/sort", "pkg/container/heap", "pkg/encoding/base64", "pkg/syscall", "pkg/time", "pkg/os", "pkg/reflect", "pkg/fmt", "pkg/encoding/json", "pkg/flag", "pkg/path/filepath", "pkg/path", "pkg/io/ioutil", "pkg/log", "pkg/regexp/syntax", "pkg/regexp", "pkg/go/token", "pkg/go/scanner", "pkg/go/ast", "pkg/go/parser", "pkg/go/build", "pkg/os/exec", "pkg/net/url", "pkg/text/template/parse", "pkg/text/template", "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/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 0) usage(); xprintf(format, "GOROOT", goroot); xprintf(format, "GOBIN", gobin); 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 cmdbootstrap(int argc, char **argv) { int i; Buf b; ARGBEGIN{ case 'v': vflag++; break; default: usage(); }ARGEND if(argc > 0) usage(); clean(); goversion = findgoversion(); setup(); binit(&b); for(i=0; i 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", goversion); }