diff options
-rw-r--r-- | src/cmd/dist/README | 62 | ||||
-rw-r--r-- | src/cmd/dist/build.go | 2030 | ||||
-rw-r--r-- | src/cmd/dist/buildgc.go | 304 | ||||
-rw-r--r-- | src/cmd/dist/buildgo.go | 48 | ||||
-rw-r--r-- | src/cmd/dist/buildruntime.go | 68 | ||||
-rw-r--r-- | src/cmd/dist/cpuid_386.s | 14 | ||||
-rw-r--r-- | src/cmd/dist/cpuid_amd64.s | 14 | ||||
-rw-r--r-- | src/cmd/dist/cpuid_default.s | 10 | ||||
-rw-r--r-- | src/cmd/dist/main.go | 89 | ||||
-rw-r--r-- | src/cmd/dist/sys_default.go | 10 | ||||
-rw-r--r-- | src/cmd/dist/sys_windows.go | 49 | ||||
-rw-r--r-- | src/cmd/dist/util.go | 1132 | ||||
-rw-r--r-- | src/cmd/dist/vfp_arm.s | 15 | ||||
-rw-r--r-- | src/cmd/dist/vfp_default.s | 14 | ||||
-rwxr-xr-x | src/make.bash | 26 | ||||
-rwxr-xr-x | src/sudo.bash | 41 |
16 files changed, 1632 insertions, 2294 deletions
diff --git a/src/cmd/dist/README b/src/cmd/dist/README index e6d08cf028..0649e887f4 100644 --- a/src/cmd/dist/README +++ b/src/cmd/dist/README @@ -1,45 +1,27 @@ This program, dist, is the bootstrapping tool for the Go distribution. -It takes care of building the C programs (like the Go compiler) and -the initial bootstrap copy of the go tool. It also serves as a catch-all -to replace odd jobs previously done with shell scripts. -Dist is itself written in very simple C. All interaction with C libraries, -even standard C libraries, is confined to a single system-specific file -(plan9.c, unix.c, windows.c), to aid portability. Functionality needed -by other files should be exposed via the portability layer. Functions -in the portability layer begin with an x prefix when they would otherwise -use the same name as or be confused for an existing function. -For example, xprintf is the portable printf. +As of Go 1.5, dist and other parts of the compiler toolchain are written +in Go, making bootstrapping a little more involved than in the past. +The approach is to build the current release of Go with an earlier one. -By far the most common data types in dist are strings and arrays of -strings. Instead of using char* and char**, though, dist uses two named -data structures, Buf and Vec, which own all the data they point at. -The Buf operations are functions beginning with b; the Vec operations -are functions beginning with v. The basic form of any function declaring -Bufs or Vecs on the stack should be +The process to install Go 1.x, for x ≥ 5, is: - void - myfunc(void) - { - Buf b1, b2; - Vec v1; - - binit(&b1); - binit(&b2); - vinit(&v1); - - ... main code ... - bprintf(&b1, "hello, world"); - vadd(&v1, bstr(&b1)); // v1 takes a copy of its argument - bprintf(&b2, "another string"); - vadd(&v1, bstr(&b2)); // v1 now has two strings - - bfree(&b1); - bfree(&b2); - vfree(&v1); - } - -The binit/vinit calls prepare a buffer or vector for use, initializing the -data structures, and the bfree/vfree calls free any memory they are still -holding onto. Use of this idiom gives us lexically scoped allocations. +1. Build cmd/dist with Go 1.4. +2. Using dist, build Go 1.x compiler toolchain with Go 1.4. +3. Using dist, rebuild Go 1.x compiler toolchain with itself. +4. Using dist, build Go 1.x cmd/go (as go_bootstrap) with Go 1.x compiler toolchain. +5. Using go_bootstrap, build the remaining Go 1.x standard library and commands. +NOTE: During the transition from the old C-based toolchain to the Go-based one, +step 2 also builds the parts of the toolchain written in C, and step 3 does not +recompile those. + +Because of backward compatibility, although the steps above say Go 1.4, +in practice any release ≥ Go 1.4 but < Go 1.x will work as the bootstrap base. + +See golang.org/s/go15bootstrap for more details. + +Compared to Go 1.4 and earlier, dist will also take over much of what used to +be done by make.bash/make.bat/make.rc and all of what used to be done by +run.bash/run.bat/run.rc, because it is nicer to implement that logic in Go +than in three different scripting languages simultaneously. diff --git a/src/cmd/dist/build.go b/src/cmd/dist/build.go index e4b8b58e43..9e4d1e3c22 100644 --- a/src/cmd/dist/build.go +++ b/src/cmd/dist/build.go @@ -2,47 +2,58 @@ // 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" +package main -/* - * Initialization for any invocation. - */ +import ( + "bytes" + "flag" + "fmt" + "os" + "path/filepath" + "runtime" + "strings" +) + +// 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); +var ( + goarch string + gobin string + gohostarch string + gohostchar string + gohostos string + goos string + goarm string + go386 string + goroot string + goroot_final string + goextlinkenabled string + workdir string + tooldir string + gochar string + goversion string + oldgoos string + oldgoarch string + oldgochar string + slash string + defaultcc string + defaultcflags string + defaultldflags string + defaultcxxtarget string + defaultcctarget string + rebuildall bool + defaultclang bool + + sflag bool // build static binaries + vflag int // verbosity +) // The known architecture letters. -static char *gochars = "566899"; +var gochars = "566899" // The known architectures. -static char *okgoarch[] = { +var okgoarch = []string{ // same order as gochars "arm", "amd64", @@ -50,10 +61,10 @@ static char *okgoarch[] = { "386", "ppc64", "ppc64le", -}; +} // The known operating systems. -static char *okgoos[] = { +var okgoos = []string{ "darwin", "dragonfly", "linux", @@ -65,294 +76,252 @@ static char *okgoos[] = { "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<n; i++) - if(streq(p, l[i])) - return i; - return -1; +func find(p string, l []string) int { + for i, s := range l { + if p == s { + return i + } + } + return -1 } -// init handles initialization of the various global state, like goroot and goarch. -void -init(void) -{ - char *p; - int i; - Buf b; - - binit(&b); - - xgetenv(&b, "GOROOT"); - if(b.len > 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) { +// xinit handles initialization of the various global state, like goroot and goarch. +func xinit() { + goroot = os.Getenv("GOROOT") + if slash == "/" && len(goroot) > 1 || slash == `\` && len(goroot) > 3 { + // if not "/" or "c:\", then strip trailing path separator + goroot = strings.TrimSuffix(goroot, slash) + } + if goroot == "" { + fatal("$GOROOT must be set") + } + + goroot_final = os.Getenv("GOROOT_FINAL") + if goroot_final == "" { + goroot_final = goroot + } + + b := os.Getenv("GOBIN") + if b == "" { + b = goroot + slash + "bin" + } + gobin = b + + b = os.Getenv("GOOS") + if b == "" { + b = gohostos + } + goos = b + if find(goos, okgoos) < 0 { + fatal("unknown $GOOS %s", goos) + } + + b = os.Getenv("GOARM") + if b == "" { + b = xgetgoarm() + } + goarm = b + + b = os.Getenv("GO386") + if b == "" { + if cansse2() { + b = "sse2" + } else { + b = "387" + } + } + go386 = b + + p := pathf("%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) + } + + b = os.Getenv("GOHOSTARCH") + if b != "" { + gohostarch = b + } + + i := find(gohostarch, okgoarch) + if i < 0 { + fatal("unknown $GOHOSTARCH %s", gohostarch) + } + gohostchar = gochars[i : i+1] + + b = os.Getenv("GOARCH") + if b == "" { + b = gohostarch + } + goarch = b + i = find(goarch, okgoarch) + if i < 0 { + fatal("unknown $GOARCH %s", goarch) + } + gochar = gochars[i : i+1] + + b = os.Getenv("GO_EXTLINK_ENABLED") + if b != "" { + if b != "0" && b != "1" { + fatal("unknown $GO_EXTLINK_ENABLED %s", b) + } + goextlinkenabled = b + } + + b = os.Getenv("CC") + if b == "" { // 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"); + if defaultclang { + b = "clang" + } else { + b = "gcc" + } } - defaultcc = btake(&b); + defaultcc = b - xgetenv(&b, "CFLAGS"); - defaultcflags = btake(&b); + defaultcflags = os.Getenv("CFLAGS") - xgetenv(&b, "LDFLAGS"); - defaultldflags = btake(&b); + defaultldflags = os.Getenv("LDFLAGS") - xgetenv(&b, "CC_FOR_TARGET"); - if(b.len == 0) { - bprintf(&b, defaultcc); + b = os.Getenv("CC_FOR_TARGET") + if b == "" { + 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++"); + defaultcctarget = b + + b = os.Getenv("CXX_FOR_TARGET") + if b == "" { + b = os.Getenv("CXX") + if b == "" { + if defaultclang { + b = "clang++" + } else { + b = "g++" + } } } - defaultcxxtarget = btake(&b); - - xsetenv("GOROOT", goroot); - xsetenv("GOARCH", goarch); - xsetenv("GOOS", goos); - xsetenv("GOARM", goarm); - xsetenv("GO386", go386); + defaultcxxtarget = b + + // For tools being invoked but also for os.ExpandEnv. + os.Setenv("GO386", go386) + os.Setenv("GOARCH", goarch) + os.Setenv("GOARM", goarm) + os.Setenv("GOHOSTARCH", gohostarch) + os.Setenv("GOHOSTOS", gohostos) + os.Setenv("GOOS", goos) + os.Setenv("GOROOT", goroot) + os.Setenv("GOROOT_FINAL", goroot_final) // Make the environment more predictable. - xsetenv("LANG", "C"); - xsetenv("LANGUAGE", "en_US.UTF8"); - - goversion = findgoversion(); + os.Setenv("LANG", "C") + os.Setenv("LANGUAGE", "en_US.UTF8") - workdir = xworkdir(); - xatexit(rmworkdir); + goversion = findgoversion() - bpathf(&b, "%s/pkg/tool/%s_%s", goroot, gohostos, gohostarch); - tooldir = btake(&b); + workdir = xworkdir() + xatexit(rmworkdir) - bfree(&b); + tooldir = pathf("%s/pkg/tool/%s_%s", goroot, gohostos, gohostarch) } // rmworkdir deletes the work directory. -static void -rmworkdir(void) -{ - if(vflag > 1) - errprintf("rm -rf %s\n", workdir); - xremoveall(workdir); +func rmworkdir() { + 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--; +func chomp(s string) string { + return strings.TrimRight(s, " \t\r\n") } -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++) { +func branchtag(branch string) (tag string, precise bool) { + b := run(goroot, CheckExit, "git", "log", "--decorate=full", "--format=format:%d", "master.."+branch) + tag = branch + for _, line := range splitlines(b) { // 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/"); + i := strings.Index(line, " refs/tags/") + if i < 0 { + continue + } + i += len(" 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; + j := strings.Index(line[i:], ",") + if j < 0 { + j = strings.Index(line[i:], ")") + } + if j < 0 { + continue // malformed line; ignore it + } + tag = line[i : i+j] + if i == 0 { + precise = true // tag denotes HEAD + } + break + } + return } // 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); - +func findgoversion() string { // 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); + path := pathf("%s/VERSION", goroot) + if isfile(path) { + b := chomp(readfile(path)) // 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; + // Only use the VERSION file if it is non-empty. + if b != "" { + return b + } } // 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; + path = pathf("%s/VERSION.cache", goroot) + if isfile(path) { + return chomp(readfile(path)) } // Otherwise, use Git. // What is the current branch? - run(&branch, goroot, CheckExit, "git", "rev-parse", "--abbrev-ref", "HEAD", nil); - chomp(&branch); + branch := chomp(run(goroot, CheckExit, "git", "rev-parse", "--abbrev-ref", "HEAD")) // What are the tags along the current branch? - tag = "devel"; - precise = 0; + tag := "devel" + precise := false // 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); + if strings.HasPrefix(branch, "release-branch.") { + tag, precise = branchtag(branch) + } - bprintf(&b, "%s", tag); - if(!precise) { + 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); + tag += chomp(run(goroot, CheckExit, "git", "log", "-n", "1", "--format=format: +%h %cd", "HEAD")) } // Cache version. - writefile(&b, bstr(&path), 0); - -done: - p = btake(&b); + writefile(tag, path, 0) - - bfree(&b); - bfree(&path); - bfree(&bmore); - bfree(&branch); - - return p; + return tag } /* @@ -360,7 +329,7 @@ done: */ // The old tools that no longer live in $GOBIN or $GOROOT/bin. -static char *oldtool[] = { +var oldtool = []string{ "5a", "5c", "5g", "5l", "6a", "6c", "6g", "6l", "8a", "8c", "8g", "8l", @@ -381,44 +350,40 @@ static char *oldtool[] = { "govet", "goyacc", "quietgcc", -}; +} // Unreleased directories (relative to $GOROOT) that should // not be in release branches. -static char *unreleased[] = { +var unreleased = []string{ "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); - +func setup() { // Create bin directory. - p = bpathf(&b, "%s/bin", goroot); - if(!isdir(p)) - xmkdir(p); + if p := pathf("%s/bin", goroot); !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); + if p := pathf("%s/pkg", goroot); !isdir(p) { + xmkdir(p) + } + + p := pathf("%s/pkg/%s_%s", goroot, gohostos, gohostarch) + if rebuildall { + xremoveall(p) + } + xmkdirall(p) + + if goos != gohostos || goarch != gohostarch { + p := pathf("%s/pkg/%s_%s", goroot, goos, goarch) + if rebuildall { + xremoveall(p) + } + xmkdirall(p) } // Create object directory. @@ -426,44 +391,48 @@ setup(void) // 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); + if p := pathf("%s/pkg/obj/libgc.a", goroot); isfile(p) { + xremoveall(pathf("%s/pkg/obj", goroot)) + } + p = pathf("%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); + if rebuildall { + xremoveall(tooldir) + } + xmkdirall(tooldir) // Remove tool binaries from before the tool/gohostos_gohostarch - xremoveall(bpathf(&b, "%s/bin/tool", goroot)); + xremoveall(pathf("%s/bin/tool", goroot)) // Remove old pre-tool binaries. - for(i=0; i<nelem(oldtool); i++) - xremove(bpathf(&b, "%s/bin/%s", goroot, oldtool[i])); + for _, old := range oldtool { + xremove(pathf("%s/bin/%s", goroot, old)) + } // If $GOBIN is set and has a Go compiler, it must be cleaned. - 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; + for _, char := range gochars { + if isfile(pathf("%s%s%c%s", gobin, slash, char, "g")) { + for _, old := range oldtool { + xremove(pathf("%s/%s", gobin, old)) + } + break } } // For release, make sure excluded things are excluded. - 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)); + if strings.HasPrefix(goversion, "release.") || (strings.HasPrefix(goversion, "go") && !strings.Contains(goversion, "beta")) { + for _, dir := range unreleased { + if p := pathf("%s/%s", goroot, dir); isdir(p) { + fatal("%s should not exist in release build", p) + } + } } - - bfree(&b); } /* @@ -471,7 +440,7 @@ setup(void) */ // gccargs is the gcc command line to use for compiling a single C file. -static char *proto_gccargs[] = { +var proto_gccargs = []string{ "-Wall", // native Plan 9 compilers don't like non-standard prototypes // so let gcc catch them. @@ -489,34 +458,36 @@ static char *proto_gccargs[] = { "-fno-common", "-ggdb", "-pipe", -}; +} // gccargs2 is the second part of gccargs. // it is used if the environment isn't defining CFLAGS. -static char *proto_gccargs2[] = { +var proto_gccargs2 = []string{ // on older versions of GCC, -Wuninitialized is not supported // without -O, so put it here together with -O settings in case // the user's $CFLAGS doesn't include -O. "-Wuninitialized", -#if defined(__NetBSD__) && defined(__arm__) - // GCC 4.5.4 (NetBSD nb1 20120916) on ARM is known to mis-optimize gc/mparith3.c - // Fix available at http://patchwork.ozlabs.org/patch/64562/. - "-O1", -#else "-O2", -#endif -}; +} + +func init() { + if runtime.GOOS == "netbsd" && runtime.GOARCH == "arm" { + // GCC 4.5.4 (NetBSD nb1 20120916) on ARM is known to mis-optimize gc/mparith3.c + // Fix available at http://patchwork.ozlabs.org/patch/64562/. + proto_gccargs2[1] = "-O1" + } +} -static Vec gccargs, ldargs; +var gccargs, ldargs []string // deptab lists changes to the default dependencies for a given prefix. // deps ending in /* read the whole directory; deps beginning with - // exclude files with that prefix. -static struct { - char *prefix; // prefix of target - char *dep[20]; // dependency tweaks for targets with that prefix -} deptab[] = { - {"lib9", { +var deptab = []struct { + prefix string // prefix of target + dep []string // dependency tweaks for targets with that prefix +}{ + {"lib9", []string{ "$GOROOT/include/u.h", "$GOROOT/include/utf.h", "$GOROOT/include/fmt.h", @@ -524,14 +495,14 @@ static struct { "fmt/*", "utf/*", }}, - {"libbio", { + {"libbio", []string{ "$GOROOT/include/u.h", "$GOROOT/include/utf.h", "$GOROOT/include/fmt.h", "$GOROOT/include/libc.h", "$GOROOT/include/bio.h", }}, - {"liblink", { + {"liblink", []string{ "$GOROOT/include/u.h", "$GOROOT/include/utf.h", "$GOROOT/include/fmt.h", @@ -544,85 +515,85 @@ static struct { "anames8.c", "anames9.c", }}, - {"cmd/gc", { + {"cmd/gc", []string{ "-cplx.c", "-pgen.c", "-plive.c", "-popt.c", - "-y1.tab.c", // makefile dreg + "-y1.tab.c", // makefile dreg "opnames.h", }}, - {"cmd/5g", { + {"cmd/5g", []string{ "../gc/cplx.c", "../gc/pgen.c", "../gc/plive.c", "../gc/popt.c", "../gc/popt.h", - "$GOROOT/pkg/obj/$GOHOSTOS_$GOHOSTARCH/libgc.a", + "$GOROOT/pkg/obj/${GOHOSTOS}_$GOHOSTARCH/libgc.a", }}, - {"cmd/6g", { + {"cmd/6g", []string{ "../gc/cplx.c", "../gc/pgen.c", "../gc/plive.c", "../gc/popt.c", "../gc/popt.h", - "$GOROOT/pkg/obj/$GOHOSTOS_$GOHOSTARCH/libgc.a", + "$GOROOT/pkg/obj/${GOHOSTOS}_$GOHOSTARCH/libgc.a", }}, - {"cmd/8g", { + {"cmd/8g", []string{ "../gc/cplx.c", "../gc/pgen.c", "../gc/plive.c", "../gc/popt.c", "../gc/popt.h", - "$GOROOT/pkg/obj/$GOHOSTOS_$GOHOSTARCH/libgc.a", + "$GOROOT/pkg/obj/${GOHOSTOS}_$GOHOSTARCH/libgc.a", }}, - {"cmd/9g", { + {"cmd/9g", []string{ "../gc/cplx.c", "../gc/pgen.c", "../gc/plive.c", "../gc/popt.c", "../gc/popt.h", - "$GOROOT/pkg/obj/$GOHOSTOS_$GOHOSTARCH/libgc.a", + "$GOROOT/pkg/obj/${GOHOSTOS}_$GOHOSTARCH/libgc.a", }}, - {"cmd/5l", { + {"cmd/5l", []string{ "../ld/*", }}, - {"cmd/6l", { + {"cmd/6l", []string{ "../ld/*", }}, - {"cmd/8l", { + {"cmd/8l", []string{ "../ld/*", }}, - {"cmd/9l", { + {"cmd/9l", []string{ "../ld/*", }}, - {"cmd/go", { + {"cmd/go", []string{ "zdefaultcc.go", }}, - {"cmd/", { - "$GOROOT/pkg/obj/$GOHOSTOS_$GOHOSTARCH/liblink.a", - "$GOROOT/pkg/obj/$GOHOSTOS_$GOHOSTARCH/libbio.a", - "$GOROOT/pkg/obj/$GOHOSTOS_$GOHOSTARCH/lib9.a", + {"cmd/", []string{ + "$GOROOT/pkg/obj/${GOHOSTOS}_$GOHOSTARCH/liblink.a", + "$GOROOT/pkg/obj/${GOHOSTOS}_$GOHOSTARCH/libbio.a", + "$GOROOT/pkg/obj/${GOHOSTOS}_$GOHOSTARCH/lib9.a", }}, - {"runtime", { + {"runtime", []string{ "zaexperiment.h", "zversion.go", }}, -}; +} // depsuffix records the allowed suffixes for source files. -char *depsuffix[] = { +var depsuffix = []string{ ".c", ".h", ".s", ".go", -}; +} // gentab records how to generate some trivial files. -static struct { - char *nameprefix; - void (*gen)(char*, char*); -} gentab[] = { +var gentab = []struct { + nameprefix string + gen func(string, string) +}{ {"opnames.h", gcopnames}, {"anames5.c", mkanames}, {"anames6.c", mkanames}, @@ -634,272 +605,238 @@ static struct { // not generated anymore, but delete the file if we see it {"enam.c", nil}, -}; +} // install installs the library, package, or binary associated with dir, // which is relative to $GOROOT/src. -static void -install(char *dir) -{ - char *name, *p, *elem, *prefix, *exe; - bool islib, ispkg, isgo, stale, ispackcmd; - Buf b, b1, path, final_path, final_name, archive; - 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); - binit(&final_path); - binit(&final_name); - binit(&archive); - vinit(&compile); - vinit(&files); - vinit(&link); - vinit(&go); - vinit(&missing); - vinit(&clean); - vinit(&lib); - vinit(&extra); +func install(dir string) { + if vflag > 0 { + if goos != gohostos || goarch != gohostarch { + errprintf("%s (%s/%s)\n", dir, goos, goarch) + } else { + errprintf("%s\n", dir) + } + } + var clean []string + defer func() { + for _, name := range clean { + xremove(name) + } + }() // path = full path to dir. - bpathf(&path, "%s/src/%s", goroot, dir); - bpathf(&final_path, "%s/src/%s", goroot_final, dir); - name = lastelem(dir); + path := pathf("%s/src/%s", goroot, dir) + name := filepath.Base(dir) // set up gcc command line on first run. - 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 gccargs == nil { + gccargs = splitfields(defaultcc + " " + defaultcflags) + gccargs = append(gccargs, proto_gccargs...) + if defaultcflags == "" { + gccargs = append(gccargs, proto_gccargs2...) } - if(contains(gccargs.p[0], "clang")) { + if strings.Contains(gccargs[0], "clang") { // disable ASCII art in clang errors, if possible - vadd(&gccargs, "-fno-caret-diagnostics"); + gccargs = append(gccargs, "-fno-caret-diagnostics") // clang is too smart about unused command-line arguments - vadd(&gccargs, "-Qunused-arguments"); + gccargs = append(gccargs, "-Qunused-arguments") } // disable word wrapping in error messages - vadd(&gccargs, "-fmessage-length=0"); - if(streq(gohostos, "darwin")) { + gccargs = append(gccargs, "-fmessage-length=0") + if gohostos == "darwin" { // golang.org/issue/5261 - vadd(&gccargs, "-mmacosx-version-min=10.6"); + gccargs = append(gccargs, "-mmacosx-version-min=10.6") } } - if(ldargs.len == 0 && defaultldflags[0] != '\0') { - bprintf(&b, "%s", defaultldflags); - splitfields(&ldargs, bstr(&b)); + if ldargs == nil && defaultldflags != "" { + ldargs = splitfields(defaultldflags) } - islib = hasprefix(dir, "lib") || streq(dir, "cmd/gc"); - ispkg = !islib && !hasprefix(dir, "cmd/"); - isgo = ispkg || streq(dir, "cmd/go") || streq(dir, "cmd/cgo"); + islib := strings.HasPrefix(dir, "lib") || dir == "cmd/gc" + ispkg := !islib && !strings.HasPrefix(dir, "cmd/") + isgo := ispkg || dir == "cmd/go" || dir == "cmd/cgo" - exe = ""; - if(streq(gohostos, "windows")) - exe = ".exe"; + exe := "" + if gohostos == "windows" { + exe = ".exe" + } // Start final link command line. // Note: code below knows that link.p[targ] is the target. - ispackcmd = 0; - if(islib) { + var ( + link []string + targ int + ispackcmd bool + ) + switch { + case islib: // C library. - 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) { + prefix := "" + if !strings.HasPrefix(name, "lib") { + prefix = "lib" + } + link = []string{"ar", "rsc", pathf("%s/pkg/obj/%s_%s/%s%s.a", goroot, gohostos, gohostarch, prefix, name)} + if gohostos == "plan9" { + link[1] = "rc" + } + targ = len(link) - 1 + + case ispkg: // Go library (package). - ispackcmd = 1; - vadd(&link, "pack"); // program name - unused here, but all the other cases record one - p = bprintf(&b, "%s/pkg/%s_%s/%s", goroot, goos, goarch, dir); - *xstrrchr(p, '/') = '\0'; - xmkdirall(p); - targ = link.len; - vadd(&link, bpathf(&b, "%s/pkg/%s_%s/%s.a", goroot, goos, goarch, dir)); - } else if(streq(dir, "cmd/go") || streq(dir, "cmd/cgo")) { + ispackcmd = true + link = []string{"pack", pathf("%s/pkg/%s_%s/%s.a", goroot, goos, goarch, dir)} + targ = len(link) - 1 + xmkdirall(filepath.Dir(link[targ])) + + case dir == "cmd/go" || dir == "cmd/cgo": // Go command. - 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 { + elem := name + if elem == "go" { + elem = "go_bootstrap" + } + link = []string{fmt.Sprintf("%s/%sl", tooldir, gochar), "-o", pathf("%s/%s%s", tooldir, elem, exe)} + targ = len(link) - 1 + + default: // C command. Use gccargs and ldargs. - if(streq(gohostos, "plan9")) { - vadd(&link, bprintf(&b, "%sl", gohostchar)); - vadd(&link, "-o"); - targ = link.len; - vadd(&link, bpathf(&b, "%s/%s", tooldir, name)); + if gohostos == "plan9" { + link = []string{fmt.Sprintf("%sl", gohostchar), "-o", pathf("%s/%s", tooldir, name)} + targ = len(link) - 1 } 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"); + link = append(link, gccargs...) + link = append(link, ldargs...) + if sflag { + link = append(link, "-static") + } + link = append(link, "-o", pathf("%s/%s%s", tooldir, name, exe)) + targ = len(link) - 1 + switch gohostarch { + case "amd64": + link = append(link, "-m64") + case "386": + link = append(link, "-m32") + } } } - ttarg = mtime(link.p[targ]); + ttarg := mtime(link[targ]) // Gather files that are sources for this target. // Everything in that directory, and any target-specific // additions. - xreaddir(&files, bstr(&path)); + files := xreaddir(path) // Remove files beginning with . or _, // which are likely to be editor temporary files. // This is the same heuristic build.ScanDir uses. // There do exist real C files beginning with _, // so limit that check to just Go files. - 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 = filter(files, func(p string) bool { + return !strings.HasPrefix(p, ".") && (!strings.HasPrefix(p, "_") || !strings.HasSuffix(p, ".go")) + }) + + var libs []string + + for _, dt := range deptab { + if dir == dt.prefix || strings.HasSuffix(dt.prefix, "/") && strings.HasPrefix(dir, dt.prefix) { + for _, p := range dt.dep { + p = os.ExpandEnv(p) + switch { + case strings.HasSuffix(p, ".a"): + libs = append(libs, p) + + case strings.HasSuffix(p, "/*"): + dir := strings.TrimSuffix(p, "/*") + for _, name := range xreaddir(pathf("%s/%s", path, dir)) { + files = append(files, pathf("%s/%s", dir, name)) } - files.len = n; - continue; + + case strings.HasPrefix(p, "-"): + files = filter(files, func(s string) bool { + return !strings.HasPrefix(s, p[1:]) + }) + + default: + files = append(files, p) } - vadd(&files, p); } } } - vuniq(&files); + files = uniq(files) // Convert to absolute paths. - 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); + for i, p := range files { + if !isabs(p) { + files[i] = pathf("%s/%s", path, p) } } // Is the target up-to-date? - 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; + var gofiles, missing []string + stale := rebuildall + files = filter(files, func(p string) bool { + for _, suf := range depsuffix { + if strings.HasSuffix(p, suf) { + goto ok + } + } + return false ok: - t = mtime(p); - if(t != 0 && !hassuffix(p, ".a") && !shouldbuild(p, dir)) { - xfree(files.p[i]); - continue; + t := mtime(p) + if !t.IsZero() && !strings.HasSuffix(p, ".a") && !shouldbuild(p, dir) { + return false } - 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; + if strings.HasSuffix(p, ".go") { + gofiles = append(gofiles, p) } - files.p[n++] = files.p[i]; - } - files.len = n; + if t.After(ttarg) { + stale = true + } + if t.IsZero() { + missing = append(missing, p) + } + return true + }) // If there are no files to compile, we're done. - if(files.len == 0) - goto out; - - for(i=0; i<lib.len && !stale; i++) - if(mtime(lib.p[i]) > ttarg) - stale = 1; + if len(files) == 0 { + return + } - if(!stale) - goto out; + if !stale { + for _, p := range libs { + if mtime(p).After(ttarg) { + stale = true + break + } + } + } + + if !stale { + return + } // For package runtime, copy some files into the work space. - if(streq(dir, "runtime")) { - 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); + if dir == "runtime" { + // For use by assembly and C files. + copyfile(pathf("%s/pkg/%s_%s/textflag.h", goroot, goos, goarch), + pathf("%s/src/cmd/ld/textflag.h", goroot), 0) + copyfile(pathf("%s/pkg/%s_%s/funcdata.h", goroot, goos, goarch), + pathf("%s/src/runtime/funcdata.h", goroot), 0) } // 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(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); + for _, p := range files { + elem := filepath.Base(p) + for _, gt := range gentab { + if gt.gen == nil { + continue + } + if strings.HasPrefix(elem, gt.nameprefix) { + if vflag > 1 { + errprintf("generate %s\n", p) + } + gt.gen(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, @@ -907,222 +844,173 @@ install(char *dir) // exist (it does not know how to build them). // The 'clean' command can remove // the generated files. - goto built; + goto built } } // Did not rebuild p. - if(find(p, missing.p, missing.len) >= 0) - fatal("missing file %s", p); - built:; + if find(p, missing) >= 0 { + fatal("missing file %s", p) + } + built: } - if((!streq(goos, gohostos) || !streq(goarch, gohostarch)) && isgo) { + if (goos != gohostos || 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; + if vflag > 1 { + errprintf("skip build for cross-compile %s\n", dir) + } + return } - if(isgo) { + var archive string + if isgo { // The next loop will compile individual non-Go files. // Hand the Go files to the compiler en masse. // For package runtime, this writes go_asm.h, which // the assembly files will need. - 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)); - else - bwriteb(&archive, &b); - - vadd(&compile, "-p"); - if(hasprefix(dir, "cmd/")) - vadd(&compile, "main"); - else - vadd(&compile, dir); - - if(streq(dir, "runtime")) { - vadd(&compile, "-+"); - vadd(&compile, "-asmhdr"); - bpathf(&b1, "%s/go_asm.h", workdir); - vadd(&compile, bstr(&b1)); + pkg := dir + if strings.HasPrefix(dir, "cmd/") { + pkg = "main" } - - vcopy(&compile, go.p, go.len); - - runv(nil, bstr(&path), CheckExit, &compile); + b := pathf("%s/_go_.a", workdir) + clean = append(clean, b) + if !ispackcmd { + link = append(link, b) + } else { + archive = b + } + compile := []string{pathf("%s/%sg", tooldir, gochar), "-pack", "-o", b, "-p", pkg} + if dir == "runtime" { + compile = append(compile, "-+", "-asmhdr", pathf("%s/go_asm.h", workdir)) + } + compile = append(compile, gofiles...) + run(path, CheckExit|ShowOutput, compile...) } // Compile the files. - for(i=0; i<files.len; i++) { - if(!hassuffix(files.p[i], ".c") && !hassuffix(files.p[i], ".s")) - continue; - name = lastelem(files.p[i]); + for _, p := range files { + if !strings.HasSuffix(p, ".c") && !strings.HasSuffix(p, ".s") { + continue + } + name := filepath.Base(p) - vreset(&compile); - if(!isgo) { + var compile []string + if !isgo { // C library or tool. - 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"); // for GNU Bison - vadd(&compile, bpathf(&b, "-I%s/include/plan9", goroot)); - vadd(&compile, bpathf(&b, "-I%s/include/plan9/%s", goroot, gohostarch)); + if gohostos == "plan9" { + compile = []string{ + gohostchar + "c", "-FTVwp", + "-DPLAN9", + "-D__STDC__=1", + "-D__SIZE_TYPE__=ulong", // for GNU bison + pathf("-I%s/include/plan9", goroot), + pathf("-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)); + compile = gccargs[0:len(gccargs):len(gccargs)] + compile = append(compile, "-c") + switch gohostarch { + case "amd64": + compile = append(compile, "-m64") + case "386": + compile = append(compile, "-m32") + } + compile = append(compile, "-I", pathf("%s/include", goroot)) } - if(streq(dir, "lib9")) - vadd(&compile, "-DPLAN9PORT"); - + if dir == "lib9" { + compile = append(compile, "-DPLAN9PORT") + } - vadd(&compile, "-I"); - vadd(&compile, bstr(&path)); + compile = append(compile, "-I", path) // lib9/goos.c gets the default constants hard-coded. - 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, "\\", "\\\\"); // turn into C string - 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 name == "goos.c" { + compile = append(compile, + "-D", fmt.Sprintf("GOOS=%q", goos), + "-D", fmt.Sprintf("GOARCH=%q", goarch), + "-D", fmt.Sprintf("GOROOT=%q", goroot_final), + "-D", fmt.Sprintf("GOVERSION=%q", goversion), + "-D", fmt.Sprintf("GOARM=%q", goarm), + "-D", fmt.Sprintf("GO386=%q", go386), + "-D", fmt.Sprintf("GO_EXTLINK_ENABLED=%q", goextlinkenabled), + ) } // gc/lex.c records the GOEXPERIMENT setting used during the build. - if(streq(name, "lex.c")) { - xgetenv(&b, "GOEXPERIMENT"); - vadd(&compile, "-D"); - vadd(&compile, bprintf(&b1, "GOEXPERIMENT=\"%s\"", bstr(&b))); + if name == "lex.c" { + compile = append(compile, + "-D", fmt.Sprintf("GOEXPERIMENT=%q", os.Getenv("GOEXPERIMENT"))) } } else { - // Supporting files for a Go package. - 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"); + // Assembly file for a Go package. + compile = []string{ + pathf("%s/%sa", tooldir, gochar), + "-I", workdir, + "-I", pathf("%s/pkg/%s_%s", goroot, goos, goarch), + "-D", "GOOS_" + goos, + "-D", "GOARCH_" + goarch, + "-D", "GOOS_GOARCH_" + goos + "_" + goarch, } - 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")) { + doclean := true + b := pathf("%s/%s", workdir, filepath.Base(p)) + if !isgo && 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])); - doclean = 0; + obj := pathf("%s/pkg/obj/%s", goroot, dir) + xmkdirall(obj) + b = pathf("%s/%s", obj, filepath.Base(p)) + doclean = false } // Change the last character of the output file (which was c or s). - 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); + if gohostos == "plan9" { + b = b[:len(b)-1] + gohostchar + } else { + b = b[:len(b)-1] + "o" + } + compile = append(compile, "-o", b, p) + bgrun(path, compile...) - vadd(&link, bstr(&b)); - if(doclean) - vadd(&clean, bstr(&b)); + link = append(link, b) + if doclean { + clean = append(clean, b) + } } - bgwait(); + bgwait() - if(isgo && ispackcmd) { - xremove(link.p[targ]); - dopack(link.p[targ], bstr(&archive), &link.p[targ+1], link.len - (targ+1)); - goto nobuild; + if isgo && ispackcmd { + xremove(link[targ]) + dopack(link[targ], archive, link[targ+1:]) + return } - if(!islib && !isgo) { + if !islib && !isgo { // C binaries need the libraries explicitly, and -lm. - vcopy(&link, lib.p, lib.len); - if(!streq(gohostos, "plan9")) - vadd(&link, "-lm"); + link = append(link, libs...) + if gohostos != "plan9" { + link = append(link, "-lm") + } } // Remove target before writing it. - xremove(link.p[targ]); - - runv(nil, nil, CheckExit, &link); -nobuild: - -out: - for(i=0; i<clean.len; i++) - xremove(clean.p[i]); - - bfree(&b); - bfree(&b1); - bfree(&path); - bfree(&archive); - vfree(&compile); - vfree(&files); - vfree(&link); - vfree(&go); - vfree(&missing); - vfree(&clean); - vfree(&lib); - vfree(&extra); + xremove(link[targ]) + run("", CheckExit|ShowOutput, link...) } // matchfield reports whether the field matches this build. -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") || (streq(goos, "android") && streq(f, "linux")); - *p = 0; - res = matchfield(f) && matchfield(p+1); - *p = ','; - return res; +func matchfield(f string) bool { + for _, tag := range strings.Split(f, ",") { + if tag == goos || tag == goarch || tag == "cmd_go_bootstrap" || tag == "go1.1" || (goos == "android" && tag == "linux") { + continue + } + return false + } + return true } // shouldbuild reports whether we should build this file. @@ -1132,162 +1020,117 @@ matchfield(char *f) // In particular, they can be the entire file name (like windows.c). // We also allow the special tag cmd_go_bootstrap. // See ../go/bootstrap.go and package go/build. -static bool -shouldbuild(char *file, char *dir) -{ - char *name, *p; - int i, j, ret; - Buf b; - Vec lines, fields; - +func shouldbuild(file, dir string) bool { // Check file name for GOOS or GOARCH. - name = lastelem(file); - for(i=0; i<nelem(okgoos); i++) { - if(streq(okgoos[i], goos)) - continue; - p = xstrstr(name, okgoos[i]); - if(p == nil) - continue; - p += xstrlen(okgoos[i]); - if(*p == '.' || *p == '_' || *p == '\0') - return 0; - } - for(i=0; i<nelem(okgoarch); i++) { - if(streq(okgoarch[i], goarch)) - continue; - p = xstrstr(name, okgoarch[i]); - if(p == nil) - continue; - p += xstrlen(okgoarch[i]); - if(*p == '.' || *p == '_' || *p == '\0') - return 0; + name := filepath.Base(file) + excluded := func(list []string, ok string) bool { + for _, x := range list { + if x == ok { + continue + } + i := strings.Index(name, x) + if i < 0 { + continue + } + i += len(x) + if i == len(name) || name[i] == '.' || name[i] == '_' { + return true + } + } + return false + } + if excluded(okgoos, goos) || excluded(okgoarch, goarch) { + return false } // Omit test files. - if(contains(name, "_test")) - return 0; + if strings.Contains(name, "_test") { + return false + } // 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; - if(hassuffix(file, "cmd/cgo/doc.go") || hassuffix(file, "cmd\\cgo\\doc.go")) - return 0; + if strings.HasSuffix(file, "cmd/go/doc.go") || strings.HasSuffix(file, "cmd\\go\\doc.go") { + return false + } + if strings.HasSuffix(file, "cmd/cgo/doc.go") || strings.HasSuffix(file, "cmd\\cgo\\doc.go") { + return false + } // Check file contents for // +build lines. - 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; + for _, p := range splitlines(readfile(file)) { + p = strings.TrimSpace(p) + if p == "" { + continue + } + if strings.Contains(p, "package documentation") { + return false } - if(contains(p, "package main") && !streq(dir, "cmd/go") && !streq(dir, "cmd/cgo")) { - ret = 0; - goto out; + if strings.Contains(p, "package main") && dir != "cmd/go" && dir != "cmd/cgo" { + return false } - 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; + if !strings.HasPrefix(p, "//") { + break } - ret = 0; - goto out; - fieldmatch:; + if !strings.Contains(p, "+build") { + continue + } + fields := splitfields(p) + if len(fields) < 2 || fields[1] != "+build" { + continue + } + for _, p := range fields[2:] { + if (p[0] == '!' && !matchfield(p[1:])) || matchfield(p) { + goto fieldmatch + } + } + return false + fieldmatch: } -out: - bfree(&b); - vfree(&lines); - vfree(&fields); - - return ret; + return true } // copy copies the file src to dst, via memory (so only good for small files). -void -copyfile(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); +func copyfile(dst, src string, exec int) { + if vflag > 1 { + errprintf("cp %s %s\n", src, dst) + } + writefile(readfile(src), dst, exec) } // 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<nextra; i++) { - readfile(&b, extra[i]); +func dopack(dst, src string, extra []string) { + bdst := bytes.NewBufferString(readfile(src)) + for _, file := range extra { + b := readfile(file) // find last path element for archive member name - p = xstrrchr(extra[i], '/'); - if(p) - p++; - q = xstrrchr(extra[i], '\\'); - if(q) { - q++; - if(p == nil || q > p) - p = q; + i := strings.LastIndex(file, "/") + 1 + j := strings.LastIndex(file, `\`) + 1 + if i < j { + i = j } - 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); + fmt.Fprintf(bdst, "%-16.16s%-12d%-6d%-6d%-8o%-10d`\n", file[i:], 0, 0, 0, 0644, len(b)) + bdst.WriteString(b) + if len(b)&1 != 0 { + bdst.WriteByte(0) } } - - writefile(&bdst, dst, 0); - - bfree(&b); - bfree(&bdst); + writefile(bdst.String(), dst, 0) } // buildorder records the order of builds for the 'go bootstrap' command. -static char *buildorder[] = { +var buildorder = []string{ "lib9", "libbio", "liblink", "cmd/gc", // must be before g - "cmd/%sl", // must be before a, g + "cmd/%sl", // must be before a, g "cmd/%sa", "cmd/%sg", @@ -1337,12 +1180,12 @@ static char *buildorder[] = { "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[] = { +var cleantab = []string{ // Commands and C libraries. "cmd/5a", "cmd/5g", @@ -1357,7 +1200,7 @@ static char *cleantab[] = { "cmd/9g", "cmd/9l", "cmd/gc", - "cmd/go", + "cmd/go", "lib9", "libbio", "liblink", @@ -1403,383 +1246,246 @@ static char *cleantab[] = { "unicode", "unicode/utf16", "unicode/utf8", -}; +} -static char *runtimegen[] = { +var runtimegen = []string{ "zaexperiment.h", "zversion.go", -}; - -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)); +} + +func clean() { + for _, name := range cleantab { + path := pathf("%s/src/%s", goroot, name) // 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])); + for _, elem := range xreaddir(path) { + for _, gt := range gentab { + if strings.HasPrefix(elem, gt.nameprefix) { + xremove(pathf("%s/%s", path, elem)) + } } } // Remove generated binary named for directory. - if(hasprefix(cleantab[i], "cmd/")) - xremove(bpathf(&b, "%s/%s", bstr(&path), cleantab[i]+4)); + if strings.HasPrefix(name, "cmd/") { + xremove(pathf("%s/%s", path, name[4:])) + } } - // remove src/runtime/zaexperiment.h and - // except leave zgoos and zgoarch, now maintained with go generate. - bpathf(&path, "%s/src/runtime", goroot); - for(j=0; j<nelem(runtimegen); j++) - xremove(bpathf(&b, "%s/%s", bstr(&path), runtimegen[j])); + // remove runtimegen files. + path := pathf("%s/src/runtime", goroot) + for _, elem := range runtimegen { + xremove(pathf("%s/%s", path, elem)) + } - if(rebuildall) { + if rebuildall { // Remove object tree. - xremoveall(bpathf(&b, "%s/pkg/obj/%s_%s", goroot, gohostos, gohostarch)); + xremoveall(pathf("%s/pkg/obj/%s_%s", goroot, gohostos, gohostarch)) // Remove installed packages and tools. - xremoveall(bpathf(&b, "%s/pkg/%s_%s", goroot, gohostos, gohostarch)); - xremoveall(bpathf(&b, "%s/pkg/%s_%s", goroot, goos, goarch)); - xremoveall(tooldir); + xremoveall(pathf("%s/pkg/%s_%s", goroot, gohostos, gohostarch)) + xremoveall(pathf("%s/pkg/%s_%s", goroot, goos, goarch)) + xremoveall(tooldir) // Remove cached version info. - xremove(bpathf(&b, "%s/VERSION.cache", goroot)); + xremove(pathf("%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); +func usage() { + 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\"\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); +func cmdenv() { + path := flag.Bool("p", false, "emit updated PATH") + plan9 := flag.Bool("9", false, "emit plan 9 syntax") + windows := flag.Bool("w", false, "emit windows syntax") + xflagparse(0) + + format := "%s=\"%s\"\n" + switch { + case *plan9: + format = "%s='%s'\n" + case *windows: + format = "set %s=%s\r\n" + } + + 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 goarch == "arm" { + xprintf(format, "GOARM", goarm) + } + if goarch == "386" { + xprintf(format, "GO386", go386) + } + + if *path { + sep := ":" + if gohostos == "windows" { + sep = ";" + } + xprintf(format, "PATH", fmt.Sprintf("%s%s%s", gobin, sep, os.Getenv("PATH"))) + } } // 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(); +func cmdbootstrap() { + flag.BoolVar(&rebuildall, "a", rebuildall, "rebuild all") + flag.BoolVar(&sflag, "s", sflag, "build static binaries") + xflagparse(0) + + if isdir(pathf("%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", + pathf("%s/src/pkg", goroot)) + } - 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() } - - if(rebuildall) - clean(); - goversion = findgoversion(); - setup(); - xsetenv("GOROOT", goroot); - xsetenv("GOROOT_FINAL", goroot_final); + setup() // 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<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); + oldgoos = goos + oldgoarch = goarch + oldgochar = gochar + goos = gohostos + goarch = gohostarch + gochar = gohostchar + os.Setenv("GOHOSTARCH", gohostarch) + os.Setenv("GOHOSTOS", gohostos) + os.Setenv("GOARCH", goarch) + os.Setenv("GOOS", goos) + + for _, pattern := range buildorder { + dir := pattern + if strings.Contains(pattern, "%s") { + dir = fmt.Sprintf(pattern, gohostchar) + } + install(dir) + if oldgochar != gohostchar && strings.Contains(pattern, "%s") { + install(fmt.Sprintf(pattern, oldgochar)) + } + } - // Build runtime for actual goos/goarch too. - if(!streq(goos, gohostos) || !streq(goarch, gohostarch)) - install("runtime"); + goos = oldgoos + goarch = oldgoarch + gochar = oldgochar + os.Setenv("GOARCH", goarch) + os.Setenv("GOOS", goos) - bfree(&b); + // Build runtime for actual goos/goarch too. + if goos != gohostos || goarch != gohostarch { + install("runtime") + } } -static char* -defaulttarg(void) -{ - char *p; - Buf pwd, src, real_src; - - binit(&pwd); - binit(&src); - binit(&real_src); - +func defaulttarg() string { // xgetwd might return a path with symlinks fully resolved, and if // there happens to be symlinks in goroot, then the hasprefix test // will never succeed. Instead, we use xrealwd to get a canonical // goroot/src before the comparison to avoid this problem. - 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; + pwd := xgetwd() + src := pathf("%s/src/", goroot) + real_src := xrealwd(src) + if !strings.HasPrefix(pwd, real_src) { + fatal("current directory %s is not under %s", pwd, real_src) + } + pwd = pwd[len(real_src):] // guard againt xrealwd return the directory without the trailing / - if(*p == slash[0]) - p++; - - bfree(&pwd); - bfree(&src); - bfree(&real_src); + pwd = strings.TrimPrefix(pwd, "/") - return p; + return pwd } // Install installs the list of packages named on the command line. -void -cmdinstall(int argc, char **argv) -{ - int i; - - ARGBEGIN{ - case 's': - sflag++; - break; - case 'v': - vflag++; - break; - default: - usage(); - }ARGEND +func cmdinstall() { + flag.BoolVar(&sflag, "s", sflag, "build static binaries") + xflagparse(-1) - if(argc == 0) - install(defaulttarg()); + if flag.NArg() == 0 { + install(defaulttarg()) + } - for(i=0; i<argc; i++) - install(argv[i]); + for _, arg := range flag.Args() { + install(arg) + } } // 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(); +func cmdclean() { + xflagparse(0) + 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); +func cmdbanner() { + xflagparse(0) - xprintf("\n"); - xprintf("---\n"); - xprintf("Installed Go for %s/%s in %s\n", goos, goarch, goroot); - xprintf("Installed commands in %s\n", gobin); + 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 !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")) { + } else if 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); + pid := strings.Replace(readfile("#c/pid"), " ", "", -1) + ns := fmt.Sprintf("/proc/%s/ns", pid) + if !strings.Contains(readfile(ns), fmt.Sprintf("bind -b %s /bin", gobin)) { + 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"); + pathsep := ":" + if gohostos == "windows" { + pathsep = ";" + } + if !strings.Contains(pathsep+os.Getenv("PATH")+pathsep, pathsep+gobin+pathsep) { + xprintf("*** You need to add %s to your PATH.\n", gobin) + } } - if(!xsamefile(goroot_final, goroot)) { - xprintf("\n" + if !xsamefile(goroot_final, goroot) { + xprintf("\n"+ "The binaries expect %s to be copied or moved to %s\n", - goroot, goroot_final); + 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); +func cmdversion() { + xflagparse(0) + xprintf("%s\n", goversion) } diff --git a/src/cmd/dist/buildgc.go b/src/cmd/dist/buildgc.go index 64434d51e1..b1b5d5e7ba 100644 --- a/src/cmd/dist/buildgc.go +++ b/src/cmd/dist/buildgc.go @@ -2,7 +2,14 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -#include "a.h" +package main + +import ( + "bytes" + "fmt" + "strconv" + "strings" +) /* * Helpers for building cmd/gc. @@ -12,207 +19,152 @@ // It finds the OXXX enum, pulls out all the constants // from OXXX to OEND, and writes a table mapping // op to string. -void -gcopnames(char *dir, char *file) -{ - char *p, *q; - int i, j, end; - Buf in, b, out; - Vec lines, fields; - - binit(&in); - binit(&b); - binit(&out); - vinit(&lines); - vinit(&fields); - - bwritestr(&out, bprintf(&b, "// auto generated by go tool dist\n")); - bwritestr(&out, bprintf(&b, "static char *opnames[] = {\n")); - - readfile(&in, bprintf(&b, "%s/go.h", dir)); - splitlines(&lines, bstr(&in)); - i = 0; - while(i<lines.len && !contains(lines.p[i], "OXXX")) - i++; - end = 0; - for(; i<lines.len && !end; i++) { - p = xstrstr(lines.p[i], "//"); - if(p != nil) - *p = '\0'; - end = contains(lines.p[i], "OEND"); - splitfields(&fields, lines.p[i]); - for(j=0; j<fields.len; j++) { - q = fields.p[j]; - if(*q == 'O') - q++; - p = q+xstrlen(q)-1; - if(*p == ',') - *p = '\0'; - bwritestr(&out, bprintf(&b, " [O%s] = \"%s\",\n", q, q)); +func gcopnames(dir, file string) { + var out bytes.Buffer + fmt.Fprintf(&out, "// auto generated by go tool dist\n") + fmt.Fprintf(&out, "static char *opnames[] = {\n") + + in := readfile(pathf("%s/go.h", dir)) + lines := splitlines(in) + i := 0 + for i < len(lines) && !strings.Contains(lines[i], "OXXX") { + i++ + } + for _, line := range lines[i:] { + if i := strings.Index(line, "//"); i >= 0 { + line = line[:i] + } + for _, field := range splitfields(line) { + field = strings.TrimPrefix(field, "O") + field = strings.TrimSuffix(field, ",") + fmt.Fprintf(&out, "\t[O%s] = \"%s\",\n", field, field) + } + if strings.Contains(line, "OEND") { + break } } - - bwritestr(&out, bprintf(&b, "};\n")); - - writefile(&out, file, 0); + fmt.Fprintf(&out, "};\n") - bfree(&in); - bfree(&b); - bfree(&out); - vfree(&lines); - vfree(&fields); -} - -static int -xatoi(char *s, char **end) -{ - int val = 0; - for(; *s && *s >= '0' && *s <= '9'; ++s) - val = val * 10 + (*s - '0'); - *end = s; - return val; + writefile(out.String(), file, 0) } // mkanames reads [5689].out.h and writes anames[5689].c // The format is much the same as the Go opcodes above. // It also writes out cnames array for C_* constants and the dnames // array for D_* constants. -void -mkanames(char *dir, char *file) -{ - int i, j, ch, n, unknown; - Buf in, b, out, out2; - Vec lines; - char *p, *p2; - Vec dnames[128]; - - binit(&b); - binit(&in); - binit(&out); - binit(&out2); - vinit(&lines); - for(i=0; i<nelem(dnames); i++) - vinit(&dnames[i]); - - ch = file[xstrlen(file)-3]; - bprintf(&b, "%s/../cmd/%cl/%c.out.h", dir, ch, ch); - readfile(&in, bstr(&b)); - splitlines(&lines, bstr(&in)); - +func mkanames(dir, file string) { + ch := file[len(file)-3] + targ := pathf("%s/../cmd/%cl/%c.out.h", dir, ch, ch) + in := readfile(targ) + lines := splitlines(in) + // Include link.h so that the extern declaration there is // checked against the non-extern declaration we are generating. - bwritestr(&out, bprintf(&b, "// auto generated by go tool dist\n")); - bwritestr(&out, bprintf(&b, "#include <u.h>\n")); - bwritestr(&out, bprintf(&b, "#include <libc.h>\n")); - bwritestr(&out, bprintf(&b, "#include <bio.h>\n")); - bwritestr(&out, bprintf(&b, "#include <link.h>\n")); - bwritestr(&out, bprintf(&b, "#include \"../cmd/%cl/%c.out.h\"\n", ch, ch)); - bwritestr(&out, bprintf(&b, "\n")); - - bwritestr(&out, bprintf(&b, "char* anames%c[] = {\n", ch)); - for(i=0; i<lines.len; i++) { - if(hasprefix(lines.p[i], "\tA")) { - p = xstrstr(lines.p[i], ","); - if(p) - *p = '\0'; - p = xstrstr(lines.p[i], "\n"); - if(p) - *p = '\0'; - p = lines.p[i] + 2; - bwritestr(&out, bprintf(&b, "\t\"%s\",\n", p)); + var out bytes.Buffer + fmt.Fprintf(&out, "// auto generated by go tool dist\n") + fmt.Fprintf(&out, "#include <u.h>\n") + fmt.Fprintf(&out, "#include <libc.h>\n") + fmt.Fprintf(&out, "#include <bio.h>\n") + fmt.Fprintf(&out, "#include <link.h>\n") + fmt.Fprintf(&out, "#include \"../cmd/%cl/%c.out.h\"\n", ch, ch) + fmt.Fprintf(&out, "\n") + + fmt.Fprintf(&out, "char* anames%c[] = {\n", ch) + for _, line := range lines { + if strings.HasPrefix(line, "\tA") { + if i := strings.Index(line, ","); i >= 0 { + line = line[:i] + } + if i := strings.Index(line, "\n"); i >= 0 { + line = line[:i] + } + line = line[2:] + fmt.Fprintf(&out, "\t\"%s\",\n", line) } } - bwritestr(&out, "};\n"); - - j=0; - bprintf(&out2, "char* cnames%c[] = {\n", ch); - for(i=0; i<lines.len; i++) { - if(hasprefix(lines.p[i], "\tC_")) { - p = xstrstr(lines.p[i], ","); - if(p) - *p = '\0'; - p = xstrstr(lines.p[i], "\n"); - if(p) - *p = '\0'; - p = lines.p[i] + 3; - bwritestr(&out2, bprintf(&b, "\t\"%s\",\n", p)); - j++; + fmt.Fprintf(&out, "};\n") + + j := 0 + var out2 bytes.Buffer + fmt.Fprintf(&out2, "char* cnames%c[] = {\n", ch) + for _, line := range lines { + if strings.HasPrefix(line, "\tC_") { + if i := strings.Index(line, ","); i >= 0 { + line = line[:i] + } + if i := strings.Index(line, "\n"); i >= 0 { + line = line[:i] + } + line = line[3:] + fmt.Fprintf(&out2, "\t\"%s\",\n", line) + j++ } } - bwritestr(&out2, "};\n"); - if(j>0) - bwriteb(&out, &out2); - - j=unknown=0; - n=-1; - for(i=0; i<lines.len; i++) { - if(hasprefix(lines.p[i], "\tD_")) { - p = xstrstr(lines.p[i], ","); - if(p) - *p = '\0'; - p = xstrstr(lines.p[i], "\n"); - if(p) - *p = '\0'; + fmt.Fprintf(&out2, "};\n") + if j > 0 { + out.Write(out2.Bytes()) + } + + var dnames [128][]string + j = 0 + unknown := false + n := -1 + for _, line := range lines { + if strings.HasPrefix(line, "\tD_") { + if i := strings.Index(line, ","); i >= 0 { + line = line[:i] + } // Parse explicit value, if any - p = xstrstr(lines.p[i], "="); - if(p) { - // Skip space after '=' - p2 = p + 1; - while(*p2 == ' ' || *p2 == '\t') - p2++; - n = xatoi(p2, &p2); - // We can't do anything about - // non-numeric values or anything that - // follows - while(*p2 == ' ' || *p2 == '\t') - p2++; - if(*p2 != 0) { - unknown = 1; - continue; + if i := strings.Index(line, "="); i >= 0 { + value := strings.TrimSpace(line[i+1:]) + line = strings.TrimSpace(line[:i]) + var err error + n, err = strconv.Atoi(value) + if err != nil { + // We can't do anything about + // non-numeric values or anything that + // follows. + unknown = true + continue } - // Truncate space before '=' - while(*(p-1) == ' ' || *(p-1) == '\t') - p--; - *p = '\0'; - unknown = 0; + unknown = false } else { - n++; + n++ + } + + if unknown || n < 0 || n >= len(dnames) { + continue } - if(unknown || n >= nelem(dnames)) - continue; + line = strings.TrimSpace(line) + line = line[len("D_"):] - p = lines.p[i] + 3; - if(xstrcmp(p, "LAST") == 0) - continue; - vadd(&dnames[n], p); - j++; + if strings.Contains(line, "LAST") { + continue + } + dnames[n] = append(dnames[n], line) + j++ } } - if(j>0){ - bwritestr(&out, bprintf(&b, "char* dnames%c[D_LAST] = {\n", ch)); - for(i=0; i<nelem(dnames); i++) { - if(dnames[i].len == 0) - continue; - bwritestr(&out, bprintf(&b, "\t[D_%s] = \"", dnames[i].p[0])); - for(j=0; j<dnames[i].len; j++) { - if(j != 0) - bwritestr(&out, "/"); - bwritestr(&out, dnames[i].p[j]); + + if j > 0 { + fmt.Fprintf(&out, "char* dnames%c[D_LAST] = {\n", ch) + for _, d := range dnames { + if len(d) == 0 { + continue } - bwritestr(&out, "\",\n"); + fmt.Fprintf(&out, "\t[D_%s] = \"", d[0]) + for k, name := range d { + if k > 0 { + fmt.Fprintf(&out, "/") + } + fmt.Fprintf(&out, "%s", name) + } + fmt.Fprintf(&out, "\",\n") } - bwritestr(&out, "};\n"); + fmt.Fprintf(&out, "};\n") } - writefile(&out, file, 0); - - bfree(&b); - bfree(&in); - bfree(&out); - bfree(&out2); - vfree(&lines); - for(i=0; i<nelem(dnames); i++) - vfree(&dnames[i]); + writefile(out.String(), file, 0) } diff --git a/src/cmd/dist/buildgo.go b/src/cmd/dist/buildgo.go index 41208fac5f..9cc650840d 100644 --- a/src/cmd/dist/buildgo.go +++ b/src/cmd/dist/buildgo.go @@ -2,7 +2,9 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -#include "a.h" +package main + +import "fmt" /* * Helpers for building cmd/go and cmd/cgo. @@ -15,35 +17,23 @@ // const defaultCXX = <defaultcxx> // // It is invoked to write cmd/go/zdefaultcc.go -// but we also write cmd/cgo/zdefaultcc.go. -void -mkzdefaultcc(char *dir, char *file) -{ - Buf b, out; - - USED(dir); - - binit(&out); - bprintf(&out, - "// auto generated by go tool dist\n" - "\n" - "package main\n" - "\n" - "const defaultCC = `%s`\n" - "const defaultCXX = `%s`\n", - defaultcctarget, defaultcxxtarget); +// but we also write cmd/cgo/zdefaultcc.go +func mkzdefaultcc(dir, file string) { + var out string - writefile(&out, file, 0); + out = fmt.Sprintf( + "// auto generated by go tool dist\n"+ + "\n"+ + "package main\n"+ + "\n"+ + "const defaultCC = `%s`\n"+ + "const defaultCXX = `%s`\n", + defaultcctarget, defaultcxxtarget) - // Convert file name to replace. - binit(&b); - bwritestr(&b, file); - if(slash[0] == '/') - bsubst(&b, "/go/zdefaultcc.go", "/cgo/zdefaultcc.go"); - else - bsubst(&b, "\\go\\zdefaultcc.go", "\\cgo\\zdefaultcc.go"); - writefile(&out, bstr(&b), 0); + writefile(out, file, 0) - bfree(&b); - bfree(&out); + // Convert file name to replace: turn go into cgo. + i := len(file) - len("go/zdefaultcc.go") + file = file[:i] + "c" + file[i:] + writefile(out, file, 0) } diff --git a/src/cmd/dist/buildruntime.go b/src/cmd/dist/buildruntime.go index add6897682..c0ec2efbd6 100644 --- a/src/cmd/dist/buildruntime.go +++ b/src/cmd/dist/buildruntime.go @@ -2,7 +2,12 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -#include "a.h" +package main + +import ( + "fmt" + "os" +) /* * Helpers for building runtime. @@ -14,55 +19,28 @@ // const defaultGoroot = <goroot> // const theVersion = <version> // -void -mkzversion(char *dir, char *file) -{ - Buf b, out; - - USED(dir); - - 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" - "var buildVersion = theVersion\n", goroot_final, goversion)); - - writefile(&out, file, 0); - - bfree(&b); - bfree(&out); +func mkzversion(dir, file string) { + out := fmt.Sprintf( + "// auto generated by go tool dist\n"+ + "\n"+ + "package runtime\n"+ + "\n"+ + "const defaultGoroot = `%s`\n"+ + "const theVersion = `%s`\n"+ + "var buildVersion = theVersion\n", goroot_final, goversion) + + writefile(out, file, 0) } // mkzexperiment writes zaexperiment.h (sic): // // #define GOEXPERIMENT "experiment string" // -void -mkzexperiment(char *dir, char *file) -{ - Buf b, out, exp; - - USED(dir); - - binit(&b); - binit(&out); - binit(&exp); - - xgetenv(&exp, "GOEXPERIMENT"); - bwritestr(&out, bprintf(&b, - "// auto generated by go tool dist\n" - "\n" - "#define GOEXPERIMENT \"%s\"\n", bstr(&exp))); +func mkzexperiment(dir, file string) { + out := fmt.Sprintf( + "// auto generated by go tool dist\n"+ + "\n"+ + "#define GOEXPERIMENT \"%s\"\n", os.Getenv("GOEXPERIMENT")) - writefile(&out, file, 0); - - bfree(&b); - bfree(&out); - bfree(&exp); + writefile(out, file, 0) } diff --git a/src/cmd/dist/cpuid_386.s b/src/cmd/dist/cpuid_386.s new file mode 100644 index 0000000000..853824a1bc --- /dev/null +++ b/src/cmd/dist/cpuid_386.s @@ -0,0 +1,14 @@ +// Copyright 2015 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. + +TEXT ·cpuid(SB),$0-8 + MOVL ax+4(FP), AX + CPUID + MOVL info+0(FP), DI + MOVL AX, 0(DI) + MOVL BX, 4(DI) + MOVL CX, 8(DI) + MOVL DX, 12(DI) + RET + diff --git a/src/cmd/dist/cpuid_amd64.s b/src/cmd/dist/cpuid_amd64.s new file mode 100644 index 0000000000..dbb1085e89 --- /dev/null +++ b/src/cmd/dist/cpuid_amd64.s @@ -0,0 +1,14 @@ +// Copyright 2015 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. + +TEXT ·cpuid(SB),$0-12 + MOVL ax+8(FP), AX + CPUID + MOVQ info+0(FP), DI + MOVL AX, 0(DI) + MOVL BX, 4(DI) + MOVL CX, 8(DI) + MOVL DX, 12(DI) + RET + diff --git a/src/cmd/dist/cpuid_default.s b/src/cmd/dist/cpuid_default.s new file mode 100644 index 0000000000..e5bfd183d9 --- /dev/null +++ b/src/cmd/dist/cpuid_default.s @@ -0,0 +1,10 @@ +// Copyright 2015 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. + +// +build !386,!amd64 + +#include "textflag.h" + +TEXT ·cpuid(SB),NOSPLIT,$0-0 + RET diff --git a/src/cmd/dist/main.go b/src/cmd/dist/main.go index fad01802a5..a2ac65ee87 100644 --- a/src/cmd/dist/main.go +++ b/src/cmd/dist/main.go @@ -2,41 +2,84 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -#include "a.h" +package main -int vflag; -int sflag; -char *argv0; +import ( + "flag" + "fmt" + "os" + "strconv" +) // cmdtab records the available commands. -static struct { - char *name; - void (*f)(int, char**); -} cmdtab[] = { +var cmdtab = []struct { + name string + f func() +}{ {"banner", cmdbanner}, {"bootstrap", cmdbootstrap}, {"clean", cmdclean}, {"env", cmdenv}, {"install", cmdinstall}, {"version", cmdversion}, -}; +} // The OS-specific main calls into the portable code here. -void -xmain(int argc, char **argv) -{ - int i; - - if(argc <= 1) - usage(); - - for(i=0; i<nelem(cmdtab); i++) { - if(streq(cmdtab[i].name, argv[1])) { - cmdtab[i].f(argc-1, argv+1); - return; +func xmain() { + if len(os.Args) < 2 { + usage() + } + cmd := os.Args[1] + os.Args = os.Args[1:] // for flag parsing during cmd + for _, ct := range cmdtab { + if ct.name == cmd { + flag.Usage = func() { + fmt.Fprintf(os.Stderr, "usage: go tool dist %s [options]\n", cmd) + flag.PrintDefaults() + os.Exit(2) + } + ct.f() + return + } + } + + xprintf("unknown command %s\n", cmd) + usage() +} + +func xflagparse(maxargs int) { + flag.Var((*count)(&vflag), "v", "verbosity") + flag.Parse() + if maxargs >= 0 && flag.NArg() > maxargs { + flag.Usage() + } +} + +// count is a flag.Value that is like a flag.Bool and a flag.Int. +// If used as -name, it increments the count, but -name=x sets the count. +// Used for verbose flag -v. +type count int + +func (c *count) String() string { + return fmt.Sprint(int(*c)) +} + +func (c *count) Set(s string) error { + switch s { + case "true": + *c++ + case "false": + *c = 0 + default: + n, err := strconv.Atoi(s) + if err != nil { + return fmt.Errorf("invalid count %q", s) } + *c = count(n) } + return nil +} - xprintf("unknown command %s\n", argv[1]); - usage(); +func (c *count) IsBoolFlag() bool { + return true } diff --git a/src/cmd/dist/sys_default.go b/src/cmd/dist/sys_default.go new file mode 100644 index 0000000000..ab97f19b3d --- /dev/null +++ b/src/cmd/dist/sys_default.go @@ -0,0 +1,10 @@ +// Copyright 2015 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. + +// +build !windows,!plan9 + +package main + +func sysinit() { +} diff --git a/src/cmd/dist/sys_windows.go b/src/cmd/dist/sys_windows.go new file mode 100644 index 0000000000..c6867fb895 --- /dev/null +++ b/src/cmd/dist/sys_windows.go @@ -0,0 +1,49 @@ +// Copyright 2015 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. + +package main + +import ( + "syscall" + "unsafe" +) + +var ( + modkernel32 = syscall.NewLazyDLL("kernel32.dll") + procGetSystemInfo = syscall.NewProc("GetSystemInfo") +) + +// see http://msdn.microsoft.com/en-us/library/windows/desktop/ms724958(v=vs.85).aspx +type systeminfo struct { + wProcessorArchitecture uint16 + wReserved uint16 + dwPageSize uint32 + lpMinimumApplicationAddress uintptr + lpMaximumApplicationAddress uintptr + dwActiveProcessorMask uintptr + dwNumberOfProcessors uint32 + dwProcessorType uint32 + dwAllocationGranularity uint32 + wProcessorLevel uint16 + wProcessorRevision uint16 +} + +const ( + PROCESSOR_ARCHITECTURE_AMD64 = 9 + PROCESSOR_ARCHITECTURE_INTEL = 0 +) + +var sysinfo systeminfo + +func sysinit() { + syscall.Syscall(procGetSystemInfo.Addr(), 1, uintptr(unsafe.Pointer(&sysinfo)), 0, 0) + switch sysinfo.wProcessorArchitecture { + case PROCESSOR_ARCHITECTURE_AMD64: + gohostarch = "amd64" + case PROCESSOR_ARCHITECTURE_INTEL: + gohostarch = "386" + default: + fatal("unknown processor architecture") + } +} diff --git a/src/cmd/dist/util.go b/src/cmd/dist/util.go index 0fd17c1509..4628eead80 100644 --- a/src/cmd/dist/util.go +++ b/src/cmd/dist/util.go @@ -2,722 +2,375 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// These #ifdefs are being used as a substitute for -// build configuration, so that on any system, this -// tool can be built with the local equivalent of -// cc *.c -// -#ifndef WIN32 -#ifndef PLAN9 - -#include "a.h" -#include <unistd.h> -#include <dirent.h> -#include <sys/stat.h> -#include <sys/wait.h> -#include <sys/param.h> -#include <sys/utsname.h> -#include <fcntl.h> -#include <string.h> -#include <stdio.h> -#include <stdlib.h> -#include <errno.h> -#include <stdarg.h> -#include <setjmp.h> -#include <signal.h> - -// bprintf replaces the buffer with the result of the printf formatting -// and returns a pointer to the NUL-terminated buffer contents. -char* -bprintf(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); -} - -// 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) -{ - int n; - - for(;;) { - bgrow(b, 4096); - n = read(fd, b->p+b->len, 4096); - if(n < 0) - fatal("read: %s", strerror(errno)); - if(n == 0) - break; - b->len += n; - } -} - -// xgetenv replaces b with the value of the named environment variable. -void -xgetenv(Buf *b, char *name) -{ - char *p; - - breset(b); - p = getenv(name); - if(p != NULL) - 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. -// If mode is CheckExit, run calls fatal if the command is not successful. -void -run(Buf *b, char *dir, int mode, char *cmd, ...) -{ - va_list arg; - Vec argv; - char *p; - - vinit(&argv); - vadd(&argv, cmd); - va_start(arg, cmd); - while((p = va_arg(arg, char*)) != nil) - vadd(&argv, p); - va_end(arg); - - runv(b, dir, mode, &argv); - - vfree(&argv); -} - -// runv is like run but takes a vector. -void -runv(Buf *b, char *dir, int mode, Vec *argv) -{ - 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; - Buf *b; -} bg[MAXBG]; -static int nbg; -static int maxnbg = nelem(bg); - -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 >= maxnbg) - bgwait1(); - - // Generate a copy of the command to show in a log. - // Substitute $WORK for the work directory. - binit(&cmd); - for(i=0; i<argv->len; i++) { - if(i > 0) - bwritestr(&cmd, " "); - q = argv->p[i]; - if(workdir != nil && hasprefix(q, workdir)) { - bwritestr(&cmd, "$WORK"); - q += strlen(workdir); +package main + +import ( + "fmt" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "runtime" + "sort" + "strconv" + "strings" + "sync" + "sync/atomic" + "time" +) + +// pathf is fmt.Sprintf for generating paths +// (on windows it turns / into \ after the printf). +func pathf(format string, args ...interface{}) string { + return filepath.Clean(fmt.Sprintf(format, args...)) +} + +// filter returns a slice containing the elements x from list for which f(x) == true. +func filter(list []string, f func(string) bool) []string { + var out []string + for _, x := range list { + if f(x) { + out = append(out, x) } - bwritestr(&cmd, q); - } - if(vflag > 1) - errprintf("%s\n", bstr(&cmd)); - - if(b != nil) { - breset(b); - if(pipe(p) < 0) - fatal("pipe: %s", strerror(errno)); - } - - switch(pid = fork()) { - case -1: - fatal("fork: %s", strerror(errno)); - case 0: - if(b != nil) { - close(0); - close(p[0]); - dup2(p[1], 1); - dup2(p[1], 2); - if(p[1] > 2) - close(p[1]); + } + return out +} + +// uniq returns a sorted slice containing the unique elements of list. +func uniq(list []string) []string { + out := make([]string, len(list)) + copy(out, list) + sort.Strings(out) + keep := out[:0] + for _, x := range out { + if len(keep) == 0 || keep[len(keep)-1] != x { + keep = append(keep, x) + } + } + return keep +} + +// splitlines returns a slice with the result of splitting +// the input p after each \n. +func splitlines(p string) []string { + return strings.SplitAfter(p, "\n") +} + +// splitfields replaces the vector v with the result of splitting +// the input p into non-empty fields containing no spaces. +func splitfields(p string) []string { + return strings.Fields(p) +} + +const ( + CheckExit = 1 << iota + ShowOutput + Background +) + +var outputLock sync.Mutex + +// run runs the command line cmd in dir. +// If mode has ShowOutput set, run collects cmd's output and returns it as a string; +// otherwise, run prints cmd's output to standard output after the command finishes. +// If mode has CheckExit set and the command fails, run calls fatal. +// If mode has Background set, this command is being run as a +// Background job. Only bgrun should use the Background mode, +// not other callers. +func run(dir string, mode int, cmd ...string) string { + if vflag > 1 { + errprintf("run: %s\n", strings.Join(cmd, " ")) + } + + xcmd := exec.Command(cmd[0], cmd[1:]...) + xcmd.Dir = dir + var err error + data, err := xcmd.CombinedOutput() + if err != nil && mode&CheckExit != 0 { + outputLock.Lock() + if len(data) > 0 { + xprintf("%s\n", data) + } + outputLock.Unlock() + atomic.AddInt32(&ndone, +1) + die := func() { + time.Sleep(100 * time.Millisecond) + fatal("FAILED: %v", strings.Join(cmd, " ")) } - if(dir != nil) { - if(chdir(dir) < 0) { - fprintf(stderr, "chdir %s: %s\n", dir, strerror(errno)); - _exit(1); - } + if mode&Background != 0 { + // This is a background run, and fatal will + // wait for it to finish before exiting. + // If we call fatal directly, that's a deadlock. + // Instead, call fatal in a background goroutine + // and let this run return normally, so that + // fatal can wait for it to finish. + go die() + } else { + die() } - 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); - } - if(b != nil) { - close(p[1]); - breadfrom(b, p[0]); - close(p[0]); - } - - if(nbg < 0) - fatal("bad bookkeeping"); - bg[nbg].pid = pid; - bg[nbg].mode = mode; - bg[nbg].cmd = btake(&cmd); - bg[nbg].b = b; - nbg++; - - if(wait) - bgwait(); - - bfree(&cmd); -} - -// bgwait1 waits for a single background job. -static void -bgwait1(void) -{ - int i, pid, status, mode; - char *cmd; - Buf *b; - - errno = 0; - while((pid = wait(&status)) < 0) { - if(errno != EINTR) - fatal("waitpid: %s", strerror(errno)); - } - 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; - b = bg[i].b; - bg[i].b = nil; - bg[i] = bg[--nbg]; - - if(mode == CheckExit && (!WIFEXITED(status) || WEXITSTATUS(status) != 0)) { - if(b != nil) - xprintf("%s\n", bstr(b)); - 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. -void -xgetwd(Buf *b) -{ - char buf[MAXPATHLEN]; - - breset(b); - if(getcwd(buf, MAXPATHLEN) == nil) - fatal("getcwd: %s", strerror(errno)); - bwritestr(b, buf); -} - -// xrealwd replaces b with the 'real' name for the given path. -// real is defined as what getcwd returns in that directory. -void -xrealwd(Buf *b, char *path) -{ - int fd; - - fd = open(".", 0); - if(fd < 0) - fatal("open .: %s", strerror(errno)); - if(chdir(path) < 0) - fatal("chdir %s: %s", path, strerror(errno)); - xgetwd(b); - if(fchdir(fd) < 0) - fatal("fchdir: %s", strerror(errno)); - close(fd); + } + if mode&ShowOutput != 0 { + os.Stdout.Write(data) + } + return string(data) +} + +var maxbg = 4 /* maximum number of jobs to run at once */ + +var ( + bgwork = make(chan func()) + bgdone = make(chan struct{}, 1e6) + nwork int32 + ndone int32 +) + +func bginit() { + for i := 0; i < maxbg; i++ { + go bghelper() + } +} + +func bghelper() { + for { + (<-bgwork)() + } +} + +// bgrun is like run but runs the command in the background. +// CheckExit|ShowOutput mode is implied (since output cannot be returned). +func bgrun(dir string, cmd ...string) { + bgwork <- func() { + run(dir, CheckExit|ShowOutput|Background, cmd...) + } +} + +// bgwait waits for pending bgruns to finish. +func bgwait() { + var wg sync.WaitGroup + wg.Add(maxbg) + for i := 0; i < maxbg; i++ { + bgwork <- func() { + wg.Done() + wg.Wait() + } + } + wg.Wait() +} + +// xgetwd returns the current directory. +func xgetwd() string { + wd, err := os.Getwd() + if err != nil { + fatal("%s", err) + } + return wd +} + +// xrealwd returns the 'real' name for the given path. +// real is defined as what xgetwd returns in that directory. +func xrealwd(path string) string { + old := xgetwd() + if err := os.Chdir(path); err != nil { + fatal("chdir %s: %v", path, err) + } + real := xgetwd() + if err := os.Chdir(old); err != nil { + fatal("chdir %s: %v", old, err) + } + return real } // isdir reports whether p names an existing directory. -bool -isdir(char *p) -{ - struct stat st; - - return stat(p, &st) >= 0 && S_ISDIR(st.st_mode); +func isdir(p string) bool { + fi, err := os.Stat(p) + return err == nil && fi.IsDir() } // isfile reports whether p names an existing file. -bool -isfile(char *p) -{ - struct stat st; - - return stat(p, &st) >= 0 && S_ISREG(st.st_mode); +func isfile(p string) bool { + fi, err := os.Stat(p) + return err == nil && fi.Mode().IsRegular() } // mtime returns the modification time of the file p. -Time -mtime(char *p) -{ - struct stat st; - - if(stat(p, &st) < 0) - return 0; - return (Time)st.st_mtime*1000000000LL; +func mtime(p string) time.Time { + fi, err := os.Stat(p) + if err != nil { + return time.Time{} + } + return fi.ModTime() } // isabs reports whether p is an absolute path. -bool -isabs(char *p) -{ - return hasprefix(p, "/"); -} - -// readfile replaces b with the content of the named file. -void -readfile(Buf *b, char *file) -{ - int fd; - - breset(b); - fd = open(file, 0); - if(fd < 0) - fatal("open %s: %s", file, strerror(errno)); - breadfrom(b, fd); - close(fd); +func isabs(p string) bool { + return filepath.IsAbs(p) +} + +// readfile returns the content of the named file. +func readfile(file string) string { + data, err := ioutil.ReadFile(file) + if err != nil { + fatal("%v", err) + } + return string(data) } // writefile writes b to the named file, creating it if needed. if // exec is non-zero, marks the file as executable. -void -writefile(Buf *b, char *file, int exec) -{ - int fd; - - fd = creat(file, 0666); - if(fd < 0) - fatal("create %s: %s", file, strerror(errno)); - if(write(fd, b->p, b->len) != b->len) - fatal("short write: %s", strerror(errno)); - if(exec) - fchmod(fd, 0755); - close(fd); +func writefile(b, file string, exec int) { + mode := os.FileMode(0666) + if exec != 0 { + mode = 0777 + } + err := ioutil.WriteFile(file, []byte(b), mode) + if err != nil { + fatal("%v", err) + } } // xmkdir creates the directory p. -void -xmkdir(char *p) -{ - if(mkdir(p, 0777) < 0) - fatal("mkdir %s: %s", p, strerror(errno)); +func xmkdir(p string) { + err := os.Mkdir(p, 0777) + if err != nil { + fatal("%v", err) + } } // xmkdirall creates the directory p and its parents, as needed. -void -xmkdirall(char *p) -{ - char *q; - - if(isdir(p)) - return; - q = strrchr(p, '/'); - if(q != nil) { - *q = '\0'; - xmkdirall(p); - *q = '/'; +func xmkdirall(p string) { + err := os.MkdirAll(p, 0777) + if err != nil { + fatal("%v", err) } - xmkdir(p); } // xremove removes the file p. -void -xremove(char *p) -{ - if(vflag > 2) - errprintf("rm %s\n", p); - unlink(p); +func xremove(p string) { + if vflag > 2 { + errprintf("rm %s\n", p) + } + os.Remove(p) } // xremoveall removes the file or directory tree rooted at p. -void -xremoveall(char *p) -{ - int i; - Buf b; - Vec dir; - - binit(&b); - vinit(&dir); - - if(isdir(p)) { - xreaddir(&dir, p); - for(i=0; i<dir.len; i++) { - bprintf(&b, "%s/%s", p, dir.p[i]); - xremoveall(bstr(&b)); - } - if(vflag > 2) - errprintf("rm %s\n", p); - rmdir(p); - } else { - if(vflag > 2) - errprintf("rm %s\n", p); - unlink(p); +func xremoveall(p string) { + if vflag > 2 { + errprintf("rm -r %s\n", p) } - - bfree(&b); - vfree(&dir); + os.RemoveAll(p) } // xreaddir replaces dst with a list of the names of the files in dir. // The names are relative to dir; they are not full paths. -void -xreaddir(Vec *dst, char *dir) -{ - DIR *d; - struct dirent *dp; - - vreset(dst); - d = opendir(dir); - if(d == nil) - fatal("opendir %s: %s", dir, strerror(errno)); - while((dp = readdir(d)) != nil) { - if(streq(dp->d_name, ".") || streq(dp->d_name, "..")) - continue; - vadd(dst, dp->d_name); +func xreaddir(dir string) []string { + f, err := os.Open(dir) + if err != nil { + fatal("%v", err) } - closedir(d); + defer f.Close() + names, err := f.Readdirnames(-1) + if err != nil { + fatal("reading %s: %v", dir, err) + } + return names } // xworkdir creates a new temporary directory to hold object files // and returns the name of that directory. -char* -xworkdir(void) -{ - Buf b; - char *p; - - binit(&b); - - xgetenv(&b, "TMPDIR"); - if(b.len == 0) - bwritestr(&b, "/var/tmp"); - if(b.p[b.len-1] != '/') - bwrite(&b, "/", 1); - bwritestr(&b, "go-cbuild-XXXXXX"); - p = bstr(&b); - if(mkdtemp(p) == nil) - fatal("mkdtemp(%s): %s", p, strerror(errno)); - p = btake(&b); - - bfree(&b); - - return p; +func xworkdir() string { + name, err := ioutil.TempDir("", "go-tool-dist-") + if err != nil { + fatal("%v", err) + } + return name } // fatal prints an error message to standard error and exits. -void -fatal(char *msg, ...) -{ - va_list arg; - - fflush(stdout); - fprintf(stderr, "go tool dist: "); - va_start(arg, msg); - vfprintf(stderr, msg, arg); - va_end(arg); - fprintf(stderr, "\n"); - - bgwait(); - exit(1); -} - -// xmalloc returns a newly allocated zeroed block of n bytes of memory. -// It calls fatal if it runs out of memory. -void* -xmalloc(int n) -{ - void *p; - - p = malloc(n); - if(p == nil) - fatal("out of memory"); - memset(p, 0, n); - return p; -} - -// xstrdup returns a newly allocated copy of p. -// It calls fatal if it runs out of memory. -char* -xstrdup(char *p) -{ - p = strdup(p); - if(p == nil) - fatal("out of memory"); - return p; -} - -// xrealloc grows the allocation p to n bytes and -// returns the new (possibly moved) pointer. -// It calls fatal if it runs out of memory. -void* -xrealloc(void *p, int n) -{ - p = realloc(p, n); - if(p == nil) - fatal("out of memory"); - return p; -} - -// xfree frees the result returned by xmalloc, xstrdup, or xrealloc. -void -xfree(void *p) -{ - free(p); -} - -// hassuffix reports whether p ends with suffix. -bool -hassuffix(char *p, char *suffix) -{ - int np, ns; - - np = strlen(p); - ns = strlen(suffix); - return np >= ns && streq(p+np-ns, suffix); -} - -// hasprefix reports whether p begins with prefix. -bool -hasprefix(char *p, char *prefix) -{ - return strncmp(p, prefix, strlen(prefix)) == 0; -} - -// contains reports whether sep appears in p. -bool -contains(char *p, char *sep) -{ - return strstr(p, sep) != nil; -} - -// streq reports whether p and q are the same string. -bool -streq(char *p, char *q) -{ - return strcmp(p, q) == 0; -} - -// lastelem returns the final path element in p. -char* -lastelem(char *p) -{ - char *out; - - out = p; - for(; *p; p++) - if(*p == '/') - out = p+1; - return out; -} - -// xmemmove copies n bytes from src to dst. -void -xmemmove(void *dst, void *src, int n) -{ - memmove(dst, src, n); -} - -// xmemcmp compares the n-byte regions starting at a and at b. -int -xmemcmp(void *a, void *b, int n) -{ - return memcmp(a, b, n); -} - -// xstrlen returns the length of the NUL-terminated string at p. -int -xstrlen(char *p) -{ - return strlen(p); +func fatal(format string, args ...interface{}) { + fmt.Fprintf(os.Stderr, "go tool dist: %s\n", fmt.Sprintf(format, args...)) + bgwait() + xexit(2) } +var atexits []func() + // xexit exits the process with return code n. -void -xexit(int n) -{ - exit(n); +func xexit(n int) { + for i := len(atexits) - 1; i >= 0; i-- { + atexits[i]() + } + os.Exit(n) } // xatexit schedules the exit-handler f to be run when the program exits. -void -xatexit(void (*f)(void)) -{ - atexit(f); +func xatexit(f func()) { + atexits = append(atexits, f) } // xprintf prints a message to standard output. -void -xprintf(char *fmt, ...) -{ - va_list arg; - - va_start(arg, fmt); - vprintf(fmt, arg); - va_end(arg); +func xprintf(format string, args ...interface{}) { + fmt.Printf(format, args...) } // errprintf prints a message to standard output. -void -errprintf(char *fmt, ...) -{ - va_list arg; - - va_start(arg, fmt); - vfprintf(stderr, fmt, arg); - va_end(arg); -} - -// xsetenv sets the environment variable $name to the given value. -void -xsetenv(char *name, char *value) -{ - setenv(name, value, 1); +func errprintf(format string, args ...interface{}) { + fmt.Fprintf(os.Stderr, format, args...) } // main takes care of OS-specific startup and dispatches to xmain. -int -main(int argc, char **argv) -{ - Buf b; - int osx; - struct utsname u; - - setvbuf(stdout, nil, _IOLBF, 0); - setvbuf(stderr, nil, _IOLBF, 0); - - setenv("TERM", "dumb", 1); // disable escape codes in clang errors - - binit(&b); - - slash = "/"; - -#if defined(__APPLE__) - gohostos = "darwin"; - // Even on 64-bit platform, darwin uname -m prints i386. - run(&b, nil, 0, "sysctl", "machdep.cpu.extfeatures", nil); - if(contains(bstr(&b), "EM64T")) - gohostarch = "amd64"; -#elif defined(__linux__) - gohostos = "linux"; -#elif defined(__DragonFly__) - gohostos = "dragonfly"; -#elif defined(__FreeBSD__) - gohostos = "freebsd"; -#elif defined(__FreeBSD_kernel__) - // detect debian/kFreeBSD. - // http://wiki.debian.org/Debian_GNU/kFreeBSD_FAQ#Q._How_do_I_detect_kfreebsd_with_preprocessor_directives_in_a_C_program.3F - gohostos = "freebsd"; -#elif defined(__OpenBSD__) - gohostos = "openbsd"; -#elif defined(__NetBSD__) - gohostos = "netbsd"; -#elif defined(__sun) && defined(__SVR4) - gohostos = "solaris"; - // Even on 64-bit platform, solaris uname -m prints i86pc. - run(&b, nil, 0, "isainfo", "-n", nil); - if(contains(bstr(&b), "amd64")) - gohostarch = "amd64"; - if(contains(bstr(&b), "i386")) - gohostarch = "386"; -#else - fatal("unknown operating system"); -#endif - - if(gohostarch == nil) { - if(uname(&u) < 0) - fatal("uname: %s", strerror(errno)); - if(contains(u.machine, "x86_64") || contains(u.machine, "amd64")) - gohostarch = "amd64"; - else if(hassuffix(u.machine, "86")) - gohostarch = "386"; - else if(contains(u.machine, "arm")) - gohostarch = "arm"; - else if(contains(u.machine, "ppc64le")) - gohostarch = "ppc64le"; - else if(contains(u.machine, "ppc64")) - gohostarch = "ppc64"; - else - fatal("unknown architecture: %s", u.machine); - } - - if(streq(gohostarch, "arm")) - maxnbg = 1; +func main() { + os.Setenv("TERM", "dumb") // disable escape codes in clang errors + + slash = string(filepath.Separator) + + gohostos = runtime.GOOS + switch gohostos { + case "darwin": + // Even on 64-bit platform, darwin uname -m prints i386. + if strings.Contains(run("", CheckExit, "sysctl", "machdep.cpu.extfeatures"), "EM64T") { + gohostarch = "amd64" + } + case "solaris": + // Even on 64-bit platform, solaris uname -m prints i86pc. + out := run("", CheckExit, "isainfo", "-n") + if strings.Contains(out, "amd64") { + gohostarch = "amd64" + } + if strings.Contains(out, "i386") { + gohostarch = "386" + } + case "plan9": + gohostarch = os.Getenv("objtype") + if gohostarch == "" { + fatal("$objtype is unset") + } + } + + sysinit() + + if gohostarch == "" { + // Default Unix system. + out := run("", CheckExit, "uname", "-m") + switch { + case strings.Contains(out, "x86_64"), strings.Contains(out, "amd64"): + gohostarch = "amd64" + case strings.Contains(out, "86"): + gohostarch = "386" + case strings.Contains(out, "arm"): + gohostarch = "arm" + case strings.Contains(out, "ppc64le"): + gohostarch = "ppc64le" + case strings.Contains(out, "ppc64"): + gohostarch = "ppc64" + default: + fatal("unknown architecture: %s", out) + } + } + + if gohostarch == "arm" { + maxbg = 1 + } + bginit() // The OS X 10.6 linker does not support external linking mode. // See golang.org/issue/5130. @@ -728,120 +381,77 @@ main(int argc, char **argv) // // Roughly, OS X 10.N shows up as uname release (N+4), // so OS X 10.6 is uname version 10 and OS X 10.8 is uname version 12. - if(streq(gohostos, "darwin")) { - if(uname(&u) < 0) - fatal("uname: %s", strerror(errno)); - osx = atoi(u.release) - 4; - if(osx <= 6) - goextlinkenabled = "0"; - if(osx >= 8) - defaultclang = 1; + if gohostos == "darwin" { + rel := run("", CheckExit, "uname", "-r") + if i := strings.Index(rel, "."); i >= 0 { + rel = rel[:i] + } + osx, _ := strconv.Atoi(rel) + if osx <= 6+4 { + goextlinkenabled = "0" + } + if osx >= 8+4 { + defaultclang = true + } } - init(); - xmain(argc, argv); - bfree(&b); - return 0; + xinit() + xmain() } -// xqsort is a wrapper for the C standard qsort. -void -xqsort(void *data, int n, int elemsize, int (*cmp)(const void*, const void*)) -{ - qsort(data, n, elemsize, cmp); +// xsamefile reports whether f1 and f2 are the same file (or dir) +func xsamefile(f1, f2 string) bool { + fi1, err1 := os.Stat(f1) + fi2, err2 := os.Stat(f2) + if err1 != nil || err2 != nil { + return f1 == f2 + } + return os.SameFile(fi1, fi2) } -// xstrcmp compares the NUL-terminated strings a and b. -int -xstrcmp(char *a, char *b) -{ - return strcmp(a, b); +func cpuid(info *[4]uint32, ax uint32) + +func cansse2() bool { + if gohostarch != "386" && gohostarch != "amd64" { + return false + } + + var info [4]uint32 + cpuid(&info, 1) + return info[3]&(1<<26) != 0 // SSE2 } -// xstrstr returns a pointer to the first occurrence of b in a. -char* -xstrstr(char *a, char *b) -{ - return strstr(a, b); +func xgetgoarm() string { + if goos == "nacl" { + // NaCl guarantees VFPv3 and is always cross-compiled. + return "7" + } + if gohostarch != "arm" || goos != gohostos { + // Conservative default for cross-compilation. + return "5" + } + if goos == "freebsd" { + // FreeBSD has broken VFP support. + return "5" + } + if xtryexecfunc(useVFPv3) { + return "7" + } + if xtryexecfunc(useVFPv1) { + return "6" + } + return "5" } -// xstrrchr returns a pointer to the final occurrence of c in p. -char* -xstrrchr(char *p, int c) -{ - return strrchr(p, c); +func xtryexecfunc(f func()) bool { + // TODO(rsc): Implement. + // The C cmd/dist used this to test whether certain assembly + // sequences could be executed properly. It used signals and + // timers and sigsetjmp, which is basically not possible in Go. + // We probably have to invoke ourselves as a subprocess instead, + // to contain the fault/timeout. + return false } -// xsamefile reports whether f1 and f2 are the same file (or dir) -int -xsamefile(char *f1, char *f2) -{ - return streq(f1, f2); // suffice for now -} - -sigjmp_buf sigill_jmpbuf; -static void sigillhand(int); - -// xtryexecfunc tries to execute function f, if any illegal instruction -// signal received in the course of executing that function, it will -// return 0, otherwise it will return 1. -// Some systems (notably NetBSD) will spin and spin when executing VFPv3 -// instructions on VFPv2 system (e.g. Raspberry Pi) without ever triggering -// SIGILL, so we set a 1-second alarm to catch that case. -int -xtryexecfunc(void (*f)(void)) -{ - int r; - r = 0; - signal(SIGILL, sigillhand); - signal(SIGALRM, sigillhand); - alarm(1); - if(sigsetjmp(sigill_jmpbuf, 1) == 0) { - f(); - r = 1; - } - signal(SIGILL, SIG_DFL); - alarm(0); - signal(SIGALRM, SIG_DFL); - return r; -} - -// SIGILL handler helper -static void -sigillhand(int signum) -{ - USED(signum); - siglongjmp(sigill_jmpbuf, 1); -} - -static void -__cpuid(int dst[4], int ax) -{ -#ifdef __i386__ - // we need to avoid ebx on i386 (esp. when -fPIC). - asm volatile( - "mov %%ebx, %%edi\n\t" - "cpuid\n\t" - "xchgl %%ebx, %%edi" - : "=a" (dst[0]), "=D" (dst[1]), "=c" (dst[2]), "=d" (dst[3]) - : "0" (ax)); -#elif defined(__x86_64__) - asm volatile("cpuid" - : "=a" (dst[0]), "=b" (dst[1]), "=c" (dst[2]), "=d" (dst[3]) - : "0" (ax)); -#else - dst[0] = dst[1] = dst[2] = dst[3] = 0; -#endif -} - -bool -cansse2(void) -{ - int info[4]; - - __cpuid(info, 1); - return (info[3] & (1<<26)) != 0; // SSE2 -} - -#endif // PLAN9 -#endif // __WINDOWS__ +func useVFPv1() +func useVFPv3() diff --git a/src/cmd/dist/vfp_arm.s b/src/cmd/dist/vfp_arm.s new file mode 100644 index 0000000000..3cc11b298b --- /dev/null +++ b/src/cmd/dist/vfp_arm.s @@ -0,0 +1,15 @@ +// Copyright 2015 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 "textflag.h" + +// try to run "vmov.f64 d0, d0" instruction +TEXT useVFPv1(SB),NOSPLIT,$0 + VMOV.F64 D0, D0 + RET + +// try to run VFPv3-only "vmov.f64 d0, #112" instruction +TEXT useVFPv3(SB),NOSPLIT,$0 + VMOV.F64 $112, D0 + RET diff --git a/src/cmd/dist/vfp_default.s b/src/cmd/dist/vfp_default.s new file mode 100644 index 0000000000..c795b357f7 --- /dev/null +++ b/src/cmd/dist/vfp_default.s @@ -0,0 +1,14 @@ +// Copyright 2015 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. + +// +build !arm + +#include "textflag.h" + +TEXT ·useVFPv1(SB),NOSPLIT,$0 + RET + +TEXT ·useVFPv3(SB),NOSPLIT,$0 + RET + diff --git a/src/make.bash b/src/make.bash index a90937a77e..54c4d61249 100755 --- a/src/make.bash +++ b/src/make.bash @@ -3,6 +3,8 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. +# See golang.org/s/go15bootstrap for an overview of the build process. + # Environment variables that control make.bash: # # GOROOT_FINAL: The expected final Go root, baked into binaries. @@ -110,26 +112,16 @@ rm -f ./runtime/runtime_defs.go # Finally! Run the build. -echo '##### Building C bootstrap tool.' +echo '##### Building Go bootstrap tool.' echo cmd/dist export GOROOT="$(cd .. && pwd)" -GOROOT_FINAL="${GOROOT_FINAL:-$GOROOT}" -DEFGOROOT='-DGOROOT_FINAL="'"$GOROOT_FINAL"'"' - -mflag="" -case "$GOHOSTARCH" in -386) mflag=-m32;; -amd64) mflag=-m64;; -esac -if [ "$(uname)" == "Darwin" ]; then - # golang.org/issue/5261 - mflag="$mflag -mmacosx-version-min=10.6" -fi -# if gcc does not exist and $CC is not set, try clang if available. -if [ -z "$CC" -a -z "$(type -t gcc)" -a -n "$(type -t clang)" ]; then - export CC=clang CXX=clang++ +GOROOT_BOOTSTRAP=${GOROOT_BOOTSTRAP:-$HOME/go1.4} +if [ ! -x "$GOROOT_BOOTSTRAP/bin/go" ]; then + echo "ERROR: Cannot find $GOROOT_BOOTSTRAP/bin/go." >&2 + echo "Set \$GOROOT_BOOTSTRAP to a working Go tree >= Go 1.4." >&2 fi -${CC:-gcc} $mflag -O2 -Wall -Werror -o cmd/dist/dist -Icmd/dist "$DEFGOROOT" cmd/dist/*.c +rm -f cmd/dist/dist +GOROOT="$GOROOT_BOOTSTRAP" "$GOROOT_BOOTSTRAP/bin/go" build -o cmd/dist/dist ./cmd/dist # -e doesn't propagate out of eval, so check success by hand. eval $(./cmd/dist/dist env -p || echo FAIL=true) diff --git a/src/sudo.bash b/src/sudo.bash deleted file mode 100755 index 33254c2c5e..0000000000 --- a/src/sudo.bash +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env bash -# 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. - -set -e - -case "`uname`" in -Darwin) - ;; -*) - exit 0 -esac - -# Check that the go command exists -if ! go help >/dev/null 2>&1; then - echo "The go command is not in your PATH." >&2 - exit 2 -fi - -eval $(go env) -if ! [ -x $GOTOOLDIR/prof ]; then - echo "You don't need to run sudo.bash." >&2 - exit 2 -fi - -if [[ ! -d /usr/local/bin ]]; then - echo 1>&2 'sudo.bash: problem with /usr/local/bin; cannot install tools.' - exit 2 -fi - -cd $(dirname $0) -for i in prof -do - # Remove old binaries if present - sudo rm -f /usr/local/bin/6$i - # Install new binaries - sudo cp $GOTOOLDIR/$i /usr/local/bin/go$i - sudo chgrp procmod /usr/local/bin/go$i - sudo chmod g+s /usr/local/bin/go$i -done |