| // 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 |
| // |
| #ifdef PLAN9 |
| |
| #include <u.h> |
| #include <libc.h> |
| #include <stdio.h> |
| #undef nil |
| #undef nelem |
| #include "a.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"); |
| 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 != nil) |
| 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 b1, cmd; |
| char *q; |
| |
| while(nbg >= maxnbg) |
| bgwait1(); |
| |
| binit(&b1); |
| binit(&cmd); |
| |
| if(!isabs(argv->p[0])) { |
| bpathf(&b1, "/bin/%s", argv->p[0]); |
| free(argv->p[0]); |
| argv->p[0] = xstrdup(bstr(&b1)); |
| } |
| |
| // Generate a copy of the command to show in a log. |
| // Substitute $WORK for the work directory. |
| 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"); |
| } |
| |
| switch(pid = fork()) { |
| case -1: |
| fatal("fork"); |
| case 0: |
| if(b != nil) { |
| close(0); |
| close(p[0]); |
| dup(p[1], 1); |
| dup(p[1], 2); |
| if(p[1] > 2) |
| close(p[1]); |
| } |
| if(dir != nil) { |
| if(chdir(dir) < 0) { |
| fprint(2, "chdir: %r\n"); |
| _exits("chdir"); |
| } |
| } |
| vadd(argv, nil); |
| exec(argv->p[0], argv->p); |
| fprint(2, "%s\n", bstr(&cmd)); |
| fprint(2, "exec: %r\n"); |
| _exits("exec"); |
| } |
| 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); |
| bfree(&b1); |
| } |
| |
| // bgwait1 waits for a single background job. |
| static void |
| bgwait1(void) |
| { |
| Waitmsg *w; |
| int i, mode; |
| char *cmd; |
| Buf *b; |
| |
| w = wait(); |
| if(w == nil) |
| fatal("wait"); |
| |
| for(i=0; i<nbg; i++) |
| if(bg[i].pid == w->pid) |
| goto ok; |
| fatal("wait: 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 && w->msg[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[4096]; |
| |
| breset(b); |
| if(getwd(buf, sizeof buf) == nil) |
| fatal("getwd"); |
| 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) |
| { |
| char buf[4096]; |
| int fd; |
| |
| fd = open(path, OREAD); |
| if(fd2path(fd, buf, sizeof buf) < 0) |
| fatal("fd2path"); |
| close(fd); |
| breset(b); |
| bwritestr(b, buf); |
| } |
| |
| // isdir reports whether p names an existing directory. |
| bool |
| isdir(char *p) |
| { |
| Dir *d; |
| ulong mode; |
| |
| d = dirstat(p); |
| if(d == nil) |
| return 0; |
| mode = d->mode; |
| free(d); |
| return (mode & DMDIR) == DMDIR; |
| } |
| |
| // isfile reports whether p names an existing file. |
| bool |
| isfile(char *p) |
| { |
| Dir *d; |
| ulong mode; |
| |
| d = dirstat(p); |
| if(d == nil) |
| return 0; |
| mode = d->mode; |
| free(d); |
| return (mode & DMDIR) == 0; |
| } |
| |
| // mtime returns the modification time of the file p. |
| Time |
| mtime(char *p) |
| { |
| Dir *d; |
| ulong t; |
| |
| d = dirstat(p); |
| if(d == nil) |
| return 0; |
| t = d->mtime; |
| free(d); |
| return (Time)t; |
| } |
| |
| // 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, OREAD); |
| if(fd < 0) |
| fatal("open %s", file); |
| breadfrom(b, fd); |
| close(fd); |
| } |
| |
| // writefile writes b to the named file, creating it if needed. |
| void |
| writefile(Buf *b, char *file, int exec) |
| { |
| int fd; |
| Dir d; |
| |
| fd = create(file, ORDWR, 0666); |
| if(fd < 0) |
| fatal("create %s", file); |
| if(write(fd, b->p, b->len) != b->len) |
| fatal("short write"); |
| if(exec) { |
| nulldir(&d); |
| d.mode = 0755; |
| dirfwstat(fd, &d); |
| } |
| close(fd); |
| } |
| |
| // xmkdir creates the directory p. |
| void |
| xmkdir(char *p) |
| { |
| int fd; |
| |
| if(isdir(p)) |
| return; |
| fd = create(p, OREAD, 0777|DMDIR); |
| close(fd); |
| if(fd < 0) |
| fatal("mkdir %s", p); |
| } |
| |
| // 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); |
| 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); |
| remove(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; |
| int fd, i, n; |
| |
| vreset(dst); |
| |
| fd = open(dir, OREAD); |
| if(fd < 0) |
| fatal("open %s", dir); |
| n = dirreadall(fd, &d); |
| for(i=0; i<n; i++) |
| vadd(dst, d[i].name); |
| free(d); |
| close(fd); |
| } |
| |
| // xworkdir creates a new temporary directory to hold object files |
| // and returns the name of that directory. |
| char* |
| xworkdir(void) |
| { |
| Buf b; |
| char *p; |
| int fd, tries; |
| |
| binit(&b); |
| |
| fd = 0; |
| for(tries=0; tries<1000; tries++) { |
| bprintf(&b, "/tmp/go-cbuild-%06x", nrand((1<<24)-1)); |
| fd = create(bstr(&b), OREAD|OEXCL, 0700|DMDIR); |
| if(fd >= 0) |
| goto done; |
| } |
| fatal("xworkdir create"); |
| |
| done: |
| close(fd); |
| p = btake(&b); |
| |
| bfree(&b); |
| return p; |
| } |
| |
| // fatal prints an error message to standard error and exits. |
| void |
| fatal(char *msg, ...) |
| { |
| char buf[ERRMAX]; |
| va_list arg; |
| |
| rerrstr(buf, sizeof buf); |
| |
| fflush(stdout); |
| fprintf(stderr, "go tool dist: "); |
| va_start(arg, msg); |
| vfprintf(stderr, msg, arg); |
| va_end(arg); |
| |
| if(buf[0]) |
| fprintf(stderr, ": %s", buf); |
| fprintf(stderr, "\n"); |
| |
| bgwait(); |
| exits(msg); |
| } |
| |
| // 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) |
| { |
| char buf[32]; |
| |
| snprintf(buf, sizeof buf, "%d", n); |
| exits(buf); |
| } |
| |
| // 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) |
| { |
| putenv(name, value); |
| } |
| |
| // main takes care of OS-specific startup and dispatches to xmain. |
| void |
| main(int argc, char **argv) |
| { |
| Buf b; |
| |
| setvbuf(stdout, nil, _IOLBF, BUFSIZ); |
| setvbuf(stderr, nil, _IOLBF, BUFSIZ); |
| |
| binit(&b); |
| |
| rfork(RFENVG); |
| |
| slash = "/"; |
| gohostos = "plan9"; |
| |
| xgetenv(&b, "objtype"); |
| if(b.len == 0) |
| fatal("$objtype is unset"); |
| gohostarch = btake(&b); |
| |
| srand(time(0)+getpid()); |
| init(); |
| xmain(argc, argv); |
| |
| bfree(&b); |
| exits(nil); |
| } |
| |
| // 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 |
| } |
| |
| // 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. |
| int |
| xtryexecfunc(void (*f)(void)) |
| { |
| USED(f); |
| return 0; // suffice for now |
| } |
| |
| bool |
| cansse2(void) |
| { |
| // if we had access to cpuid, could answer this question |
| // less conservatively. |
| return 0; |
| } |
| |
| #endif // PLAN9 |