diff options
Diffstat (limited to 'src/cmd/dist/util.go')
-rw-r--r-- | src/cmd/dist/util.go | 1132 |
1 files changed, 371 insertions, 761 deletions
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() |