summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/cmd/gobuild/Makefile59
-rw-r--r--src/cmd/gobuild/gobuild.c590
-rw-r--r--src/cmd/gobuild/gobuild.go288
-rw-r--r--src/cmd/gobuild/main.go14
-rw-r--r--src/cmd/gobuild/makefile.go122
-rw-r--r--src/cmd/gobuild/util.go244
-rw-r--r--src/cmd/make.bash2
-rwxr-xr-xsrc/make.bash2
8 files changed, 719 insertions, 602 deletions
diff --git a/src/cmd/gobuild/Makefile b/src/cmd/gobuild/Makefile
index 339399033..28e2a2e03 100644
--- a/src/cmd/gobuild/Makefile
+++ b/src/cmd/gobuild/Makefile
@@ -2,19 +2,58 @@
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
-include ../../Make.conf
+# sadly, not auto-generated
-TARG=gobuild
-OFILES=\
- gobuild.$O\
+O=6
+OS=568vq
+GC=$(O)g
+CC=$(O)c -FVw
+AS=$(O)a
+AR=6ar
+LD=$(O)l
-$(TARG): $(OFILES)
- $(LD) -o $(TARG) -L$(GOROOT)/lib $(OFILES) -lbio -l9
+default: gobuild
clean:
- rm -f $(OFILES) $(TARG)
+ rm -f *.[$(OS)] *.a [$(OS)].out gobuild
-install: $(TARG)
- cp $(TARG) $(BIN)/$(TARG)
+%.$O: %.go
+ $(GC) $*.go
+
+O1=util.$O
+O2=makefile.$O
+O3=gobuild.$O
+
+phases: a1 a2 a3
+gobuild.a: phases
+
+a1: $(O1)
+ $(AR) grc gobuild.a util.$O
+ rm -f $(O1)
+
+a2: $(O2)
+ $(AR) grc gobuild.a makefile.$O
+ rm -f $(O2)
+
+a3: $(O3)
+ $(AR) grc gobuild.a gobuild.$O
+ rm -f $(O3)
+
+newpkg: clean
+ $(AR) grc gobuild.a
+
+$(O1): newpkg
+$(O2): a1
+$(O3): a2
+
+gobuild: main.$O gobuild.a
+ $(LD) -o gobuild main.$O
+
+main.$O: gobuild.a
+
+nuke: clean
+ rm -f $(HOME)/bin/gobuild
+
+install: gobuild
+ cp gobuild $(HOME)/bin/gobuild
-$(OFILES): $(HFILES)
diff --git a/src/cmd/gobuild/gobuild.c b/src/cmd/gobuild/gobuild.c
deleted file mode 100644
index 5368d9f4d..000000000
--- a/src/cmd/gobuild/gobuild.c
+++ /dev/null
@@ -1,590 +0,0 @@
-// 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...]\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; 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)
-{
- 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);
-}
-
-// 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((b = Bopen(file, OREAD)) == nil)
- sysfatal("open %s: %r", file);
- while((p = Brdline(b, '\n')) != nil) {
- p[Blinelen(b)-1] = '\0';
- if(!suffix(file, ".go")) {
- if(*p != '/' || *(p+1) != '/')
- continue;
- p += 2;
- }
- if(strstr(p, "gobuild: ignore"))
- return "main";
- 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];
- }
- }
- // don't put main in the package list
- if(strcmp(p, "main") == 0)
- return "main";
- 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)
-{
- char *s;
- Rune r;
- int n;
-
- s = va_arg(f->args, char*);
- if(s == nil){
- fmtstrcpy(f, "<nil>");
- return 0;
- }
- for(; *s; s+=n){
- n = strlen(goarch);
- if(strncmp(s, goarch, n) == 0){
- if(f->flags & FmtSharp)
- fmtstrcpy(f, "${GOARCH}"); // shell
- else
- fmtstrcpy(f, "$(GOARCH)"); // make
- continue;
- }
- n = strlen(goos);
- if(strncmp(s, goos, n) == 0){
- if(f->flags & FmtSharp)
- fmtstrcpy(f, "${GOOS}"); // shell
- else
- fmtstrcpy(f, "$(GOOS)"); // make
- 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<oargc; i++){
- if(Boffset(&bout) - o > 60){
- Bprint(&bout, "\\\n# ");
- o = Boffset(&bout);
- }
- Bprint(&bout, " %#$", 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);
-
- goos = getenv("GOOS");
- if(goos == nil)
- sysfatal("no $GOOS");
- goarch = getenv("GOARCH");
- if(goarch == nil)
- sysfatal("no $GOARCH");
- if(strcmp(goarch, "amd64") == 0)
- 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");
-
- 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(suffix(argv[i], "_test.go"))
- continue;
- job[njob].name = argv[i];
- job[njob].pass = -1;
- job[njob].pkg = getpkg(argv[i]);
- if(job[njob].pkg && strcmp(job[njob].pkg, "main") == 0)
- continue;
- njob++;
- }
-
- // 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);
- }
- }
-
- // TODO: subdirectory packages
-
- // 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 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 ", j->name);
- success[nsuccess++] = j;
- }
- }
- 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");
-
- // 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;
- }
- if(narfiles > 0)
- ar(pkg[i], arfiles, narfiles);
- for(k=0; k<narfiles; k++)
- unlink(arfiles[k]);
- }
-
- for(i=0; i<nfail; i++)
- pending[i] = fail[i];
- npending = nfail;
- }
-
- if(makefile)
- writemakefile();
- exits(0);
-}
diff --git a/src/cmd/gobuild/gobuild.go b/src/cmd/gobuild/gobuild.go
new file mode 100644
index 000000000..c28419da2
--- /dev/null
+++ b/src/cmd/gobuild/gobuild.go
@@ -0,0 +1,288 @@
+// 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.
+
+package gobuild
+
+import (
+ "flag";
+ "fmt";
+ "gobuild";
+ "io";
+ "os";
+ "path";
+ "sort";
+ "strings";
+ "template";
+)
+
+type Pkg struct
+
+type File struct {
+ Name string;
+ Pkg *Pkg;
+ Imports []string;
+ Deps []*Pkg;
+ Phase int;
+}
+
+type Pkg struct {
+ Name string;
+ Path string;
+ Files []*File;
+}
+
+type ArCmd struct {
+ Pkg *Pkg;
+ Files []*File;
+}
+
+type Phase struct {
+ Phase int;
+ ArCmds []*ArCmd;
+}
+
+type Info struct {
+ Args []string;
+ Char string;
+ Pkgmap map[string] *Pkg;
+ Packages []*Pkg;
+ Files map[string] *File;
+ Imports map[string] bool;
+ Phases []*Phase;
+ MaxPhase int;
+}
+
+var verbose = flag.Bool("v", false, "verbose mode")
+var writeMakefile = flag.Bool("m", false, "write Makefile to standard output")
+
+func PushPkg(v *[]*Pkg, p *Pkg) {
+ n := len(v);
+ if n >= cap(v) {
+ m := 2*n + 10;
+ a := make([]*Pkg, n, m);
+ for i := range *v {
+ a[i] = v[i];
+ }
+ *v = a;
+ }
+ *v = v[0:n+1];
+ v[n] = p;
+}
+
+func PushFile(v *[]*File, p *File) {
+ n := len(v);
+ if n >= cap(v) {
+ m := 2*n + 10;
+ a := make([]*File, n, m);
+ for i := range *v {
+ a[i] = v[i];
+ }
+ *v = a;
+ }
+ *v = v[0:n+1];
+ v[n] = p;
+}
+
+// For sorting Files
+type FileArray []*File
+
+func (a FileArray) Len() int {
+ return len(a)
+}
+
+func (a FileArray) Less(i, j int) bool {
+ return a[i].Name < a[j].Name
+}
+
+func (a FileArray) Swap(i, j int) {
+ a[i], a[j] = a[j], a[i]
+}
+
+func ScanFiles(filenames []string) *Info {
+ // Build list of imports, local packages, and files.
+ // Exclude *_test.go and anything in package main.
+ // TODO(rsc): Build a binary from package main?
+
+ z := new(Info);
+ z.Args = sys.Args;
+ z.Char = theChar;
+ z.Pkgmap = make(map[string] *Pkg);
+ z.Files = make(map[string] *File);
+ z.Imports = make(map[string] bool);
+
+ // Read Go files to find out packages and imports.
+ var pkg *Pkg;
+ for _, filename := range filenames {
+ if strings.HasSuffix(filename, "_test.go") {
+ continue;
+ }
+ f := new(File);
+ f.Name = filename;
+ if path.Ext(filename) == ".go" {
+ pkgname, imp, err := PackageImports(filename);
+ if err != nil {
+ fatal("parsing", filename, err.String());
+ }
+ if pkgname == "main" {
+ continue;
+ }
+
+ path := pkgname;
+ var ok bool;
+ pkg, ok = z.Pkgmap[path];
+ if !ok {
+ pkg = new(Pkg);
+ pkg.Name = pkgname;
+ pkg.Path = path;
+ z.Pkgmap[path] = pkg;
+ PushPkg(&z.Packages, pkg);
+ }
+ f.Pkg = pkg;
+ f.Imports = imp;
+ for _, name := range imp {
+ z.Imports[name] = true;
+ }
+ PushFile(&pkg.Files, f);
+ }
+ z.Files[filename] = f;
+ }
+
+ // Loop through files again, filling in more info.
+ for _, f := range z.Files {
+ if f.Pkg == nil {
+ // non-Go file: fill in package name.
+ // Must only be a single package in this directory.
+ if len(z.Pkgmap) != 1 {
+ fatal("cannot determine package for ", f.Name);
+ }
+ f.Pkg = pkg;
+ }
+
+ // Go file: record dependencies on other packages in this directory.
+ for _, imp := range f.Imports {
+ pkg, ok := z.Pkgmap[imp];
+ if ok && pkg != f.Pkg {
+ PushPkg(&f.Deps, pkg);
+ }
+ }
+ }
+
+ return z;
+}
+
+func PackageObj(pkg string) string {
+ return pkg + ".a"
+}
+
+func (z *Info) Build() {
+ // Create empty archives.
+ for pkgname := range z.Pkgmap {
+ ar := PackageObj(pkgname);
+ os.Remove(ar);
+ Archive(ar, nil);
+ }
+
+ // Compile by repeated passes: build as many .6 as possible,
+ // put them in their archives, and repeat.
+ var pending, fail, success []*File;
+ for _, file := range z.Files {
+ PushFile(&pending, file);
+ }
+ sort.Sort(FileArray(pending));
+
+ var arfiles []string;
+ z.Phases = make([]*Phase, 0, len(z.Files));
+
+ for phase := 1; len(pending) > 0; phase++ {
+ // Run what we can.
+ fail = fail[0:0];
+ success = success[0:0];
+ for _, f := range pending {
+ if !Build(Compiler(f.Name), f.Name, false) {
+ PushFile(&fail, f);
+ } else {
+ if *verbose {
+ fmt.Fprint(os.Stderr, f.Name, " ");
+ }
+ PushFile(&success, f);
+ }
+ }
+ if len(success) == 0 {
+ // Nothing ran; give up.
+ for _, f := range fail {
+ Build(Compiler(f.Name), f.Name, true);
+ }
+ fatal("stalemate");
+ }
+ if *verbose {
+ fmt.Fprint(os.Stderr, "\n");
+ }
+
+ // Record phase data.
+ p := new(Phase);
+ p.ArCmds = make([]*ArCmd, 0, len(z.Pkgmap));
+ p.Phase = phase;
+ n := len(z.Phases);
+ z.Phases = z.Phases[0:n+1];
+ z.Phases[n] = p;
+
+ // Update archives.
+ for _, pkg := range z.Pkgmap {
+ arfiles = arfiles[0:0];
+ var files []*File;
+ for _, f := range success {
+ if f.Pkg == pkg {
+ PushString(&arfiles, Object(f.Name, theChar));
+ PushFile(&files, f);
+ }
+ f.Phase = phase;
+ }
+ if len(arfiles) > 0 {
+ Archive(pkg.Name + ".a", arfiles);
+
+ n := len(p.ArCmds);
+ p.ArCmds = p.ArCmds[0:n+1];
+ p.ArCmds[n] = &ArCmd{pkg, files};
+ }
+ for _, filename := range arfiles {
+ os.Remove(filename);
+ }
+ }
+ pending, fail = fail, pending;
+
+ }
+}
+
+func (z *Info) Clean() {
+ for pkgname := range z.Pkgmap {
+ os.Remove(PackageObj(pkgname));
+ }
+}
+
+func Main() {
+ flag.Parse();
+
+ filenames := flag.Args();
+ if len(filenames) == 0 {
+ var err *os.Error;
+ filenames, err= SourceFiles(".");
+ if err != nil {
+ fatal("reading .: ", err.String());
+ }
+ }
+
+ state := ScanFiles(filenames);
+ state.Build();
+ if *writeMakefile {
+ t, err, line := template.Parse(makefileTemplate, makefileMap);
+ if err != nil {
+ fatal("template.Parse: ", err.String());
+ }
+ err = t.Execute(state, os.Stdout);
+ if err != nil {
+ fatal("template.Expand: ", err.String());
+ }
+ }
+}
+
diff --git a/src/cmd/gobuild/main.go b/src/cmd/gobuild/main.go
new file mode 100644
index 000000000..da781f988
--- /dev/null
+++ b/src/cmd/gobuild/main.go
@@ -0,0 +1,14 @@
+// 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.
+
+package main
+
+import (
+ "gobuild";
+)
+
+func main() {
+ gobuild.Main();
+}
+
diff --git a/src/cmd/gobuild/makefile.go b/src/cmd/gobuild/makefile.go
new file mode 100644
index 000000000..cbdad90c0
--- /dev/null
+++ b/src/cmd/gobuild/makefile.go
@@ -0,0 +1,122 @@
+// 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.
+
+package gobuild
+
+import (
+ "fmt";
+ "gobuild";
+ "io";
+ "path";
+ "template";
+)
+
+var makefileTemplate =
+ "# DO NOT EDIT. Automatically generated by gobuild.\n"
+ "{Args|args} >Makefile\n"
+ "\n"
+ "O_arm=5\n" // TODO(rsc): include something here?
+ "O_amd64=6\n"
+ "O_386=8\n"
+ "OS=568vq\n"
+ "\n"
+ "O=$(O_$(GOARCH))\n"
+ "GC=$(O)g\n"
+ "CC=$(O)c -FVw\n"
+ "AS=$(O)a\n"
+ "AR=6ar\n"
+ "\n"
+ "default: packages\n"
+ "\n"
+ "clean:\n"
+ " rm -f *.[$(OS)] *.a [$(OS)].out\n"
+ "\n"
+ "test: packages\n"
+ " gotest\n"
+ "\n"
+ "coverage: packages\n"
+ " gotest\n"
+ " 6cov -g `pwd` | grep -v '_test\\.go:'\n"
+ "\n"
+ "%.$O: %.go\n"
+ " $(GC) $*.go\n"
+ "\n"
+ "%.$O: %.c\n"
+ " $(CC) $*.c\n"
+ "\n"
+ "%.$O: %.s\n"
+ " $(AS) $*.s\n"
+ "\n"
+ "{.repeated section Phases}\n"
+ "O{Phase}=\\\n"
+ "{.repeated section ArCmds}\n"
+ "{.repeated section Files}\n"
+ " {Name|basename}.$O\\\n"
+ "{.end}\n"
+ "{.end}\n"
+ "\n"
+ "{.end}\n"
+ "\n"
+ "phases:{.repeated section Phases} a{Phase}{.end}\n"
+ "{.repeated section Packages}\n"
+ "{Name}.a: phases\n"
+ "{.end}\n"
+ "\n"
+ "{.repeated section Phases}\n"
+ "a{Phase}: $(O{Phase})\n"
+ "{.repeated section ArCmds}\n"
+ " $(AR) grc {.section Pkg}{Name}.a{.end}{.repeated section Files} {Name|basename}.$O{.end}\n"
+ "{.end}\n"
+ " rm -f $(O{Phase})\n"
+ "\n"
+ "{.end}\n"
+ "\n"
+ "newpkg: clean\n"
+ "{.repeated section Packages}\n"
+ " $(AR) grc {Name}.a\n"
+ "{.end}\n"
+ "\n"
+ "$(O1): newpkg\n"
+ "{.repeated section Phases}\n"
+ "$(O{Phase|+1}): a{Phase}\n"
+ "{.end}\n"
+ "\n"
+ "nuke: clean\n"
+ " rm -f{.repeated section Packages} $(GOROOT)/pkg/{Name}.a{.end}\n"
+ "\n"
+ "packages:{.repeated section Packages} {Name}.a{.end}\n"
+ "\n"
+ "install: packages\n"
+ "{.repeated section Packages}\n"
+ " cp {Name}.a $(GOROOT)/pkg/{Name}.a\n"
+ "{.end}\n"
+
+func argsFmt(w io.Write, x interface{}, format string) {
+ args := x.([]string);
+ fmt.Fprint(w, "#");
+ for i, a := range args {
+ fmt.Fprint(w, " ", ShellString(a));
+ }
+}
+
+func basenameFmt(w io.Write, x interface{}, format string) {
+ t := fmt.Sprint(x);
+ t = t[0:len(t)-len(path.Ext(t))];
+ fmt.Fprint(w, MakeString(t));
+}
+
+func plus1Fmt(w io.Write, x interface{}, format string) {
+ fmt.Fprint(w, x.(int) + 1);
+}
+
+func makeFmt(w io.Write, x interface{}, format string) {
+ fmt.Fprint(w, MakeString(fmt.Sprint(x)));
+}
+
+var makefileMap = template.FormatterMap {
+ "": makeFmt,
+ "+1": plus1Fmt,
+ "args": argsFmt,
+ "basename": basenameFmt,
+}
diff --git a/src/cmd/gobuild/util.go b/src/cmd/gobuild/util.go
new file mode 100644
index 000000000..022417289
--- /dev/null
+++ b/src/cmd/gobuild/util.go
@@ -0,0 +1,244 @@
+// 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.
+
+
+package gobuild
+
+import (
+ "ast";
+ "exec";
+ "fmt";
+ "os";
+ "parser";
+ "path";
+ "sort";
+ "strconv";
+ "strings";
+)
+
+var (
+ theChar string;
+ goarch string;
+ goos string;
+ bin = make(map[string] string);
+)
+
+var theChars = map[string] string {
+ "amd64": "6",
+ "386": "8",
+ "arm": "5"
+}
+
+func fatal(args ...) {
+ fmt.Fprintf(os.Stderr, "gobuild: %s\n", fmt.Sprint(args));
+ sys.Exit(1);
+}
+
+func init() {
+ var err *os.Error;
+ goarch, err = os.Getenv("GOARCH");
+ goos, err = os.Getenv("GOOS");
+
+ var ok bool;
+ theChar, ok = theChars[goarch];
+ if !ok {
+ fatal("unknown $GOARCH: ", goarch);
+ }
+
+ var binaries = []string{
+ theChar + "g",
+ theChar + "c",
+ theChar + "a",
+ "6ar", // sic
+ };
+
+ for i, v := range binaries {
+ var s string;
+ if s, err = exec.LookPath(v); err != nil {
+ fatal("cannot find binary ", v);
+ }
+ bin[v] = s;
+ }
+}
+
+func PushString(v *[]string, p string) {
+ n := len(v);
+ if n >= cap(v) {
+ m := 2*n + 10;
+ a := make([]string, n, m);
+ for i := range *v {
+ a[i] = v[i];
+ }
+ *v = a;
+ }
+ *v = v[0:n+1];
+ v[n] = p;
+}
+
+
+func run(argv []string, display bool) (ok bool) {
+ argv0 := bin[argv[0]];
+ output := exec.DevNull;
+ if display {
+ output = exec.PassThrough;
+ }
+ p, err1 := exec.Run(argv0, argv, os.Environ(), exec.DevNull, output, output);
+ if err1 != nil {
+ return false;
+ }
+ w, err2 := p.Wait(0);
+ if err2 != nil {
+ return false;
+ }
+ return w.Exited() && w.ExitStatus() == 0;
+}
+
+func Build(cmd []string, file string, display bool) (ok bool) {
+ if display {
+ fmt.Fprint(os.Stderr, "$ ");
+ for i, s := range cmd {
+ fmt.Fprint(os.Stderr, s[i], " ");
+ }
+ fmt.Fprint(os.Stderr, file, "\n");
+ }
+
+ var argv []string;
+ for i, c := range cmd {
+ PushString(&argv, c);
+ }
+ PushString(&argv, file);
+ return run(argv, display);
+}
+
+func Archive(pkg string, files []string) {
+ argv := []string{ "6ar", "grc", pkg };
+ for i, file := range files {
+ PushString(&argv, file);
+ }
+ if !run(argv, true) {
+ fatal("archive failed");
+ }
+}
+
+func Compiler(file string) []string {
+ switch {
+ case strings.HasSuffix(file, ".go"):
+ return []string{ theChar + "g" };
+ case strings.HasSuffix(file, ".c"):
+ return []string{ theChar + "c", "-FVw" };
+ case strings.HasSuffix(file, ".s"):
+ return []string{ theChar + "a" };
+ }
+ fatal("don't know how to compile ", file);
+ return nil;
+}
+
+func Object(file, suffix string) string {
+ ext := path.Ext(file);
+ return file[0:len(file)-len(ext)] + "." + suffix;
+}
+
+// Dollarstring returns s with literal goarch/goos values
+// replaced by $lGOARCHr where l and r are the specified delimeters.
+func dollarString(s, l, r string) string {
+ out := "";
+ j := 0; // index of last byte in s copied to out.
+ for i := 0; i < len(s); {
+ switch {
+ case i+len(goarch) <= len(s) && s[i:i+len(goarch)] == goarch:
+ out += s[j:i];
+ out += "$" + l + "GOARCH" + r;
+ i += len(goarch);
+ j = i;
+ case i+len(goos) <= len(s) && s[i:i+len(goos)] == goos:
+ out += s[j:i];
+ out += "$" + l + "GOOS" + r;
+ i += len(goos);
+ j = i;
+ default:
+ i++;
+ }
+ }
+ out += s[j:len(s)];
+ return out;
+}
+
+// dollarString wrappers.
+// Print ShellString(s) or MakeString(s) depending on
+// the context in which the result will be interpreted.
+type ShellString string;
+func (s ShellString) String() string {
+ return dollarString(s, "{", "}");
+}
+
+type MakeString string;
+func (s MakeString) String() string {
+ return dollarString(s, "(", ")");
+}
+
+// TODO(rsc): parse.Parse should return an os.Error.
+var ParseError = os.NewError("parse errors");
+
+// TODO(rsc): Should this be in the AST library?
+func LitString(p []*ast.StringLit) (string, *os.Error) {
+ s := "";
+ for i, lit := range p {
+ t, err := strconv.Unquote(string(lit.Value));
+ if err != nil {
+ return "", err;
+ }
+ s += t;
+ }
+ return s, nil;
+}
+
+func PackageImports(file string) (pkg string, imports []string, err1 *os.Error) {
+ f, err := os.Open(file, os.O_RDONLY, 0);
+ if err != nil {
+ return "", nil, err
+ }
+
+ prog, ok := parser.Parse(f, nil, parser.ImportsOnly);
+ if !ok {
+ return "", nil, ParseError;
+ }
+
+ // Normally one must consult the types of decl and spec,
+ // but we told the parser to return imports only,
+ // so assume it did.
+ var imp []string;
+ for _, decl := range prog.Decls {
+ for _, spec := range decl.(*ast.GenDecl).Specs {
+ str, err := LitString(spec.(*ast.ImportSpec).Path);
+ if err != nil {
+ return "", nil, ParseError; // ParseError is better than os.EINVAL
+ }
+ PushString(&imp, str);
+ }
+ }
+
+ // TODO(rsc): should be prog.Package.Value
+ return prog.Name.Value, imp, nil;
+}
+
+func SourceFiles(dir string) ([]string, *os.Error) {
+ f, err := os.Open(dir, os.O_RDONLY, 0);
+ if err != nil {
+ return nil, err;
+ }
+ names, err1 := f.Readdirnames(-1);
+ f.Close();
+ out := make([]string, 0, len(names));
+ for i, name := range names {
+ if strings.HasSuffix(name, ".go")
+ || strings.HasSuffix(name, ".c")
+ || strings.HasSuffix(name, ".s") {
+ n := len(out);
+ out = out[0:n+1];
+ out[n] = name;
+ }
+ }
+ sort.SortStrings(out);
+ return out, nil;
+}
diff --git a/src/cmd/make.bash b/src/cmd/make.bash
index 3d812c414..08cffbb9b 100644
--- a/src/cmd/make.bash
+++ b/src/cmd/make.bash
@@ -12,7 +12,7 @@ bash mkenam
make enam.o
cd ..
-for i in cc 6l 6a 6c gc 6g ar db nm acid cov gobuild godefs prof gotest
+for i in cc 6l 6a 6c gc 6g ar db nm acid cov godefs prof gotest
do
echo; echo; echo %%%% making $i %%%%; echo
cd $i
diff --git a/src/make.bash b/src/make.bash
index 830ac7068..5c5e36eff 100755
--- a/src/make.bash
+++ b/src/make.bash
@@ -18,7 +18,7 @@ rm -f $HOME/bin/quietgcc
cp quietgcc.bash $HOME/bin/quietgcc
chmod +x $HOME/bin/quietgcc
-for i in lib9 libbio libmach_amd64 libregexp cmd runtime lib
+for i in lib9 libbio libmach_amd64 libregexp cmd runtime lib cmd/gobuild
do
echo; echo; echo %%%% making $i %%%%; echo
cd $i