--- /dev/null
+// 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 <u.h>
+#include <unistd.h>
+#include <libc.h>
+#include <bio.h>
+
+void
+usage(void)
+{
+ fprint(2, "usage: gobuild [-m] packagename *.go *.c *.s\n");
+ exits("usage");
+}
+
+int chatty;
+int devnull; // fd of /dev/null
+int makefile; // generate Makefile
+char *thechar; // object character
+
+// Info about when to compile a particular file.
+typedef struct Job Job;
+struct Job
+{
+ char *name;
+ int pass;
+};
+
+// 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;
+
+ n0 = nsec();
+ pid = fork();
+ if(pid < 0)
+ sysfatal("fork: %r");
+ if(pid == 0){
+ dup(devnull, 0);
+ if(!showoutput){
+ dup(devnull, 1);
+ dup(devnull, 2);
+ }
+ 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)
+ sysfatal("waitfor %d: %r", pid);
+ 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);
+}
+
+// Return bool whether s ends in suffix.
+int
+suffix(char *s, char *suffix)
+{
+ int n1, n2;
+
+ n1 = strlen(s);
+ n2 = strlen(suffix);
+ if(n1>n2 && 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);
+}
+
+// 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"
+ "PKG=$(GOROOT)/pkg/%s.a\n"
+ "\n"
+ "install: $(PKG)\n"
+ "\n"
+ "nuke: clean\n"
+ "\trm -f $(PKG)\n"
+ "\n"
+ "clean:\n"
+ "\trm -f *.$O *.a\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
+main(int argc, char **argv)
+{
+ int i, o, p, n, pass, nar, njob, nthis, nnext, oargc;
+ char **ar, **next, **this, **tmp, *goarch, *goroot, *pkgname, *pkgpath, **oargv;
+ Job *job;
+ Biobuf bout;
+
+ oargc = argc;
+ oargv = argv;
+
+ ARGBEGIN{
+ default:
+ usage();
+ case 'm':
+ makefile = 1;
+ break;
+ case 'v':
+ chatty++;
+ break;
+ }ARGEND
+
+ if(argc < 2)
+ usage();
+
+ goarch = getenv("GOARCH");
+ if(goarch == nil)
+ sysfatal("no $GOARCH");
+ if(strcmp(goarch, "amd64") == 0)
+ thechar = "6";
+ else
+ sysfatal("unknown $GOARCH");
+
+ goroot = getenv("GOROOT");
+ if(goroot == nil)
+ sysfatal("no $GOROOT");
+
+ pkgname = argv[0];
+ if(strchr(pkgname, '.')){
+ fprint(2, "pkgname has dot\n");
+ usage();
+ }
+
+ pkgpath = smprint("%s/pkg/%s.a", goroot, pkgname);
+ unlink(pkgpath);
+ if(chatty)
+ fprint(2, "pkg %s\n", pkgpath);
+
+ if((devnull = open("/dev/null", ORDWR)) < 0)
+ sysfatal("open /dev/null: %r");
+
+ // 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;
+
+ 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);
+ if(chatty == 1)
+ fprint(2, "%s ", this[i]);
+ }
+ }
+ 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);
+ 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);
+ }
+ Bprint(&bout, " %s", oargv[i]);
+ }
+ Bprint(&bout, "\n");
+ Bprint(&bout, preamble, thechar, pkgname);
+
+ // 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%s\\\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)
+ 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, "\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);
+ }
+
+ exits(0);
+}