summaryrefslogtreecommitdiff
path: root/src/cmd/dist/util.go
diff options
context:
space:
mode:
authorRuss Cox <rsc@golang.org>2015-01-07 11:37:04 -0500
committerRuss Cox <rsc@golang.org>2015-01-10 19:15:37 +0000
commitad6ee36cac122894f7ea4043289c10a50e48ac01 (patch)
treeba42e5f60fa1d27c6ad46c6213a92bbcafa61636 /src/cmd/dist/util.go
parentce5cb037d171273f1a5294723234be5495c9d336 (diff)
downloadgo-git-ad6ee36cac122894f7ea4043289c10a50e48ac01.tar.gz
cmd/dist: remove C sources, rename some to Go files
This CL makes the next one have nice cross-file diffs. Change-Id: I9ce897dc505dea9923be4e823bae31f4f7fa2ee2 Reviewed-on: https://go-review.googlesource.com/2471 Reviewed-by: Ian Lance Taylor <iant@golang.org>
Diffstat (limited to 'src/cmd/dist/util.go')
-rw-r--r--src/cmd/dist/util.go847
1 files changed, 847 insertions, 0 deletions
diff --git a/src/cmd/dist/util.go b/src/cmd/dist/util.go
new file mode 100644
index 0000000000..0fd17c1509
--- /dev/null
+++ b/src/cmd/dist/util.go
@@ -0,0 +1,847 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// 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);
+ }
+ 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]);
+ }
+ if(dir != nil) {
+ if(chdir(dir) < 0) {
+ fprintf(stderr, "chdir %s: %s\n", dir, strerror(errno));
+ _exit(1);
+ }
+ }
+ 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);
+}
+
+// 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);
+}
+
+// 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);
+}
+
+// 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;
+}
+
+// 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);
+}
+
+// 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);
+}
+
+// xmkdir creates the directory p.
+void
+xmkdir(char *p)
+{
+ if(mkdir(p, 0777) < 0)
+ fatal("mkdir %s: %s", p, strerror(errno));
+}
+
+// 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 = '/';
+ }
+ xmkdir(p);
+}
+
+// xremove removes the file p.
+void
+xremove(char *p)
+{
+ if(vflag > 2)
+ errprintf("rm %s\n", p);
+ unlink(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);
+ }
+
+ bfree(&b);
+ vfree(&dir);
+}
+
+// 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);
+ }
+ closedir(d);
+}
+
+// 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;
+}
+
+// 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);
+}
+
+// xexit exits the process with return code n.
+void
+xexit(int n)
+{
+ exit(n);
+}
+
+// xatexit schedules the exit-handler f to be run when the program exits.
+void
+xatexit(void (*f)(void))
+{
+ atexit(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);
+}
+
+// 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);
+}
+
+// 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;
+
+ // The OS X 10.6 linker does not support external linking mode.
+ // See golang.org/issue/5130.
+ //
+ // OS X 10.6 does not work with clang either, but OS X 10.9 requires it.
+ // It seems to work with OS X 10.8, so we default to clang for 10.8 and later.
+ // See golang.org/issue/5822.
+ //
+ // 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;
+ }
+
+ init();
+ xmain(argc, argv);
+ bfree(&b);
+ return 0;
+}
+
+// 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);
+}
+
+// xstrcmp compares the NUL-terminated strings a and b.
+int
+xstrcmp(char *a, char *b)
+{
+ return strcmp(a, b);
+}
+
+// xstrstr returns a pointer to the first occurrence of b in a.
+char*
+xstrstr(char *a, char *b)
+{
+ return strstr(a, b);
+}
+
+// xstrrchr returns a pointer to the final occurrence of c in p.
+char*
+xstrrchr(char *p, int c)
+{
+ return strrchr(p, c);
+}
+
+// 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__