// Copyright 2009 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. // Build a collection of go programs into a single package. #include #include #include #include void usage(void) { fprint(2, "usage: gobuild [-m] [packagename...]\n"); exits("usage"); } int chatty; int devnull; // fd of /dev/null int makefile; // generate Makefile char *thechar; // object character char *goos; char *goarch; char *goroot; char **oargv; int oargc; void writemakefile(void); int sourcefilenames(char***); void* emalloc(int n) { void *v; v = malloc(n); if(v == nil) sysfatal("out of memory"); memset(v, 0, n); return v; } void* erealloc(void *v, int n) { v = realloc(v, n); if(v == nil) sysfatal("out of memory"); return v; } // Info about when to compile a particular file. typedef struct Job Job; struct Job { char *name; char *pkg; int pass; }; Job *job; int njob; char **pkg; int npkg; // Run the command in argv. // Return -1 if it fails (non-zero exit status). // Return 0 on success. // Showoutput controls whether to let output from command display // on standard output and standard error. int run(char **argv, int showoutput) { int pid, i; Waitmsg *w; vlong n0, n1; char buf[100]; n0 = nsec(); pid = fork(); if(pid < 0) sysfatal("fork: %r"); if(pid == 0){ dup(devnull, 0); if(!showoutput) dup(devnull, 2); dup(2, 1); if(devnull > 2) close(devnull); exec(argv[0], argv); fprint(2, "exec %s: %r\n", argv[0]); exit(1); } while((w = waitfor(pid)) == nil) { rerrstr(buf, sizeof buf); if(strstr(buf, "interrupt")) continue; sysfatal("waitfor %d: %r", pid); } n1 = nsec(); if(chatty > 1){ fprint(2, "%5.3f", (n1-n0)/1.e9); for(i=0; argv[i]; i++) fprint(2, " %s", argv[i]); if(w->msg[0]) fprint(2, " [%s]", w->msg); fprint(2, "\n"); } if(w->msg[0]) return -1; return 0; } // Build the file using the compiler cc. // Return -1 on error, 0 on success. // If show is set, print the command and the output. int buildcc(char *cc, char *file, int show) { char *argv[3]; if(show) fprint(2, "$ %s %s\n", cc, file); argv[0] = cc; argv[1] = file; argv[2] = nil; return run(argv, show); } // Run ar to add the given files to pkg.a. void ar(char *pkg, char **file, int nfile) { char **arg; int i, n; char sixar[20]; char pkga[1000]; arg = emalloc((4+nfile)*sizeof arg[0]); n = 0; snprint(sixar, sizeof sixar, "%sar", thechar); snprint(pkga, sizeof pkga, "%s.a", pkg); arg[n++] = sixar; arg[n++] = "grc"; arg[n++] = pkga; for(i=0; in2 && strcmp(s+n1-n2, suffix) == 0) return 1; return 0; } // Return the name of the compiler for file. char* compiler(char *file) { static char buf[20]; if(suffix(file, ".go")) snprint(buf, sizeof buf, "%sg", thechar); else if(suffix(file, ".c")) snprint(buf, sizeof buf, "%sc", thechar); else if(suffix(file, ".s")) snprint(buf, sizeof buf, "%sa", thechar); else sysfatal("don't know how to build %s", file); return buf; } // Return the object name for file, replacing the // .c or .g or .a with .suffix. char* goobj(char *file, char *suffix) { char *p; p = strrchr(file, '.'); if(p == nil) sysfatal("don't know object name for %s", file); return smprint("%.*s.%s", utfnlen(file, p-file), file, suffix); } // Figure out package of .go file. // Maintain list of all packages seen so far. // Returned package string is in that list, // so caller can use pointer compares. char* getpkg(char *file) { Biobuf *b; char *p, *q; int i; if(!suffix(file, ".go")) return nil; if((b = Bopen(file, OREAD)) == nil) sysfatal("open %s: %r", file); while((p = Brdline(b, '\n')) != nil) { p[Blinelen(b)-1] = '\0'; while(*p == ' ' || *p == '\t') p++; if(strncmp(p, "package", 7) == 0 && (p[7] == ' ' || p[7] == '\t')) { p+=7; while(*p == ' ' || *p == '\t') p++; q = p+strlen(p); while(q > p && (*(q-1) == ' ' || *(q-1) == '\t')) *--q = '\0'; for(i=0; iargs, char*); if(s == nil){ fmtstrcpy(f, ""); return 0; } for(; *s; s+=n){ n = strlen(goarch); if(strncmp(s, goarch, n) == 0){ fmtstrcpy(f, "$(GOARCH)"); continue; } n = strlen(goos); if(strncmp(s, goos, n) == 0){ fmtstrcpy(f, "$(GOOS)"); continue; } n = chartorune(&r, s); fmtrune(f, r); } return 0; } // Makefile preamble template. char preamble[] = "O=%s\n" "GC=$(O)g\n" "CC=$(O)c -w\n" "AS=$(O)a\n" "AR=$(O)ar\n" "\n" "default: packages\n" "\n" "clean:\n" "\trm -f *.$O *.a $O.out\n" "\n" "test: packages\n" "\tgotest\n" "\n" "coverage: packages\n" "\tgotest\n" "\t6cov -g `pwd` | grep -v '^.*test\\.go:'\n" "\n" "%%.$O: %%.go\n" "\t$(GC) $*.go\n" "\n" "%%.$O: %%.c\n" "\t$(CC) $*.c\n" "\n" "%%.$O: %%.s\n" "\t$(AS) $*.s\n" "\n" ; void writemakefile(void) { Biobuf bout; vlong o; int i, k, l, pass; char **obj; int nobj; // Write makefile. Binit(&bout, 1, OWRITE); Bprint(&bout, "# DO NOT EDIT. Automatically generated by gobuild.\n"); o = Boffset(&bout); Bprint(&bout, "#"); for(i=0; i 60){ Bprint(&bout, "\\\n# "); o = Boffset(&bout); } Bprint(&bout, " %s", oargv[i]); } Bprint(&bout, " >Makefile\n"); Bprint(&bout, preamble, thechar); // O2=\ // os_file.$O\ // os_time.$O\ // obj = emalloc(njob*sizeof obj[0]); for(pass=0;; pass++) { nobj = 0; for(i=0; i 0) { Bprint(&bout, "\t$(AR) grc %s.a", pkg[i]); for(l=0; l 0; pass++) { // Run what we can. nfail = 0; nsuccess = 0; for(i=0; iname), j->name, 0) < 0) fail[nfail++] = j; else{ if(chatty == 1) fprint(2, "%s ", j->name); success[nsuccess++] = j; } } if(nsuccess == 0) { // Nothing ran; give up. for(i=0; iname), j->name, 1); } exits("stalemate"); } if(chatty == 1) fprint(2, "\n"); // Update archives. for(i=0; ipkg == pkg[i]) arfiles[narfiles++] = goobj(j->name, thechar); j->pass = pass; } if(narfiles > 0) ar(pkg[i], arfiles, narfiles); for(k=0; k