diff options
-rw-r--r-- | src/cmd/gobuild/Makefile | 59 | ||||
-rw-r--r-- | src/cmd/gobuild/gobuild.c | 590 | ||||
-rw-r--r-- | src/cmd/gobuild/gobuild.go | 288 | ||||
-rw-r--r-- | src/cmd/gobuild/main.go | 14 | ||||
-rw-r--r-- | src/cmd/gobuild/makefile.go | 122 | ||||
-rw-r--r-- | src/cmd/gobuild/util.go | 244 | ||||
-rw-r--r-- | src/cmd/make.bash | 2 | ||||
-rwxr-xr-x | src/make.bash | 2 |
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 |