blob: 7fec135401c8b2b6b202a7aafac84f415d0aeff0 [file] [log] [blame]
// 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 (
"bufio";
"exec";
"fmt";
"io";
"go/ast";
"go/parser";
"os";
"path";
"sort";
"strconv";
"strings";
)
const (
ShowErrors = 1<<iota;
ForceDisplay;
)
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(format string, args ...) {
fmt.Fprintf(os.Stderr, "gobuild: %s\n", fmt.Sprintf(format, args));
os.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: %s", goarch);
}
var binaries = []string{
theChar + "g",
theChar + "c",
theChar + "a",
"gopack",
};
for i, v := range binaries {
var s string;
if s, err = exec.LookPath(v); err != nil {
fatal("cannot find binary %s", v);
}
bin[v] = s;
}
}
func PushString(vp *[]string, p string) {
v := *vp;
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;
*vp = v;
}
func run(argv []string, flag int) (ok bool) {
argv0 := bin[argv[0]];
null, err := os.Open("/dev/null", os.O_RDWR, 0);
if err != nil {
fatal("open /dev/null: %s", err);
}
defer null.Close();
r, w, err := os.Pipe();
if err != nil {
fatal("pipe: %s", err);
}
pid, err := os.ForkExec(argv0, argv, os.Environ(), "", []*os.File{null, w, w});
defer r.Close();
w.Close();
if err != nil {
return false;
}
// Read the first line of output, if any. Discard the rest.
// If there is output and ShowErrors is set, show it,
// preceded by a shell command line.
// If ForceDisplay is set, we show the command even
// if there's no output; this gets set if we're just trying
// to keep the user informed.
b := bufio.NewReader(r);
line, err := b.ReadLineString('\n', true);
if flag & ShowErrors != 0 && line != "" || flag & ForceDisplay != 0 {
fmt.Fprint(os.Stderr, "$ ");
for i, s := range argv {
fmt.Fprint(os.Stderr, s, " ");
}
fmt.Fprint(os.Stderr, "\n");
fmt.Fprint(os.Stderr, " ", line);
io.Copy(r, null); // don't let process block on pipe
}
waitmsg, err := os.Wait(pid, 0);
if err != nil {
return false;
}
return waitmsg.Exited() && waitmsg.ExitStatus() == 0;
}
func Build(cmd []string, file string, flag int) (ok bool) {
var argv []string;
for i, c := range cmd {
PushString(&argv, c);
}
PushString(&argv, file);
return run(argv, flag);
}
func Archive(pkg string, files []string) {
argv := []string{ "gopack", "grc", pkg };
for i, file := range files {
PushString(&argv, file);
}
if !run(argv, ShowErrors) {
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 %s", 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(string(s), "{", "}");
}
type MakeString string;
func (s MakeString) String() string {
return dollarString(string(s), "(", ")");
}
// 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, err := parser.Parse(f, parser.ImportsOnly);
if err != nil {
return "", nil, err;
}
// 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, os.NewError("invalid import specifier"); // 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);
}
}