// 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 *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; // / for unix, \ for windows char *defaultcc; char *defaultcflags; char *defaultldflags; char *defaultcxxtarget; char *defaultcctarget; bool rebuildall; bool defaultclang; static bool shouldbuild(char*, char*); static void dopack(char*, char*, char**, int); static char *findgoversion(void); // The known architecture letters. static char *gochars = "5668"; // The known architectures. static char *okgoarch[] = { // same order as gochars "arm", "amd64", "amd64p32", "386", }; // The known operating systems. static char *okgoos[] = { "darwin", "dragonfly", "linux", "android", "solaris", "freebsd", "nacl", "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) { // if not "/", then strip trailing path separator 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) { // Use clang on OS X, because gcc is deprecated there. // Xcode for OS X 10.9 Mavericks will ship a fake "gcc" binary that // actually runs clang. We prepare different command // lines for the two binaries, so it matters what we call it. // See golang.org/issue/5822. 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); // Make the environment more predictable. 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); } // rmworkdir deletes the work directory. static void rmworkdir(void) { if(vflag > 1) errprintf("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--; } static char* branchtag(char *branch, bool *precise) { char *tag, *p, *q; int i; Buf b, arg; Vec tags; binit(&b); binit(&arg); vinit(&tags); bprintf(&arg, "master..%s", branch); run(&b, goroot, CheckExit, "git", "log", "--decorate=full", "--format=format:%d", bstr(&arg), nil); splitlines(&tags, bstr(&b)); tag = branch; for(i=0; i < tags.len; i++) { // Each line is either blank, or looks like // (tag: refs/tags/go1.4rc2, refs/remotes/origin/release-branch.go1.4, refs/heads/release-branch.go1.4) // We need to find an element starting with refs/tags/. p = xstrstr(tags.p[i], " refs/tags/"); if(p == nil) continue; p += xstrlen(" refs/tags/"); // The tag name ends at a comma or paren (prefer the first). q = xstrstr(p, ","); if(q == nil) q = xstrstr(p, ")"); if(q == nil) continue; // malformed line; ignore it *q = '\0'; tag = xstrdup(p); if(i == 0) *precise = 1; // tag denotes HEAD break; } bfree(&b); bfree(&arg); vfree(&tags); return tag; } // findgoversion determines the Go version to use in the version string. static char* findgoversion(void) { char *tag, *p; bool precise; Buf b, path, bmore, branch; binit(&b); binit(&path); binit(&bmore); binit(&branch); // The $GOROOT/VERSION file takes priority, for distributions // without the source 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 // git 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 Git. // What is the current branch? run(&branch, goroot, CheckExit, "git", "rev-parse", "--abbrev-ref", "HEAD", nil); chomp(&branch); // What are the tags along the current branch? tag = "devel"; precise = 0; // If we're on a release branch, use the closest matching tag // that is on the release branch (and not on the master branch). if(hasprefix(bstr(&branch), "release-branch.")) tag = branchtag(bstr(&branch), &precise); bprintf(&b, "%s", tag); if(!precise) { // Tag does not point at HEAD; add hash and date to version. run(&bmore, goroot, CheckExit, "git", "log", "-n", "1", "--format=format: +%h %cd", "HEAD", nil); chomp(&bmore); bwriteb(&b, &bmore); } // Cache version. writefile(&b, bstr(&path), 0); done: p = btake(&b); bfree(&b); bfree(&path); bfree(&bmore); bfree(&branch); 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", "6prof", "cgo", "ebnflint", "goapi", "gofix", "goinstall", "gomake", "gopack", "gopprof", "gotest", "gotype", "govet", "goyacc", "quietgcc", }; // Unreleased directories (relative to $GOROOT) that should // not be in release branches. static char *unreleased[] = { "src/cmd/link", "src/debug/goobj", "src/old", }; // setup sets up the tree for the initial build. static void setup(void) { int i; Buf b; char *p; binit(&b); // Create bin directory. p = bpathf(&b, "%s/bin", 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, 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); } // Create object directory. // We keep it in pkg/ so that all the generated binaries // are in one tree. If pkg/obj/libgc.a exists, it is a dreg from // before we used subdirectories of obj. Delete all of obj // to clean up. 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); // Create tool directory. // We keep it in pkg/, just like the object directory above. if(rebuildall) xremoveall(tooldir); xmkdirall(tooldir); // Remove tool binaries from before the tool/gohostos_gohostarch xremoveall(bpathf(&b, "%s/bin/tool", goroot)); // 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; // If there are no files to compile, we're done. if(files.len == 0) goto out; for(i=0; i ttarg) stale = 1; if(!stale) goto out; // For package runtime, copy some files into the work space. if(streq(dir, "runtime")) { copyfile(bpathf(&b, "%s/arch_GOARCH.h", workdir), bpathf(&b1, "%s/arch_%s.h", bstr(&path), goarch), 0); copyfile(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)) copyfile(bpathf(&b, "%s/signal_GOOS_GOARCH.h", workdir), p, 0); copyfile(bpathf(&b, "%s/os_GOOS.h", workdir), bpathf(&b1, "%s/os_%s.h", bstr(&path), goos), 0); copyfile(bpathf(&b, "%s/signals_GOOS.h", workdir), bpathf(&b1, "%s/signals_%s.h", bstr(&path), goos), 0); copyfile(bpathf(&b, "%s/pkg/%s_%s/textflag.h", goroot, goos, goarch), bpathf(&b1, "%s/src/cmd/ld/textflag.h", goroot), 0); copyfile(bpathf(&b, "%s/pkg/%s_%s/funcdata.h", goroot, goos, goarch), bpathf(&b1, "%s/src/runtime/funcdata.h", goroot), 0); } // Generate any missing files; regenerate existing ones. for(i=0; i 1) errprintf("generate %s\n", p); gentab[j].gen(bstr(&path), p); // Do not add generated file to clean list. // In 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, "runtime")) { copyfile(bpathf(&b, "%s/zasm_GOOS_GOARCH.h", workdir), bpathf(&b1, "%s/zasm_%s_%s.h", bstr(&path), goos, goarch), 0); } if((!streq(goos, gohostos) || !streq(goarch, gohostarch)) && isgo) { // We've generated the right files; the go command can do the build. if(vflag > 1) errprintf("skip build for cross-compile %s\n", dir); goto nobuild; } // Compile the files. for(i=0; i 1) errprintf("cp %s %s\n", src, dst); binit(&b); readfile(&b, src); writefile(&b, dst, exec); bfree(&b); } // dopack copies the package src to dst, // appending the files listed in extra. // The archive format is the traditional Unix ar format. 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 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); } // buildorder records the order of builds for the 'go bootstrap' command. static char *buildorder[] = { "lib9", "libbio", "liblink", "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. "runtime", "errors", "sync/atomic", "sync", "io", "unicode", "unicode/utf8", "unicode/utf16", "bytes", "math", "strings", "strconv", "bufio", "sort", "container/heap", "encoding/base64", "syscall", "time", "os", "reflect", "fmt", "encoding", "encoding/json", "flag", "path/filepath", "path", "io/ioutil", "log", "regexp/syntax", "regexp", "go/token", "go/scanner", "go/ast", "go/parser", "os/exec", "os/signal", "net/url", "text/template/parse", "text/template", "go/doc", "go/build", "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[] = { // Commands and C libraries. "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", // Go packages. "bufio", "bytes", "container/heap", "encoding", "encoding/base64", "encoding/json", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "io", "io/ioutil", "log", "math", "net/url", "os", "os/exec", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "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, "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); } // 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; 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(isdir(bpathf(&b, "%s/src/pkg", goroot))) { fatal("\n\n" "The Go package sources have moved to $GOROOT/src.\n" "*** %s still exists. ***\n" "It probably contains stale files that may confuse the build.\n" "Please (check what's there and) remove it and try again.\n" "See http://golang.org/s/go14nopkg\n", bpathf(&b, "%s/src/pkg", goroot)); } if(rebuildall) clean(); goversion = findgoversion(); setup(); xsetenv("GOROOT", goroot); xsetenv("GOROOT_FINAL", goroot_final); // For the main bootstrap, building for host os/arch. oldgoos = goos; oldgoarch = goarch; oldgochar = gochar; goos = gohostos; goarch = gohostarch; gochar = gohostchar; xsetenv("GOARCH", goarch); xsetenv("GOOS", goos); for(i=0; i 0) usage(); clean(); } // Banner prints the 'now you've installed Go' banner. 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)) { // If the files are to be moved, don't check that gobin // is on PATH; assume they know what they are doing. } else if(streq(gohostos, "plan9")) { // Check that gobin is bound before /bin. 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 { // 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")) { 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); } // 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); }