// 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 #include #include #include #include #include #include #include #include #include #include #include #include #include // 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; ilen; 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 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 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"; #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 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__