mirror of https://github.com/golang/go.git
302 lines
6.6 KiB
Go
302 lines
6.6 KiB
Go
// 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() {
|
|
goarch = os.Getenv("GOARCH");
|
|
goos = 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, err := p.Wait(0);
|
|
if err != nil {
|
|
fatal("wait /bin/rm: %v", err);
|
|
}
|
|
if !w.Exited() || w.ExitStatus() != 0 {
|
|
fatal("/bin/rm: %v", w);
|
|
}
|
|
|
|
}
|
|
|