summaryrefslogtreecommitdiff
path: root/src/cmd/dist/util.go
diff options
context:
space:
mode:
authorRuss Cox <rsc@golang.org>2015-01-07 11:38:00 -0500
committerRuss Cox <rsc@golang.org>2015-01-10 19:16:00 +0000
commit20a10e7ddd1b2305f1279043ec281301c9199359 (patch)
tree674cac1232c8bb87c64c1c42b306808769a9ac00 /src/cmd/dist/util.go
parentad6ee36cac122894f7ea4043289c10a50e48ac01 (diff)
downloadgo-git-20a10e7ddd1b2305f1279043ec281301c9199359.tar.gz
build: require old Go to build new Go (and convert cmd/dist to Go)
This CL introduces the bootstrap requirement that in order to build the current release (or development version) of Go, you need an older Go release (1.4 or newer) already installed. This requirement is the whole point of this CL. To enforce the requirement, convert cmd/dist from C to Go. With this bootstrapping out of the way, we can move on to replacing other, larger C programs like the Go compiler, the assemblers, and the linker. See golang.org/s/go15bootstrap for details. Change-Id: I53fd08ddacf3df9fae94fe2c986dba427ee4a21d Reviewed-on: https://go-review.googlesource.com/2470 Reviewed-by: Ian Lance Taylor <iant@golang.org> Reviewed-by: Rob Pike <r@golang.org>
Diffstat (limited to 'src/cmd/dist/util.go')
-rw-r--r--src/cmd/dist/util.go1132
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()