Commit 7847056d authored by Russ Cox's avatar Russ Cox

rewrite gobuild in go.

R=r
DELTA=1305  (704 added, 590 deleted, 11 changed)
OCL=27546
CL=27548
parent 1cb3b7d1
...@@ -2,19 +2,58 @@ ...@@ -2,19 +2,58 @@
# Use of this source code is governed by a BSD-style # Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file. # license that can be found in the LICENSE file.
include ../../Make.conf # sadly, not auto-generated
TARG=gobuild O=6
OFILES=\ OS=568vq
gobuild.$O\ GC=$(O)g
CC=$(O)c -FVw
AS=$(O)a
AR=6ar
LD=$(O)l
$(TARG): $(OFILES) default: gobuild
$(LD) -o $(TARG) -L$(GOROOT)/lib $(OFILES) -lbio -l9
clean: clean:
rm -f $(OFILES) $(TARG) rm -f *.[$(OS)] *.a [$(OS)].out gobuild
install: $(TARG) %.$O: %.go
cp $(TARG) $(BIN)/$(TARG) $(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)
// 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);
}
// 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());
}
}
}
// 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();
}
// 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,
}
// 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;
}
...@@ -12,7 +12,7 @@ bash mkenam ...@@ -12,7 +12,7 @@ bash mkenam
make enam.o make enam.o
cd .. 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 do
echo; echo; echo %%%% making $i %%%%; echo echo; echo; echo %%%% making $i %%%%; echo
cd $i cd $i
......
...@@ -18,7 +18,7 @@ rm -f $HOME/bin/quietgcc ...@@ -18,7 +18,7 @@ rm -f $HOME/bin/quietgcc
cp quietgcc.bash $HOME/bin/quietgcc cp quietgcc.bash $HOME/bin/quietgcc
chmod +x $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 do
echo; echo; echo %%%% making $i %%%%; echo echo; echo; echo %%%% making $i %%%%; echo
cd $i cd $i
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment