| // 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> |
| |
| // 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(strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) |
| 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 && strcmp(p+np-ns, suffix) == 0; |
| } |
| |
| // 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"; |
| #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 |
| fatal("unknown architecture: %s", u.machine); |
| } |
| |
| if(strcmp(gohostarch, "arm") == 0) |
| 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(strcmp(gohostos, "darwin") == 0) { |
| 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__ |