// 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 ( "exec"; "fmt"; "go/ast"; "go/parser"; "os"; "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" } const ObjDir = "_obj" 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", "-I", ObjDir }; 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; } // TODO(rsc): Implement these for real as // os.MkdirAll and os.RemoveAll and then // make these wrappers that call fatal on error. func MkdirAll(name string) { p, err := exec.Run("/bin/mkdir", []string{"mkdir", "-p", name}, os.Environ(), exec.DevNull, exec.PassThrough, exec.PassThrough); if err != nil { fatal("run /bin/mkdir: %v", err); } w, err1 := p.Wait(0); if err1 != nil { fatal("wait /bin/mkdir: %v", err); } if !w.Exited() || w.ExitStatus() != 0 { fatal("/bin/mkdir: %v", w); } } func RemoveAll(name string) { p, err := exec.Run("/bin/rm", []string{"rm", "-rf", name}, os.Environ(), exec.DevNull, exec.PassThrough, exec.PassThrough); if err != nil { fatal("run /bin/rm: %v", err); } w, err1 := p.Wait(0); if err1 != nil { fatal("wait /bin/rm: %v", err); } if !w.Exited() || w.ExitStatus() != 0 { fatal("/bin/rm: %v", w); } }