diff options
Diffstat (limited to 'src/cmd/dist/plan9.c')
-rw-r--r-- | src/cmd/dist/plan9.c | 764 |
1 files changed, 764 insertions, 0 deletions
diff --git a/src/cmd/dist/plan9.c b/src/cmd/dist/plan9.c new file mode 100644 index 000000000..8a7c0ab1c --- /dev/null +++ b/src/cmd/dist/plan9.c @@ -0,0 +1,764 @@ +// 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 wtih 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 returns 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 |