void
usage(void)
{
- fprint(2, "usage: gobuild [-m] packagename *.go *.c *.s\n");
+ fprint(2, "usage: gobuild [-m] [packagename...]\n");
exits("usage");
}
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).
int pid, i;
Waitmsg *w;
vlong n0, n1;
+ char buf[100];
n0 = nsec();
pid = fork();
sysfatal("fork: %r");
if(pid == 0){
dup(devnull, 0);
- if(!showoutput){
- dup(devnull, 1);
+ if(!showoutput)
dup(devnull, 2);
- }else{
- dup(2, 1);
- }
+ dup(2, 1);
if(devnull > 2)
close(devnull);
exec(argv[0], argv);
fprint(2, "exec %s: %r\n", argv[0]);
exit(1);
}
- w = waitfor(pid);
- n1 = nsec();
- if(w == nil)
+ 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++)
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; i<nfile; i++)
+ arg[n++] = file[i];
+ arg[n] = nil;
+
+ if(run(arg, 1) < 0)
+ sysfatal("ar: %r");
+}
+
// Return bool whether s ends in suffix.
int
suffix(char *s, char *suffix)
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; i<npkg; i++) {
+ if(strcmp(pkg[i], p) == 0) {
+ Bterm(b);
+ return pkg[i];
+ }
+ }
+ npkg++;
+ pkg = erealloc(pkg, npkg*sizeof pkg[0]);
+ pkg[i] = emalloc(strlen(p)+1);
+ strcpy(pkg[i], p);
+ Bterm(b);
+ return pkg[i];
+ }
+ }
+ Bterm(b);
+ return nil;
+}
+
// Format name using $(GOOS) and $(GOARCH).
int
dollarfmt(Fmt *f)
"AS=$(O)a\n"
"AR=$(O)ar\n"
"\n"
- "PKG=%s.a\n"
- "PKGDIR=$(GOROOT)/pkg%s\n"
- "\n"
- "install: $(PKG)\n"
- "\tmv $(PKG) $(PKGDIR)/$(PKG)\n"
- "\n"
- "nuke: clean\n"
- "\trm -f $(PKGDIR)/$(PKG)\n"
+ "default: packages\n"
"\n"
"clean:\n"
- "\trm -f *.$O *.a $(PKG)\n"
+ "\trm -f *.$O *.a\n"
+ "\n"
+ "test: packages\n"
+ "\tgotest\n"
"\n"
"%%.$O: %%.go\n"
"\t$(GC) $*.go\n"
;
void
-main(int argc, char **argv)
+writemakefile(void)
{
- int i, o, p, n, pass, nar, njob, nthis, nnext, oargc;
- char **ar, **next, **this, **tmp, *goroot, *pkgname, *pkgpath, *pkgdir, **oargv, *q;
- Job *job;
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<oargc; i++){
+ if(Boffset(&bout) - o > 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<njob; i++)
+ if(job[i].pass == pass)
+ obj[nobj++] = goobj(job[i].name, "$O");
+ if(nobj == 0)
+ break;
+ Bprint(&bout, "O%d=\\\n", pass+1);
+ for(i=0; i<nobj; i++)
+ Bprint(&bout, "\t%$\\\n", obj[i]);
+ Bprint(&bout, "\n");
+ }
+
+ // math.a: a1 a2
+ for(i=0; i<npkg; i++) {
+ Bprint(&bout, "%s.a:", pkg[i]);
+ for(k=0; k<pass; k++)
+ Bprint(&bout, " a%d", k+1);
+ Bprint(&bout, "\n");
+ }
+ Bprint(&bout, "\n");
+
+ // a1: $(O1)
+ // $(AS) grc $(PKG) $(O1)
+ // rm -f $(O1)
+ for(k=0; k<pass; k++){
+ Bprint(&bout, "a%d:\t$(O%d)\n", k+1, k+1);
+ for(i=0; i<npkg; i++) {
+ nobj = 0;
+ for(l=0; l<njob; l++)
+ if(job[l].pass == k && job[l].pkg == pkg[i])
+ obj[nobj++] = goobj(job[l].name, "$O");
+ if(nobj > 0) {
+ Bprint(&bout, "\t$(AR) grc %s.a", pkg[i]);
+ for(l=0; l<nobj; l++)
+ Bprint(&bout, " %$", obj[l]);
+ Bprint(&bout, "\n");
+ }
+ }
+ Bprint(&bout, "\trm -f $(O%d)\n", k+1);
+ Bprint(&bout, "\n");
+ }
+
+ // newpkg: clean
+ // 6ar grc pkg.a
+ Bprint(&bout, "newpkg: clean\n");
+ for(i=0; i<npkg; i++)
+ Bprint(&bout, "\t$(AR) grc %s.a\n", pkg[i]);
+ Bprint(&bout, "\n");
+
+ // $(O1): newpkg
+ // $(O2): a1
+ Bprint(&bout, "$(O1): newpkg\n");
+ for(i=1; i<pass; i++)
+ Bprint(&bout, "$(O%d): a%d\n", i+1, i);
+ Bprint(&bout, "\n");
+
+ // nuke: clean
+ // rm -f $(GOROOT)/pkg/xxx.a
+ Bprint(&bout, "nuke: clean\n");
+ Bprint(&bout, "\trm -f");
+ for(i=0; i<npkg; i++)
+ Bprint(&bout, " $(GOROOT)/pkg/%s.a", pkg[i]);
+ Bprint(&bout, "\n\n");
+
+ // packages: pkg.a
+ // rm -f $(GOROOT)/pkg/xxx.a
+ Bprint(&bout, "packages:");
+ for(i=0; i<npkg; i++)
+ Bprint(&bout, " %s.a", pkg[i]);
+ Bprint(&bout, "\n\n");
+
+ // install: packages
+ // cp xxx.a $(GOROOT)/pkg/xxx.a
+ Bprint(&bout, "install: packages\n");
+ for(i=0; i<npkg; i++)
+ Bprint(&bout, "\tcp %s.a $(GOROOT)/pkg/%s.a\n", pkg[i], pkg[i]);
+ Bprint(&bout, "\n");
+
+ Bterm(&bout);
+}
+
+int
+sourcefilenames(char ***argvp)
+{
+ Dir *d;
+ int dir, nd, i, argc;
+ char **argv;
+
+ if((dir = open(".", OREAD)) < 0)
+ sysfatal("open .: %r");
+
+ nd = dirreadall(dir, &d);
+ close(dir);
+
+ argv = emalloc((nd+1)*sizeof argv[0]);
+ argc = 0;
+ for(i=0; i<nd; i++) {
+ if(suffix(d[i].name, ".go")
+ || suffix(d[i].name, ".c")
+ || suffix(d[i].name, ".s"))
+ argv[argc++] = d[i].name;
+ }
+ *argvp = argv;
+ argv[argc] = nil;
+ return argc;
+}
+
+void
+main(int argc, char **argv)
+{
+ int i, k, pass, npending, nfail, nsuccess, narfiles;
+ Job **pending, **fail, **success, *j;
+ char **arfiles;
oargc = argc;
oargv = argv;
fmtinstall('$', dollarfmt);
- ARGBEGIN{
- default:
- usage();
- case 'm':
- makefile = 1;
- break;
- case 'v':
- chatty++;
- break;
- }ARGEND
-
- if(argc < 2)
- usage();
-
goos = getenv("GOOS");
if(goos == nil)
sysfatal("no $GOOS");
thechar = "6";
else
sysfatal("unknown $GOARCH");
-
+ devnull = open("/dev/null", OWRITE);
+ if(devnull < 0)
+ sysfatal("open /dev/null: %r");
goroot = getenv("GOROOT");
if(goroot == nil)
sysfatal("no $GOROOT");
- pkgname = argv[0];
- if(strchr(pkgname, '.')){
- fprint(2, "pkgname has dot\n");
+ ARGBEGIN{
+ default:
usage();
+ case 'm':
+ makefile = 1;
+ break;
+ case 'v':
+ chatty++;
+ break;
+ }ARGEND
+
+ // If no arguments, use all source files in current directory.
+ if(argc == 0)
+ argc = sourcefilenames(&argv);
+
+ // Make the job list.
+ njob = 0;
+ job = emalloc(argc*sizeof job[0]);
+ for(i=0; i<argc; i++) {
+ if(strncmp(argv[i], "test", 4) == 0)
+ continue;
+ job[njob].name = argv[i];
+ job[njob].pass = -1;
+ job[njob].pkg = getpkg(argv[i]);
+ njob++;
}
- q = strrchr(pkgname, '/');
- if(q) {
- pkgdir = pkgname;
- *q++ = '\0';
- pkgname = q;
- pkgdir = smprint("/%s", pkgdir);
- } else {
- pkgdir = "";
+ // Look for non-go files, which don't have packages.
+ // If there's only one package in the go files, use it.
+ for(i=0; i<njob; i++) {
+ if(job[i].pkg == nil) {
+ if(npkg == 1) {
+ job[i].pkg = pkg[0];
+ continue;
+ }
+ sysfatal("cannot determine package for %s", job[i].name);
+ }
}
- pkgpath = smprint("%s.a", pkgname);
- unlink(pkgpath);
- if(chatty)
- fprint(2, "pkg %s\n", pkgpath);
+ // TODO: subdirectory packages
- if((devnull = open("/dev/null", ORDWR)) < 0)
- sysfatal("open /dev/null: %r");
+ // Create empty archives for each package.
+ for(i=0; i<npkg; i++) {
+ unlink(smprint("%s.a", pkg[i]));
+ ar(pkg[i], nil, 0);
+ }
// Compile by repeated passes: build as many .6 as you can,
- // put them all in the archive, and repeat.
- //
- // "this" contains the list of files to compile in this pass.
- // "next" contains the list of files to re-try in the next pass.
- // "job" contains the list of files that are done, annotated
- // with their pass numbers.
- // "ar" contains the ar command line to run at the end
- // of the pass.
-
- n = argc-1;
- this = malloc(n*sizeof this[0]);
- next = malloc(n*sizeof next[0]);
- job = malloc(n*sizeof job[0]);
- ar = malloc((n+4)*sizeof job[0]);
- if(this == nil || next == nil || job == 0 || ar == 0)
- sysfatal("malloc: %r");
-
- // Initial "this" is the files given on the command line.
- for(i=0; i<n; i++)
- this[i] = argv[i+1];
- nthis = n;
-
- ar[0] = smprint("%sar", thechar);
- ar[1] = "grc";
- ar[2] = pkgpath;
- ar[3] = nil;
- if(run(ar, 1) < 0)
- sysfatal("ar: %r");
-
- njob = 0;
-
- for(pass=0; nthis > 0; pass++){
- nnext = 0;
- nar = 3;
-
- // Try to build.
- for(i=0; i<nthis; i++){
- if(buildcc(compiler(this[i]), this[i], 0) < 0){
- next[nnext++] = this[i];
- }else{
- job[njob].pass = pass;
- job[njob++].name = this[i];
- ar[nar++] = goobj(this[i], thechar);
+ // put them in their archives, and repeat.
+ pending = emalloc(njob*sizeof pending[0]);
+ for(i=0; i<njob; i++)
+ pending[i] = &job[i];
+ npending = njob;
+
+ fail = emalloc(njob*sizeof fail[0]);
+ success = emalloc(njob*sizeof success[0]);
+ arfiles = emalloc(njob*sizeof arfiles[0]);
+
+ for(pass=0; npending > 0; pass++) {
+ // Run what we can.
+ nfail = 0;
+ nsuccess = 0;
+ for(i=0; i<npending; i++) {
+ j = pending[i];
+ if(buildcc(compiler(j->name), j->name, 0) < 0)
+ fail[nfail++] = j;
+ else{
if(chatty == 1)
- fprint(2, "%s ", this[i]);
+ fprint(2, "%s ", j->name);
+ success[nsuccess++] = j;
}
}
- if(nthis == nnext){ // they all failed
- fprint(2, "cannot make progress\n");
- for(i=0; i<nthis; i++)
- buildcc(compiler(this[i]), this[i], 1);
+ if(nsuccess == 0) {
+ // Nothing ran; give up.
+ for(i=0; i<nfail; i++) {
+ j = fail[i];
+ buildcc(compiler(j->name), j->name, 1);
+ }
exits("stalemate");
}
if(chatty == 1)
fprint(2, "\n");
- // Add to archive.
- ar[nar] = nil;
- if(run(ar, 1) < 0)
- sysfatal("ar: %r");
-
- // Delete objects.
- for(i=3; i<nar; i++)
- unlink(ar[i]);
-
- // Set up for next pass: next = this.
- tmp = next;
- next = this;
- this = tmp;
- nthis = nnext;
- }
-
- if(makefile){
- // 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<oargc; i++){
- if(Boffset(&bout) - o > 60){
- Bprint(&bout, "\\\n# ");
- o = Boffset(&bout);
+ // Update archives.
+ for(i=0; i<npkg; i++) {
+ narfiles = 0;
+ for(k=0; k<nsuccess; k++) {
+ j = success[k];
+ if(j->pkg == pkg[i])
+ arfiles[narfiles++] = goobj(j->name, thechar);
+ j->pass = pass;
}
- Bprint(&bout, " %s", oargv[i]);
+ if(narfiles > 0)
+ ar(pkg[i], arfiles, narfiles);
+ for(k=0; k<narfiles; k++)
+ unlink(arfiles[k]);
}
- Bprint(&bout, "\n");
- Bprint(&bout, preamble, thechar, pkgname, pkgdir);
-
- // O2=\
- // os_file.$O\
- // os_time.$O\
- //
- p = -1;
- for(i=0; i<n; i++){
- if(job[i].pass != p){
- p = job[i].pass;
- Bprint(&bout, "\nO%d=\\\n", p+1);
- }
- Bprint(&bout, "\t%$\\\n", goobj(job[i].name, "$O"));
- }
- Bprint(&bout, "\n");
- // $(PKG): a1 a2
- Bprint(&bout, "$(PKG):");
- for(i=0; i<pass; i++)
- Bprint(&bout, " a%d", i+1);
- Bprint(&bout, "\n");
-
- // a1: $(O1)
- // $(AS) grc $(PKG) $(O1)
- // rm -f $(O1)
- for(i=0; i<pass; i++){
- Bprint(&bout, "a%d:\t$(O%d)\n", i+1, i+1);
- Bprint(&bout, "\t$(AR) grc $(PKG) $(O%d)\n", i+1);
- Bprint(&bout, "\trm -f $(O%d)\n", i+1);
- }
- Bprint(&bout, "\n");
-
- // $(O1): nuke
- // $(O2): a1
- Bprint(&bout, "$(O1): nuke\n");
- for(i=1; i<pass; i++)
- Bprint(&bout, "$(O%d): a%d\n", i+1, i);
- Bprint(&bout, "\n");
- Bterm(&bout);
+ for(i=0; i<nfail; i++)
+ pending[i] = fail[i];
+ npending = nfail;
}
+ if(makefile)
+ writemakefile();
exits(0);
}