Commit 3dd1e5be authored by Russ Cox's avatar Russ Cox

cmd/dist: new command

dist is short for distribution.  This is the new Go distribution tool.

The plan is to replace the Makefiles with what amounts to
'go tool dist bootstrap', although it cannot be invoked like
that since it is in charge of getting us to the point where we
can build the go command.

It will also add additional commands to replace bash scripts
like test/run (go tool dist testrun), eventually eliminating our
dependence on not just bash but all the Unix tools and all
of cygwin.

This is strong enough to build (cc *.c) and run (a.out bootstrap)
to build not just the C libraries and tools but also the basic
Go packages up to the bootstrap form of the go command
(go_bootstrap).  I've run it successfully on both Linux and Windows.
This means that once we've switched to this tool in the build,
we can delete the buildscripts.

This tool is not nearly as nice as the go tool.  There are many
special cases that turn into simple if statements or tables in
the code.  Please forgive that.  C does not enjoy the benefits
that we designed into Go.

I was planning to wait to do this until after Go 1, but the
Windows builders are both broken due to a bug in either
make or bash or both involving the parsing of quoted command
arguments.  Make thinks it is invoking

        quietgcc -fno-common -I"c:/go/include" -ggdb -O2 -c foo.c

but bash (quietgcc is a bash script) thinks it is being invoked as

        quietgcc -fno-common '-Ic:/go/include -ggdb' -O2 -c foo.c

which obviously does not have the desired effect.  Rather than fight
these clumsy ports, I accelerated the schedule for the new tool.
We should be completely off cygwin (using just the mingw gcc port,
which is much more standalone) before Go 1.

It is big for a single CL, and for that I apologize.  I can cut it into
separate CLs along file boundaries if people would prefer that.

R=golang-dev, adg, gri, bradfitz, alex.brainman, dsymonds, iant, ality, hcwfrichter
CC=golang-dev
https://golang.org/cl/5620045
parent bf89d58e
This program, dist, is the bootstrapping tool for the Go distribution.
It takes care of building the C programs (like the Go compiler) and
the initial bootstrap copy of the go tool. It also serves as a catch-all
to replace odd jobs previously done with shell scripts.
Dist is itself written in very simple C. All interaction with C libraries,
even standard C libraries, is confined to a single system-specific file
(plan9.c, unix.c, windows.c), to aid portability. Functionality needed
by other files should be exposed via the portability layer. Functions
in the portability layer begin with an x prefix when they would otherwise
use the same name as or be confused for an existing function.
For example, xprintf is the portable printf.
By far the most common data types in dist are strings and arrays of
strings. Instead of using char* and char**, though, dist uses two named
data structures, Buf and Vec, which own all the data they point at.
The Buf operations are functions beginning with b; the Vec operations
are functions beginning with v. The basic form of any function declaring
Bufs or Vecs on the stack should be
void
myfunc(void)
{
Buf b1, b2;
Vec v1;
binit(&b1);
binit(&b2);
vinit(&v1);
... main code ...
bprintf(&b1, "hello, world");
vadd(&v1, bstr(&b1)); // v1 takes a copy of its argument
bprintf(&b1, "another string");
vadd(&v1, bstr(&b1)); // v1 now has two strings
bfree(&b1);
bfree(&b2);
vfree(&v1);
}
The binit/vinit calls prepare a buffer or vector for use, initializing the
data structures, and the bfree/vfree calls free any memory they are still
holding onto. Use of this idiom gives us lexically scoped allocations.
// Copyright 2012 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.
typedef int bool;
// The Time unit is unspecified; we just need to
// be able to compare whether t1 is older than t2 with t1 < t2.
typedef long long Time;
#define nil ((void*)0)
#define nelem(x) (sizeof(x)/sizeof((x)[0]))
#define USED(x) ((void)(x))
// A Buf is a byte buffer, like Go's []byte.
typedef struct Buf Buf;
struct Buf
{
char *p;
int len;
int cap;
};
// A Vec is a string vector, like Go's []string.
typedef struct Vec Vec;
struct Vec
{
char **p;
int len;
int cap;
};
// Modes for run.
enum {
CheckExit = 1,
};
// buf.c
bool bequal(Buf *s, Buf *t);
void bfree(Buf *b);
void bgrow(Buf *b, int n);
void binit(Buf *b);
char* bprintf(Buf *b, char *fmt, ...);
void breset(Buf *b);
char* bstr(Buf *b);
char* btake(Buf *b);
void bwrite(Buf *b, void *v, int n);
void bwriteb(Buf *dst, Buf *src);
void bwritestr(Buf *b, char *p);
void bswap(Buf *b, Buf *b1);
void vadd(Vec *v, char *p);
void vcopy(Vec *dst, char **src, int n);
void vfree(Vec *v);
void vgrow(Vec *v, int n);
void vinit(Vec *v);
void vreset(Vec *v);
void vuniq(Vec *v);
void splitlines(Vec*, char*);
void splitfields(Vec*, char*);
// build.c
extern char *default_goroot;
extern char *goarch;
extern char *gobin;
extern char *gohostarch;
extern char *gohostos;
extern char *goos;
extern char *goroot;
extern char *workdir;
extern char *slash;
void init(void);
void cmdbootstrap(int, char**);
void cmdenv(int, char**);
void cmdinstall(int, char**);
// buildgc.c
void gcopnames(char*, char*);
void mkenam(char*, char*);
// main.c
void xmain(int argc, char **argv);
// portability layer (plan9.c, unix.c, windows.c)
bool contains(char *p, char *sep);
void fatal(char *msg, ...);
bool hasprefix(char *p, char *prefix);
bool hassuffix(char *p, char *suffix);
bool isabs(char*);
bool isdir(char *p);
bool isfile(char *p);
char* lastelem(char*);
Time mtime(char*);
void readfile(Buf*, char*);
void run(Buf *b, char *dir, int mode, char *cmd, ...);
void runv(Buf *b, char *dir, int mode, Vec *argv);
bool streq(char*, char*);
void writefile(Buf*, char*);
void xatexit(void (*f)(void));
void xexit(int);
void xfree(void*);
void xgetenv(Buf *b, char *name);
void xgetwd(Buf *b);
void* xmalloc(int n);
void* xmalloc(int);
int xmemcmp(void*, void*, int);
void xmemmove(void*, void*, int);
void xmkdir(char *p);
void xmkdirall(char*);
Time xmtime(char *p);
void xprintf(char*, ...);
void xqsort(void*, int, int, int(*)(const void*, const void*));
void xreaddir(Vec *dst, char *dir);
void* xrealloc(void*, int);
void xrealwd(Buf *b, char *path);
void xremove(char *p);
void xremoveall(char *p);
void xsetenv(char*, char*);
int xstrcmp(char*, char*);
char* xstrdup(char *p);
int xstreq(char*, char*);
int xstrlen(char*);
char* xstrrchr(char*, int);
char* xstrstr(char*, char*);
char* xworkdir(void);
// Copyright 2012 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.
// Byte buffers and string vectors.
#include "a.h"
// binit prepares an uninitialized buffer for use.
void
binit(Buf *b)
{
b->p = nil;
b->len = 0;
b->cap = 0;
}
// breset truncates the buffer back to zero length.
void
breset(Buf *b)
{
b->len = 0;
}
// bfree frees the storage associated with a buffer.
void
bfree(Buf *b)
{
xfree(b->p);
binit(b);
}
// bgrow ensures that the buffer has at least n more bytes
// between its len and cap.
void
bgrow(Buf *b, int n)
{
int want;
want = b->len+n;
if(want > b->cap) {
b->cap = 2*want;
if(b->cap < 64)
b->cap = 64;
b->p = xrealloc(b->p, b->cap);
}
}
// bwrite appends the n bytes at v to the buffer.
void
bwrite(Buf *b, void *v, int n)
{
bgrow(b, n);
xmemmove(b->p+b->len, v, n);
b->len += n;
}
// bwritestr appends the string p to the buffer.
void
bwritestr(Buf *b, char *p)
{
bwrite(b, p, xstrlen(p));
}
// bstr returns a pointer to a NUL-terminated string of the
// buffer contents. The pointer points into the buffer.
char*
bstr(Buf *b)
{
bgrow(b, 1);
b->p[b->len] = '\0';
return b->p;
}
// btake takes ownership of the string form of the buffer.
// After this call, the buffer has zero length and does not
// refer to the memory that btake returned.
char*
btake(Buf *b)
{
char *p;
p = bstr(b);
binit(b);
return p;
}
// bwriteb appends the src buffer to the dst buffer.
void
bwriteb(Buf *dst, Buf *src)
{
bwrite(dst, src->p, src->len);
}
// bequal reports whether the buffers have the same content.
bool
bequal(Buf *s, Buf *t)
{
return s->len == t->len && xmemcmp(s->p, t->p, s->len) == 0;
}
// The invariant with the vectors is that v->p[0:v->len] is allocated
// strings that are owned by the vector. The data beyond v->len may
// be garbage.
// vinit prepares an uninitialized vector for use.
void
vinit(Vec *v)
{
v->p = nil;
v->len = 0;
v->cap = 0;
}
// vreset truncates the vector back to zero length.
void
vreset(Vec *v)
{
int i;
for(i=0; i<v->len; i++) {
xfree(v->p[i]);
v->p[i] = nil;
}
v->len = 0;
}
// vfree frees the storage associated with the vector.
void
vfree(Vec *v)
{
vreset(v);
xfree(v->p);
vinit(v);
}
// vgrow ensures that the vector has room for at least
// n more entries between len and cap.
void
vgrow(Vec *v, int n)
{
int want;
want = v->len+n;
if(want > v->cap) {
v->cap = 2*want;
if(v->cap < 64)
v->cap = 64;
v->p = xrealloc(v->p, v->cap*sizeof v->p[0]);
}
}
// vcopy copies the srclen strings at src into the vector.
void
vcopy(Vec *dst, char **src, int srclen)
{
int i;
// use vadd, to make copies of strings
for(i=0; i<srclen; i++)
vadd(dst, src[i]);
}
// vadd adds a copy of the string p to the vector.
void
vadd(Vec *v, char *p)
{
vgrow(v, 1);
if(p != nil)
p = xstrdup(p);
v->p[v->len++] = p;
}
// vaddn adds a string consisting of the n bytes at p to the vector.
void
vaddn(Vec *v, char *p, int n)
{
char *q;
vgrow(v, 1);
q = xmalloc(n+1);
xmemmove(q, p, n);
q[n] = '\0';
v->p[v->len++] = q;
}
static int
strpcmp(const void *a, const void *b)
{
return xstrcmp(*(char**)a, *(char**)b);
}
// vuniq sorts the vector and then discards duplicates,
// in the manner of sort | uniq.
void
vuniq(Vec *v)
{
int i, n;
xqsort(v->p, v->len, sizeof(v->p[0]), strpcmp);
n = 0;
for(i=0; i<v->len; i++) {
if(i>0 && streq(v->p[i], v->p[i-1]))
xfree(v->p[i]);
else
v->p[n++] = v->p[i];
}
v->len = n;
}
// splitlines replaces the vector v with the result of splitting
// the input p after each \n.
void
splitlines(Vec *v, char *p)
{
int i, c;
char *start;
vreset(v);
start = p;
for(i=0; p[i]; i++) {
if(p[i] == '\n') {
vaddn(v, start, (p+i+1)-start);
start = p+i+1;
}
}
if(*start != '\0')
vadd(v, start);
}
// splitfields replaces the vector v with the result of splitting
// the input p into non-empty fields containing no spaces.
void
splitfields(Vec *v, char *p)
{
char *start;
vreset(v);
for(;;) {
while(*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n')
p++;
if(*p == '\0')
break;
start = p;
while(*p != ' ' && *p != '\t' && *p != '\r' && *p != '\n' && *p != '\0')
p++;
vaddn(v, start, p-start);
}
}
// Copyright 2012 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.
#include "a.h"
/*
* Initialization for any invocation.
*/
// The usual variables.
char *goarch;
char *gobin;
char *gohostarch;
char *gohostos;
char *goos;
char *goroot;
char *workdir;
char *gochar;
char *goroot_final;
char *goversion = "go1"; // TODO: Read correct version
char *slash; // / for unix, \ for windows
char *default_goroot;
static void fixslash(Buf*);
static bool shouldbuild(char*, char*);
static void copy(char*, char*);
// The known architecture letters.
static char *gochars = "568";
// The known architectures.
static char *okgoarch[] = {
// same order as gochars
"arm",
"amd64",
"386",
};
// The known operating systems.
static char *okgoos[] = {
"darwin",
"linux",
"freebsd",
"netbsd",
"openbsd",
"plan9",
"windows",
};
static void rmworkdir(void);
// find reports the first index of p in l[0:n], or else -1.
static int
find(char *p, char **l, int n)
{
int i;
for(i=0; i<n; i++)
if(streq(p, l[i]))
return i;
return -1;
}
// init handles initialization of the various global state, like goroot and goarch.
void
init(void)
{
char *p;
int i;
Buf b;
binit(&b);
xgetenv(&b, "GOROOT");
if(b.len == 0) {
if(default_goroot == nil)
fatal("$GOROOT not set and not available");
bwritestr(&b, default_goroot);
}
goroot = btake(&b);
xgetenv(&b, "GOBIN");
if(b.len == 0)
bprintf(&b, "%s%sbin", goroot, slash);
gobin = btake(&b);
xgetenv(&b, "GOOS");
if(b.len == 0)
bwritestr(&b, gohostos);
goos = btake(&b);
if(find(goos, okgoos, nelem(okgoos)) < 0)
fatal("unknown $GOOS %s", goos);
p = bprintf(&b, "%s/include/u.h", goroot);
fixslash(&b);
if(!isfile(p)) {
fatal("$GOROOT is not set correctly or not exported\n"
"\tGOROOT=%s\n"
"\t%s does not exist", goroot, p);
}
xgetenv(&b, "GOHOSTARCH");
if(b.len > 0)
gohostarch = btake(&b);
if(find(gohostarch, okgoarch, nelem(okgoarch)) < 0)
fatal("unknown $GOHOSTARCH %s", gohostarch);
xgetenv(&b, "GOARCH");
if(b.len == 0)
bwritestr(&b, gohostarch);
goarch = btake(&b);
if((i=find(goarch, okgoarch, nelem(okgoarch))) < 0)
fatal("unknown $GOARCH %s", goarch);
bprintf(&b, "%c", gochars[i]);
gochar = btake(&b);
xgetenv(&b, "GOROOT_FINAL");
if(b.len > 0)
goroot_final = btake(&b);
else
goroot_final = goroot;
xsetenv("GOROOT", goroot);
xsetenv("GOARCH", goarch);
xsetenv("GOOS", goos);
// Make the environment more predictable.
xsetenv("LANG", "C");
xsetenv("LANGUAGE", "en_US.UTF8");
workdir = xworkdir();
xatexit(rmworkdir);
bfree(&b);
}
// rmworkdir deletes the work directory.
static void
rmworkdir(void)
{
xprintf("rm -rf %s\n", workdir);
xremoveall(workdir);
}
/*
* Initial tree setup.
*/
// The old tools that no longer live in $GOBIN or $GOROOT/bin.
static char *oldtool[] = {
"5a", "5c", "5g", "5l",
"6a", "6c", "6g", "6l",
"8a", "8c", "8g", "8l",
"6cov",
"6nm",
"cgo",
"ebnflint",
"goapi",
"gofix",
"goinstall",
"gomake",
"gopack",
"gopprof",
"gotest",
"gotype",
"govet",
"goyacc",
"quietgcc",
};
// setup sets up the tree for the initial build.
static void
setup(void)
{
int i;
Buf b;
char *p;
binit(&b);
run(&b, nil, 0, "ld", "--version", nil);
if(contains(bstr(&b), "gold") && contains(bstr(&b), " 2.20")) {
fatal("Your system has gold 2.20 installed.\n"
"This version is shipped by Ubuntu even though\n"
"it is known not to work on Ubuntu.\n"
"Binaries built with this linker are likely to fail in mysterious ways.\n"
"\n"
"Run sudo apt-get remove binutils-gold.");
}
// Create tool directory.
p = bprintf(&b, "%s/bin", goroot);
fixslash(&b);
if(!isdir(p))
xmkdir(p);
p = bprintf(&b, "%s/bin/go-tool", goroot);
fixslash(&b);
if(!isdir(p))
xmkdir(p);
// Create package directory.
p = bprintf(&b, "%s/pkg", goroot);
fixslash(&b);
if(!isdir(p))
xmkdir(p);
p = bprintf(&b, "%s/pkg/%s_%s", goroot, goos, goarch);
fixslash(&b);
xremoveall(p);
xmkdir(p);
// Remove old pre-tool binaries.
for(i=0; i<nelem(oldtool); i++)
xremove(bprintf(&b, "%s%s%s%s%s", goroot, slash, "bin", slash, oldtool[i]));
// If $GOBIN is set and has a Go compiler, it must be cleaned.
for(i=0; gochars[i]; i++) {
if(isfile(bprintf(&b, "%s%s%c%s", gobin, slash, gochars[i], "g"))) {
for(i=0; i<nelem(oldtool); i++)
xremove(bprintf(&b, "%s%s%s", gobin, slash, oldtool[i]));
break;
}
}
bfree(&b);
}
/*
* C library and tool building
*/
// gccargs is the gcc command line to use for compiling a single C file.
static char *gccargs[] = {
"gcc",
"-Wall",
"-Wno-sign-compare",
"-Wno-missing-braces",
"-Wno-parentheses",
"-Wno-unknown-pragmas",
"-Wno-switch",
"-Wno-comment",
"-Werror",
"-fno-common",
"-ggdb",
"-O2",
"-c",
};
// deptab lists changes to the default dependencies for a given prefix.
// deps ending in /* read the whole directory; deps beginning with -
// exclude files with that prefix.
static struct {
char *prefix; // prefix of target
char *dep[20]; // dependency tweaks for targets with that prefix
} deptab[] = {
{"lib9", {
"$GOROOT/include/u.h",
"$GOROOT/include/utf.h",
"$GOROOT/include/fmt.h",
"$GOROOT/include/libc.h",
"fmt/*",
"utf/*",
"-utf/mkrunetype",
"-utf\\mkrunetype",
"-utf/runetypebody",
"-utf\\runetypebody",
}},
{"libbio", {
"$GOROOT/include/u.h",
"$GOROOT/include/utf.h",
"$GOROOT/include/fmt.h",
"$GOROOT/include/libc.h",
"$GOROOT/include/bio.h",
}},
{"libmach", {
"$GOROOT/include/u.h",
"$GOROOT/include/utf.h",
"$GOROOT/include/fmt.h",
"$GOROOT/include/libc.h",
"$GOROOT/include/bio.h",
"$GOROOT/include/ar.h",
"$GOROOT/include/bootexec.h",
"$GOROOT/include/mach.h",
"$GOROOT/include/ureg_amd64.h",
"$GOROOT/include/ureg_arm.h",
"$GOROOT/include/ureg_x86.h",
}},
{"cmd/cc", {
"-pgen.c",
"-pswt.c",
}},
{"cmd/gc", {
"-cplx.c",
"-pgen.c",
"-y1.tab.c", // makefile dreg
"opnames.h",
}},
{"cmd/5c", {
"../cc/pgen.c",
"../cc/pswt.c",
"../5l/enam.c",
"$GOROOT/lib/libcc.a",
}},
{"cmd/6c", {
"../cc/pgen.c",
"../cc/pswt.c",
"../6l/enam.c",
"$GOROOT/lib/libcc.a",
}},
{"cmd/8c", {
"../cc/pgen.c",
"../cc/pswt.c",
"../8l/enam.c",
"$GOROOT/lib/libcc.a",
}},
{"cmd/5g", {
"../gc/cplx.c",
"../gc/pgen.c",
"../5l/enam.c",
"$GOROOT/lib/libgc.a",
}},
{"cmd/6g", {
"../gc/cplx.c",
"../gc/pgen.c",
"../6l/enam.c",
"$GOROOT/lib/libgc.a",
}},
{"cmd/8g", {
"../gc/cplx.c",
"../gc/pgen.c",
"../8l/enam.c",
"$GOROOT/lib/libgc.a",
}},
{"cmd/5l", {
"../ld/*",
"enam.c",
}},
{"cmd/6l", {
"../ld/*",
"enam.c",
}},
{"cmd/8l", {
"../ld/*",
"enam.c",
}},
{"cmd/", {
"$GOROOT/lib/libmach.a",
"$GOROOT/lib/libbio.a",
"$GOROOT/lib/lib9.a",
}},
};
// depsuffix records the allowed suffixes for source files.
char *depsuffix[] = {
".c",
".h",
".s",
".go",
};
// gentab records how to generate some trivial files.
static struct {
char *name;
void (*gen)(char*, char*);
} gentab[] = {
{"opnames.h", gcopnames},
{"enam.c", mkenam},
};
// install installs the library, package, or binary associated with dir,
// which is relative to $GOROOT/src.
static void
install(char *dir)
{
char *name, *p, *elem, *prefix;
bool islib, ispkg, isgo, stale;
Buf b, b1, path;
Vec compile, files, link, go, missing, clean, lib, extra;
Time ttarg, t;
int i, j, k, n;
binit(&b);
binit(&b1);
binit(&path);
vinit(&compile);
vinit(&files);
vinit(&link);
vinit(&go);
vinit(&missing);
vinit(&clean);
vinit(&lib);
vinit(&extra);
// path = full path to dir.
bprintf(&path, "%s/src/%s", goroot, dir);
fixslash(&path);
name = lastelem(dir);
islib = hasprefix(dir, "lib") || streq(dir, "cmd/cc") || streq(dir, "cmd/gc");
ispkg = hasprefix(dir, "pkg");
isgo = ispkg || streq(dir, "cmd/go");
// Start final link command line.
// Note: code below knows that link.p[2] is the target.
if(islib) {
// C library.
vadd(&link, "ar");
vadd(&link, "rsc");
prefix = "";
if(!hasprefix(name, "lib"))
prefix = "lib";
bprintf(&b, "%s/lib/%s%s.a", goroot, prefix, name);
fixslash(&b);
vadd(&link, bstr(&b));
} else if(ispkg) {
// Go library (package).
bprintf(&b, "%s/bin/go-tool/pack", goroot);
fixslash(&b);
vadd(&link, bstr(&b));
vadd(&link, "grc");
p = bprintf(&b, "%s/pkg/%s_%s/%s", goroot, goos, goarch, dir+4);
*xstrrchr(p, '/') = '\0';
xmkdirall(p);
bprintf(&b, "%s/pkg/%s_%s/%s.a", goroot, goos, goarch, dir+4);
fixslash(&b);
vadd(&link, bstr(&b));
} else if(streq(dir, "cmd/go")) {
// Go command.
bprintf(&b, "%s/bin/go-tool/%sl", goroot, gochar);
fixslash(&b);
vadd(&link, bstr(&b));
vadd(&link, "-o");
bprintf(&b, "%s/bin/go-tool/go_bootstrap", goroot);
fixslash(&b);
vadd(&link, bstr(&b));
} else {
// C command.
vadd(&link, "gcc");
vadd(&link, "-o");
bprintf(&b, "%s/bin/go-tool/%s", goroot, name);
fixslash(&b);
vadd(&link, bstr(&b));
}
ttarg = mtime(link.p[2]);
// Gather files that are sources for this target.
// Everything in that directory, and any target-specific
// additions.
xreaddir(&files, bstr(&path));
for(i=0; i<nelem(deptab); i++) {
if(hasprefix(dir, deptab[i].prefix)) {
for(j=0; (p=deptab[i].dep[j])!=nil; j++) {
if(hasprefix(p, "$GOROOT/")) {
bprintf(&b1, "%s/%s", goroot, p+8);
p = bstr(&b1);
}
if(hassuffix(p, ".a")) {
vadd(&lib, p);
continue;
}
if(hassuffix(p, "/*")) {
bprintf(&b, "%s/%s", bstr(&path), p);
b.len -= 2;
fixslash(&b);
xreaddir(&extra, bstr(&b));
bprintf(&b, "%s", p);
b.len -= 2;
for(k=0; k<extra.len; k++) {
bprintf(&b1, "%s/%s", bstr(&b), extra.p[k]);
fixslash(&b1);
vadd(&files, bstr(&b1));
}
continue;
}
if(hasprefix(p, "-")) {
p++;
n = 0;
for(k=0; k<files.len; k++) {
if(hasprefix(files.p[k], p))
xfree(files.p[k]);
else
files.p[n++] = files.p[k];
}
files.len = n;
continue;
}
vadd(&files, p);
}
}
}
vuniq(&files);
// Convert to absolute paths.
for(i=0; i<files.len; i++) {
if(!isabs(files.p[i])) {
bprintf(&b, "%s/%s", bstr(&path), files.p[i]);
fixslash(&b);
xfree(files.p[i]);
files.p[i] = btake(&b);
}
}
// For package runtime, copy some files into the work space.
if(streq(dir, "pkg/runtime")) {
copy(bprintf(&b, "%s/arch_GOARCH.h", workdir),
bprintf(&b1, "%s/arch_%s.h", bstr(&path), goarch));
copy(bprintf(&b, "%s/defs_GOOS_GOARCH.h", workdir),
bprintf(&b1, "%s/defs_%s_%s.h", bstr(&path), goos, goarch));
copy(bprintf(&b, "%s/os_GOOS.h", workdir),
bprintf(&b1, "%s/os_%s.h", bstr(&path), goos));
copy(bprintf(&b, "%s/signals_GOOS.h", workdir),
bprintf(&b1, "%s/signals_%s.h", bstr(&path), goos));
copy(bprintf(&b, "%s/zasm_GOOS_GOARCH.h", workdir),
bprintf(&b1, "%s/zasm_%s_%s.h", bstr(&path), goos, goarch));
}
// Is the target up-to-date?
stale = 1; // TODO: Decide when 0 is okay.
n = 0;
for(i=0; i<files.len; i++) {
p = files.p[i];
for(j=0; j<nelem(depsuffix); j++)
if(hassuffix(p, depsuffix[j]))
goto ok;
xfree(files.p[i]);
continue;
ok:
t = mtime(p);
if(t > ttarg)
stale = 1;
if(t == 0) {
vadd(&missing, p);
files.p[n++] = files.p[i];
continue;
}
if(!hassuffix(p, ".a") && !shouldbuild(p, dir)) {
xfree(files.p[i]);
continue;
}
if(hassuffix(p, ".go"))
vadd(&go, p);
files.p[n++] = files.p[i];
}
files.len = n;
for(i=0; i<lib.len && !stale; i++)
if(mtime(lib.p[i]) > ttarg)
stale = 1;
if(!stale)
goto out;
// Generate any missing files.
for(i=0; i<missing.len; i++) {
p = missing.p[i];
elem = lastelem(p);
for(j=0; j<nelem(gentab); j++) {
if(streq(gentab[j].name, elem)) {
gentab[j].gen(bstr(&path), p);
vadd(&clean, p);
goto built;
}
}
fatal("missing file %s", p);
built:;
}
// Compile the files.
for(i=0; i<files.len; i++) {
if(!hassuffix(files.p[i], ".c") && !hassuffix(files.p[i], ".s"))
continue;
name = lastelem(files.p[i]);
vreset(&compile);
if(!isgo) {
// C library or tool.
vcopy(&compile, gccargs, nelem(gccargs));
if(streq(gohostarch, "amd64"))
vadd(&compile, "-m64");
else if(streq(gohostarch, "386"))
vadd(&compile, "-m32");
if(streq(dir, "lib9"))
vadd(&compile, "-DPLAN9PORT");
bprintf(&b, "%s/include", goroot);
fixslash(&b);
vadd(&compile, "-I");
vadd(&compile, bstr(&b));
vadd(&compile, "-I");
vadd(&compile, bstr(&path));
// runtime/goos.c gets the default constants hard-coded.
if(streq(name, "goos.c")) {
vadd(&compile, bprintf(&b, "-DGOOS=\"%s\"", goos));
vadd(&compile, bprintf(&b, "-DGOARCH=\"%s\"", goarch));
vadd(&compile, bprintf(&b, "-DGOROOT=\"%s\"", goroot));
vadd(&compile, bprintf(&b, "-DGOVERSION=\"%s\"", goversion));
}
// gc/lex.c records the GOEXPERIMENT setting used during the build.
if(streq(name, "lex.c")) {
xgetenv(&b, "GOEXPERIMENT");
vadd(&compile, bprintf(&b1, "-DGOEXPERIMENT=\"%s\"", bstr(&b)));
}
} else {
// Supporting files for a Go package.
if(hassuffix(files.p[i], ".s")) {
bprintf(&b, "%s/bin/go-tool/%sa", goroot, gochar);
fixslash(&b);
vadd(&compile, bstr(&b));
} else {
bprintf(&b, "%s/bin/go-tool/%sc", goroot, gochar);
fixslash(&b);
vadd(&compile, bstr(&b));
vadd(&compile, "-FVw");
}
vadd(&compile, "-I");
vadd(&compile, workdir);
vadd(&compile, bprintf(&b, "-DGOOS_%s", goos));
vadd(&compile, bprintf(&b, "-DGOARCH_%s", goos));
}
bprintf(&b, "%s/%s", workdir, lastelem(files.p[i]));
b.p[b.len-1] = 'o'; // was c or s
fixslash(&b);
vadd(&compile, "-o");
vadd(&compile, bstr(&b));
vadd(&link, bstr(&b));
vadd(&clean, bstr(&b));
vadd(&compile, files.p[i]);
runv(nil, bstr(&path), CheckExit, &compile);
vreset(&compile);
}
if(isgo) {
// The last loop was compiling individual files.
// Hand the Go files to the compiler en masse.
vreset(&compile);
bprintf(&b, "%s/bin/go-tool/%sg", goroot, gochar);
fixslash(&b);
vadd(&compile, bstr(&b));
bprintf(&b, "%s/_go_.%s", workdir, gochar);
fixslash(&b);
vadd(&compile, "-o");
vadd(&compile, bstr(&b));
vadd(&clean, bstr(&b));
vadd(&link, bstr(&b));
vadd(&compile, "-p");
if(hasprefix(dir, "pkg/"))
vadd(&compile, dir+4);
else
vadd(&compile, "main");
if(streq(dir, "pkg/runtime"))
vadd(&compile, "-+");
vcopy(&compile, go.p, go.len);
runv(nil, bstr(&path), CheckExit, &compile);
}
if(!islib && !isgo) {
// C binaries need the libraries explicitly, and -lm.
vcopy(&link, lib.p, lib.len);
vadd(&link, "-lm");
}
// Remove target before writing it.
xremove(link.p[2]);
runv(nil, nil, CheckExit, &link);
out:
for(i=0; i<clean.len; i++)
xremove(clean.p[i]);
bfree(&b);
bfree(&b1);
bfree(&path);
vfree(&compile);
vfree(&files);
vfree(&link);
vfree(&go);
vfree(&missing);
vfree(&clean);
vfree(&lib);
vfree(&extra);
}
// matchfield reports whether the field matches this build.
static bool
matchfield(char *f)
{
return streq(f, goos) || streq(f, goarch) || streq(f, "cmd_go_bootstrap");
}
// shouldbuild reports whether we should build this file.
// It applies the same rules that are used with context tags
// in package go/build, except that the GOOS and GOARCH
// can appear anywhere in the file name, not just after _.
// In particular, they can be the entire file name (like windows.c).
// We also allow the special tag cmd_go_bootstrap.
// See ../go/bootstrap.go and package go/build.
static bool
shouldbuild(char *file, char *dir)
{
char *name, *p;
int i, j, ret, true;
Buf b;
Vec lines, fields;
// Check file name for GOOS or GOARCH.
name = lastelem(file);
for(i=0; i<nelem(okgoos); i++)
if(contains(name, okgoos[i]) && !streq(okgoos[i], goos))
return 0;
for(i=0; i<nelem(okgoarch); i++)
if(contains(name, okgoarch[i]) && !streq(okgoarch[i], goarch))
return 0;
// Omit test files.
if(contains(name, "_test"))
return 0;
// Check file contents for // +build lines.
binit(&b);
vinit(&lines);
vinit(&fields);
ret = 1;
readfile(&b, file);
splitlines(&lines, bstr(&b));
for(i=0; i<lines.len; i++) {
p = lines.p[i];
while(*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n')
p++;
if(*p == '\0')
continue;
if(contains(p, "package documentation")) {
ret = 0;
goto out;
}
if(contains(p, "package main") && !streq(dir, "cmd/go")) {
ret = 0;
goto out;
}
if(!hasprefix(p, "//"))
break;
if(!contains(p, "+build"))
continue;
splitfields(&fields, lines.p[i]);
if(fields.len < 2 || !streq(fields.p[1], "+build"))
continue;
for(j=2; j<fields.len; j++) {
p = fields.p[j];
if((*p == '!' && !matchfield(p+1)) || matchfield(p))
goto fieldmatch;
}
ret = 0;
goto out;
fieldmatch:;
}
out:
bfree(&b);
vfree(&lines);
vfree(&fields);
return ret;
}
// fixslash rewrites / to \ when the slash character is \, so that the paths look conventional.
static void
fixslash(Buf *b)
{
int i;
if(slash[0] == '/')
return;
for(i=0; i<b->len; i++)
if(b->p[i] == '/')
b->p[i] = '\\';
}
// copy copies the file src to dst, via memory (so only good for small files).
static void
copy(char *dst, char *src)
{
Buf b;
binit(&b);
readfile(&b, src);
writefile(&b, dst);
bfree(&b);
}
/*
* command implementations
*/
// The env command prints the default environment.
void
cmdenv(int argc, char **argv)
{
USED(argc);
USED(argv);
xprintf("GOROOT=%s\n", goroot);
xprintf("GOARCH=%s\n", goarch);
xprintf("GOOS=%s\n", goos);
}
// buildorder records the order of builds for the 'go bootstrap' command.
static char *buildorder[] = {
"lib9",
"libbio",
"libmach",
"cmd/cov",
"cmd/nm",
"cmd/pack",
"cmd/prof",
"cmd/cc", // must be before c
"cmd/gc", // must be before g
"cmd/%sl", // must be before a, c, g
"cmd/%sa",
"cmd/%sc",
"cmd/%sg",
// The dependency order here was copied from a buildscript
// back when there were build scripts. Will have to
// be maintained by hand, but shouldn't change very
// often.
"pkg/runtime",
"pkg/errors",
"pkg/sync/atomic",
"pkg/sync",
"pkg/io",
"pkg/unicode",
"pkg/unicode/utf8",
"pkg/unicode/utf16",
"pkg/bytes",
"pkg/math",
"pkg/strings",
"pkg/strconv",
"pkg/bufio",
"pkg/sort",
"pkg/container/heap",
"pkg/encoding/base64",
"pkg/syscall",
"pkg/time",
"pkg/os",
"pkg/reflect",
"pkg/fmt",
"pkg/encoding/json",
"pkg/encoding/gob",
"pkg/flag",
"pkg/path/filepath",
"pkg/path",
"pkg/io/ioutil",
"pkg/log",
"pkg/regexp/syntax",
"pkg/regexp",
"pkg/go/token",
"pkg/go/scanner",
"pkg/go/ast",
"pkg/go/parser",
"pkg/go/build",
"pkg/os/exec",
"pkg/net/url",
"pkg/text/template/parse",
"pkg/text/template",
"cmd/go",
};
// The bootstrap command runs a build from scratch,
// stopping at having installed the go_bootstrap command.
void
cmdbootstrap(int argc, char **argv)
{
int i;
Buf b;
char *p;
setup();
// TODO: nuke();
binit(&b);
for(i=0; i<nelem(buildorder); i++) {
p = bprintf(&b, buildorder[i], gochar);
xprintf("%s\n", p);
install(p);
}
bfree(&b);
}
// Install installs the list of packages named on the command line.
void
cmdinstall(int argc, char **argv)
{
int i;
for(i=1; i<argc; i++)
install(argv[i]);
}
// Copyright 2012 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.
#include "a.h"
#include <stdio.h>
/*
* Helpers for building cmd/gc.
*/
// gcopnames creates opnames.h from go.h.
// It finds the OXXX enum, pulls out all the constants
// from OXXX to OEND, and writes a table mapping
// op to string.
void
gcopnames(char *dir, char *file)
{
char *p, *q;
int i, j, end;
Buf in, b, out;
Vec lines, fields;
binit(&in);
binit(&b);
binit(&out);
vinit(&lines);
vinit(&fields);
bwritestr(&out, bprintf(&b, "// auto generated by go tool dist\n"));
bwritestr(&out, bprintf(&b, "static char *opnames[] = {\n"));
readfile(&in, bprintf(&b, "%s/go.h", dir));
splitlines(&lines, bstr(&in));
i = 0;
while(i<lines.len && !contains(lines.p[i], "OXXX"))
i++;
end = 0;
for(; i<lines.len && !end; i++) {
p = xstrstr(lines.p[i], "//");
if(p != nil)
*p = '\0';
end = contains(lines.p[i], "OEND");
splitfields(&fields, lines.p[i]);
for(j=0; j<fields.len; j++) {
q = fields.p[j];
if(*q == 'O')
q++;
p = q+xstrlen(q)-1;
if(*p == ',')
*p = '\0';
bwritestr(&out, bprintf(&b, " [O%s] = \"%s\",\n", q, q));
}
}
bwritestr(&out, bprintf(&b, "};\n"));
writefile(&out, file);
bfree(&in);
bfree(&b);
bfree(&out);
vfree(&lines);
vfree(&fields);
}
// mkenam reads [568].out.h and writes enam.c
// The format is much the same as the Go opcodes above.
void
mkenam(char *dir, char *file)
{
int i, ch;
Buf in, b, out;
Vec lines;
char *p;
binit(&b);
binit(&in);
binit(&out);
vinit(&lines);
ch = dir[xstrlen(dir)-2];
bprintf(&b, "%s/../%cl/%c.out.h", dir, ch, ch);
readfile(&in, bstr(&b));
splitlines(&lines, bstr(&in));
bwritestr(&out, "char* anames[] = {\n");
for(i=0; i<lines.len; i++) {
if(hasprefix(lines.p[i], "\tA")) {
p = xstrstr(lines.p[i], ",");
if(p)
*p = '\0';
p = xstrstr(lines.p[i], "\n");
if(p)
*p = '\0';
p = lines.p[i] + 2;
bwritestr(&out, bprintf(&b, "\t\"%s\",\n", p));
}
}
bwritestr(&out, "};\n");
writefile(&out, file);
bfree(&b);
bfree(&in);
bfree(&out);
vfree(&lines);
}
// Copyright 2012 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.
#include "a.h"
// cmdtab records the available commands.
static struct {
char *name;
void (*f)(int, char**);
} cmdtab[] = {
{"bootstrap", cmdbootstrap},
{"env", cmdenv},
{"install", cmdinstall},
};
// The OS-specific main calls into the portable code here.
void
xmain(int argc, char **argv)
{
int i;
if(argc <= 1) {
xprintf("go tool dist commands:\n");
for(i=0; i<nelem(cmdtab); i++)
xprintf("\t%s\n", cmdtab[i].name);
xexit(1);
}
for(i=0; i<nelem(cmdtab); i++) {
if(streq(cmdtab[i].name, argv[1])) {
cmdtab[i].f(argc-1, argv+1);
return;
}
}
fatal("unknown command %s", argv[1]);
}
// Copyright 2012 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.
// These #ifdefs are being used as a substitute for
// build configuration, so that on any system, this
// tool can be built with the local equivalent of
// cc *.c
//
#ifndef WIN32
#ifndef PLAN9
#include "a.h"
#include <unistd.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/param.h>
#include <sys/utsname.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <stdarg.h>
// bprintf replaces the buffer with the result of the printf formatting
// and returns a pointer to the NUL-terminated buffer contents.
char*
bprintf(Buf *b, char *fmt, ...)
{
va_list arg;
char buf[4096];
breset(b);
va_start(arg, fmt);
vsnprintf(buf, sizeof buf, fmt, arg);
va_end(arg);
bwritestr(b, buf);
return bstr(b);
}
// breadfrom appends to b all the data that can be read from fd.
static void
breadfrom(Buf *b, int fd)
{
int n;
for(;;) {
bgrow(b, 4096);
n = read(fd, b->p+b->len, 4096);
if(n < 0)
fatal("read: %s", strerror(errno));
if(n == 0)
break;
b->len += n;
}
}
// xgetenv replaces b with the value of the named environment variable.
void
xgetenv(Buf *b, char *name)
{
char *p;
breset(b);
p = getenv(name);
if(p != NULL)
bwritestr(b, p);
}
// run runs the command named by cmd.
// If b is not nil, run replaces b with the output of the command.
// If dir is not nil, run runs the command in that directory.
// If mode is CheckExit, run calls fatal if the command is not successful.
void
run(Buf *b, char *dir, int mode, char *cmd, ...)
{
va_list arg;
Vec argv;
char *p;
vinit(&argv);
vadd(&argv, cmd);
va_start(arg, cmd);
while((p = va_arg(arg, char*)) != nil)
vadd(&argv, p);
va_end(arg);
runv(b, dir, mode, &argv);
vfree(&argv);
}
// runv is like run but takes a vector.
void
runv(Buf *b, char *dir, int mode, Vec *argv)
{
int i, p[2], pid, status;
Buf cmd;
char *q;
// Generate a copy of the command to show in a log.
// Substitute $WORK for the work directory.
binit(&cmd);
for(i=0; i<argv->len; i++) {
if(i > 0)
bwritestr(&cmd, " ");
q = argv->p[i];
if(workdir != nil && hasprefix(q, workdir)) {
bwritestr(&cmd, "$WORK");
q += strlen(workdir);
}
bwritestr(&cmd, q);
}
printf("%s\n", bstr(&cmd));
bfree(&cmd);
if(b != nil) {
breset(b);
if(pipe(p) < 0)
fatal("pipe: %s", strerror(errno));
}
switch(pid = fork()) {
case -1:
fatal("fork: %s", strerror(errno));
case 0:
if(b != nil) {
close(0);
close(p[0]);
dup2(p[1], 1);
dup2(p[1], 2);
if(p[1] > 2)
close(p[1]);
}
if(dir != nil) {
if(chdir(dir) < 0) {
fprintf(stderr, "chdir %s: %s\n", dir, strerror(errno));
_exit(1);
}
}
vadd(argv, nil);
execvp(argv->p[0], argv->p);
fprintf(stderr, "exec %s: %s\n", argv->p[0], strerror(errno));
_exit(1);
}
if(b != nil) {
close(p[1]);
breadfrom(b, p[0]);
close(p[0]);
}
wait:
errno = 0;
if(waitpid(pid, &status, 0) != pid) {
if(errno == EINTR)
goto wait;
fatal("waitpid: %s", strerror(errno));
}
if(mode==CheckExit && (!WIFEXITED(status) || WEXITSTATUS(status) != 0)) {
if(b != nil)
fwrite(b->p, b->len, 1, stderr);
fatal("%s failed", argv->p[0]);
}
}
// xgetwd replaces b with the current directory.
void
xgetwd(Buf *b)
{
char buf[MAXPATHLEN];
breset(b);
if(getcwd(buf, MAXPATHLEN) == nil)
fatal("getcwd: %s", strerror(errno));
bwritestr(b, buf);
}
// xrealwd replaces b with the 'real' name for the given path.
// real is defined as what getcwd returns in that directory.
void
xrealwd(Buf *b, char *path)
{
int fd;
fd = open(".", 0);
if(fd < 0)
fatal("open .: %s", strerror(errno));
if(chdir(path) < 0)
fatal("chdir %s: %s", path, strerror(errno));
xgetwd(b);
if(fchdir(fd) < 0)
fatal("fchdir: %s", strerror(errno));
close(fd);
}
// isdir reports whether p names an existing directory.
bool
isdir(char *p)
{
struct stat st;
return stat(p, &st) >= 0 && S_ISDIR(st.st_mode);
}
// isfile reports whether p names an existing file.
bool
isfile(char *p)
{
struct stat st;
return stat(p, &st) >= 0 && S_ISREG(st.st_mode);
}
// mtime returns the modification time of the file p.
Time
mtime(char *p)
{
struct stat st;
if(stat(p, &st) < 0)
return 0;
return (Time)st.st_mtime*1000000000LL;
}
// isabs reports whether p is an absolute path.
bool
isabs(char *p)
{
return hasprefix(p, "/");
}
// readfile replaces b with the content of the named file.
void
readfile(Buf *b, char *file)
{
int fd;
breset(b);
fd = open(file, 0);
if(fd < 0)
fatal("open %s: %s", file, strerror(errno));
breadfrom(b, fd);
close(fd);
}
// writefile writes b to the named file, creating it if needed.
void
writefile(Buf *b, char *file)
{
int fd;
fd = creat(file, 0666);
if(fd < 0)
fatal("create %s: %s", file, strerror(errno));
if(write(fd, b->p, b->len) != b->len)
fatal("short write: %s", strerror(errno));
close(fd);
}
// xmkdir creates the directory p.
void
xmkdir(char *p)
{
if(mkdir(p, 0777) < 0)
fatal("mkdir %s: %s", p, strerror(errno));
}
// xmkdirall creates the directory p and its parents, as needed.
void
xmkdirall(char *p)
{
char *q;
if(isdir(p))
return;
q = strrchr(p, '/');
if(q != nil) {
*q = '\0';
xmkdirall(p);
*q = '/';
}
xmkdir(p);
}
// xremove removes the file p.
void
xremove(char *p)
{
unlink(p);
}
// xremoveall removes the file or directory tree rooted at p.
void
xremoveall(char *p)
{
int i;
Buf b;
Vec dir;
binit(&b);
vinit(&dir);
if(isdir(p)) {
xreaddir(&dir, p);
for(i=0; i<dir.len; i++) {
bprintf(&b, "%s/%s", p, dir.p[i]);
xremoveall(bstr(&b));
}
rmdir(p);
} else {
unlink(p);
}
bfree(&b);
vfree(&dir);
}
// xreaddir replaces dst with a list of the names of the files in dir.
// The names are relative to dir; they are not full paths.
void
xreaddir(Vec *dst, char *dir)
{
DIR *d;
struct dirent *dp;
vreset(dst);
d = opendir(dir);
if(d == nil)
fatal("opendir %s: %s", dir, strerror(errno));
while((dp = readdir(d)) != nil) {
if(strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0)
continue;
vadd(dst, dp->d_name);
}
closedir(d);
}
// xworkdir creates a new temporary directory to hold object files
// and returns the name of that directory.
char*
xworkdir(void)
{
Buf b;
char *p;
binit(&b);
xgetenv(&b, "TMPDIR");
if(b.len == 0)
bwritestr(&b, "/var/tmp");
bwritestr(&b, "/go-cbuild-XXXXXX");
if(mkdtemp(bstr(&b)) == nil)
fatal("mkdtemp: %s", strerror(errno));
p = btake(&b);
bfree(&b);
return p;
}
// fatal prints an error message to standard error and exits.
void
fatal(char *msg, ...)
{
va_list arg;
fprintf(stderr, "go tool dist: ");
va_start(arg, msg);
vfprintf(stderr, msg, arg);
va_end(arg);
fprintf(stderr, "\n");
exit(1);
}
// xmalloc returns a newly allocated zeroed block of n bytes of memory.
// It calls fatal if it runs out of memory.
void*
xmalloc(int n)
{
void *p;
p = malloc(n);
if(p == nil)
fatal("out of memory");
memset(p, 0, n);
return p;
}
// xstrdup returns a newly allocated copy of p.
// It calls fatal if it runs out of memory.
char*
xstrdup(char *p)
{
p = strdup(p);
if(p == nil)
fatal("out of memory");
return p;
}
// xrealloc grows the allocation p to n bytes and
// returns the new (possibly moved) pointer.
// It calls fatal if it runs out of memory.
void*
xrealloc(void *p, int n)
{
p = realloc(p, n);
if(p == nil)
fatal("out of memory");
return p;
}
// xfree frees the result returned by xmalloc, xstrdup, or xrealloc.
void
xfree(void *p)
{
free(p);
}
// hassuffix reports whether p ends with suffix.
bool
hassuffix(char *p, char *suffix)
{
int np, ns;
np = strlen(p);
ns = strlen(suffix);
return np >= ns && strcmp(p+np-ns, suffix) == 0;
}
// hasprefix reports whether p begins wtih prefix.
bool
hasprefix(char *p, char *prefix)
{
return strncmp(p, prefix, strlen(prefix)) == 0;
}
// contains reports whether sep appears in p.
bool
contains(char *p, char *sep)
{
return strstr(p, sep) != nil;
}
// streq reports whether p and q are the same string.
bool
streq(char *p, char *q)
{
return strcmp(p, q) == 0;
}
// lastelem returns the final path element in p.
char*
lastelem(char *p)
{
char *out;
out = p;
for(; *p; p++)
if(*p == '/')
out = p+1;
return out;
}
// xmemmove copies n bytes from src to dst.
void
xmemmove(void *dst, void *src, int n)
{
memmove(dst, src, n);
}
// xmemcmp compares the n-byte regions starting at a and at b.
int
xmemcmp(void *a, void *b, int n)
{
return memcmp(a, b, n);
}
// xstrlen returns the length of the NUL-terminated string at p.
int
xstrlen(char *p)
{
return strlen(p);
}
// xexit exits the process with return code n.
void
xexit(int n)
{
exit(n);
}
// xatexit schedules the exit-handler f to be run when the program exits.
void
xatexit(void (*f)(void))
{
atexit(f);
}
// xprintf prints a message to standard output.
void
xprintf(char *fmt, ...)
{
va_list arg;
va_start(arg, fmt);
vprintf(fmt, arg);
va_end(arg);
}
// xsetenv sets the environment variable $name to the given value.
void
xsetenv(char *name, char *value)
{
setenv(name, value, 1);
}
// main takes care of OS-specific startup and dispatches to xmain.
int
main(int argc, char **argv)
{
char *p;
Buf b;
struct utsname u;
binit(&b);
p = argv[0];
if(hassuffix(p, "bin/go-tool/dist")) {
default_goroot = xstrdup(p);
default_goroot[strlen(p)-strlen("bin/go-tool/dist")] = '\0';
}
slash = "/";
#if defined(__APPLE__)
gohostos = "darwin";
// Even on 64-bit platform, darwin uname -m prints i386.
run(&b, nil, 0, "sysctl", "machdep.cpu.extfeatures", nil);
if(contains(bstr(&b), "EM64T"))
gohostarch = "amd64";
#elif defined(__linux__)
gohostos = "linux";
#else
fatal("unknown operating system");
#endif
if(gohostarch == nil) {
if(uname(&u) < 0)
fatal("uname: %s", strerror(errno));
if(contains(u.machine, "x86_64"))
gohostarch = "amd64";
else if(hassuffix(u.machine, "86"))
gohostarch = "386";
else if(contains(u.machine, "arm"))
gohostarch = "arm";
else
fatal("unknown architecture: %s", u.machine);
}
init();
xmain(argc, argv);
bfree(&b);
return 0;
}
// xqsort is a wrapper for the C standard qsort.
void
xqsort(void *data, int n, int elemsize, int (*cmp)(const void*, const void*))
{
qsort(data, n, elemsize, cmp);
}
// xstrcmp compares the NUL-terminated strings a and b.
int
xstrcmp(char *a, char *b)
{
return strcmp(a, b);
}
// xstrstr returns a pointer to the first occurrence of b in a.
char*
xstrstr(char *a, char *b)
{
return strstr(a, b);
}
// xstrrchr returns a pointer to the final occurrence of c in p.
char*
xstrrchr(char *p, int c)
{
return strrchr(p, c);
}
#endif // PLAN9
#endif // __WINDOWS__
// Copyright 2012 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.
// These #ifdefs are being used as a substitute for
// build configuration, so that on any system, this
// tool can be built with the local equivalent of
// cc *.c
//
#ifdef WIN32
// Portability layer implemented for Windows.
// See unix.c for doc comments about exported functions.
#include "a.h"
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
/*
* Windows uses 16-bit rune strings in the APIs.
* Define conversions between Rune* and UTF-8 char*.
*/
typedef unsigned char uchar;
typedef unsigned short Rune; // same as Windows
// encoderune encodes the rune r into buf and returns
// the number of bytes used.
static int
encoderune(char *buf, Rune r)
{
if(r < 0x80) { // 7 bits
buf[0] = r;
return 1;
}
if(r < 0x800) { // 5+6 bits
buf[0] = 0xc0 | (r>>6);
buf[1] = 0x80 | (r&0x3f);
return 2;
}
buf[0] = 0xe0 | (r>>12);
buf[1] = 0x80 | ((r>>6)&0x3f);
buf[2] = 0x80 | (r&0x3f);
return 3;
}
// decoderune decodes the rune encoding at sbuf into r
// and returns the number of bytes used.
static int
decoderune(Rune *r, char *sbuf)
{
uchar *buf;
buf = (uchar*)sbuf;
if(buf[0] < 0x80) {
*r = buf[0];
return 1;
}
if((buf[0]&0xe0) == 0xc0 && (buf[1]&0xc0) == 0x80) {
*r = (buf[0]&~0xc0)<<6 | (buf[1]&~0x80);
if(*r < 0x80)
goto err;
return 2;
}
if((buf[0]&0xf0) == 0xe0 && (buf[1]&0xc0) == 0x80 && (buf[2]&0xc0) == 0x80) {
*r = (buf[0]&~0xc0)<<12 | (buf[1]&~0x80)<<6 | (buf[2]&~0x80);
if(*r < 0x800)
goto err;
return 3;
}
err:
*r = 0xfffd;
return 1;
}
// toutf replaces b with the UTF-8 encoding of the rune string r.
static void
toutf(Buf *b, Rune *r)
{
int i, n;
char buf[4];
breset(b);
for(i=0; r[i]; i++) {
n = encoderune(buf, r[i]);
bwrite(b, buf, n);
}
}
// torune replaces *rp with a pointer to a newly allocated
// rune string equivalent of the UTF-8 string p.
static void
torune(Rune **rp, char *p)
{
int i, n;
Rune *r, *w, r1;
r = xmalloc((strlen(p)+1) * sizeof r[0]);
w = r;
while(*p)
p += decoderune(w++, p);
*w = 0;
*rp = r;
}
// errstr returns the most recent Windows error, in string form.
static char*
errstr(void)
{
DWORD code;
Rune *r;
Buf b;
binit(&b);
code = GetLastError();
r = nil;
FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
nil, code, 0, (Rune*)&r, 0, nil);
toutf(&b, r);
return bstr(&b); // leak but we're dying anyway
}
void
xgetenv(Buf *b, char *name)
{
char *p;
Rune *buf;
int n;
Rune *r;
breset(b);
torune(&r, name);
n = GetEnvironmentVariableW(r, NULL, 0);
if(n > 0) {
buf = xmalloc((n+1)*sizeof buf[0]);
GetEnvironmentVariableW(r, buf, n+1);
buf[n] = '\0';
toutf(b, buf);
xfree(buf);
}
xfree(r);
}
void
xsetenv(char *name, char *value)
{
Rune *rname, *rvalue;
torune(&rname, name);
torune(&rvalue, value);
SetEnvironmentVariableW(rname, rvalue);
xfree(rname);
xfree(rvalue);
}
char*
bprintf(Buf *b, char *fmt, ...)
{
va_list arg;
char buf[4096];
breset(b);
va_start(arg, fmt);
vsnprintf(buf, sizeof buf, fmt, arg);
va_end(arg);
bwritestr(b, buf);
return bstr(b);
}
static void
breadfrom(Buf *b, HANDLE h)
{
DWORD n;
for(;;) {
if(b->len > 1<<22)
fatal("unlikely file size in readfrom");
bgrow(b, 4096);
n = 0;
if(!ReadFile(h, b->p+b->len, 4096, &n, nil))
fatal("ReadFile: %s", errstr());
if(n == 0)
break;
b->len += n;
}
}
void
runv(Buf *b, char *dir, int mode, Vec *argv)
{
int i, j, nslash;
Buf cmd;
char *e, *q;
Rune *rcmd, *rexe, *rdir;
STARTUPINFOW si;
PROCESS_INFORMATION pi;
HANDLE p[2];
DWORD code;
binit(&cmd);
for(i=0; i<argv->len; i++) {
if(i > 0)
bwritestr(&cmd, " ");
q = argv->p[i];
if(workdir != nil && hasprefix(q, workdir)) {
bwritestr(&cmd, "$WORK");
q += strlen(workdir);
}
bwritestr(&cmd, q);
}
//xprintf("%s\n", bstr(&cmd));
breset(&cmd);
for(i=0; i<argv->len; i++) {
if(i > 0)
bwritestr(&cmd, " ");
q = argv->p[i];
if(contains(q, " ") || contains(q, "\t") || contains(q, "\\") || contains(q, "\"")) {
bwritestr(&cmd, "\"");
nslash = 0;
for(; *q; q++) {
if(*q == '\\') {
nslash++;
continue;
}
if(*q == '"') {
for(j=0; j<2*nslash+1; j++)
bwritestr(&cmd, "\\");
nslash = 0;
}
for(j=0; j<nslash; j++)
bwritestr(&cmd, "\\");
nslash = 0;
bwrite(&cmd, q, 1);
}
for(j=0; j<2*nslash; j++)
bwritestr(&cmd, "\\");
bwritestr(&cmd, "\"");
} else {
bwritestr(&cmd, q);
}
}
torune(&rcmd, bstr(&cmd));
rexe = nil;
rdir = nil;
if(dir != nil)
torune(&rdir, dir);
memset(&si, 0, sizeof si);
si.cb = sizeof si;
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdInput = INVALID_HANDLE_VALUE;
if(b == nil) {
si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
} else {
breset(b);
if(!CreatePipe(&p[0], &p[1], nil, 0))
fatal("CreatePipe: %s", errstr());
si.hStdOutput = p[1];
si.hStdError = p[1];
}
if(!CreateProcessW(rexe, rcmd, nil, nil, TRUE, 0, nil, rdir, &si, &pi)) {
if(mode!=CheckExit)
return;
fatal("%s: %s", argv->p[0], errstr());
}
if(rexe != nil)
xfree(rexe);
xfree(rcmd);
if(rdir != nil)
xfree(rdir);
if(b != nil) {
CloseHandle(p[1]);
breadfrom(b, p[0]);
CloseHandle(p[0]);
}
WaitForSingleObject(pi.hProcess, INFINITE);
if(!GetExitCodeProcess(pi.hProcess, &code))
fatal("GetExitCodeProcess: %s", errstr());
if(mode==CheckExit && code != 0)
fatal("%s failed", argv->p[0]);
}
void
run(Buf *b, char *dir, int mode, char *cmd, ...)
{
va_list arg;
Vec argv;
char *p;
vinit(&argv);
vadd(&argv, cmd);
va_start(arg, cmd);
while((p = va_arg(arg, char*)) != nil)
vadd(&argv, p);
va_end(arg);
runv(b, dir, mode, &argv);
vfree(&argv);
}
// rgetwd returns a rune string form of the current directory's path.
static Rune*
rgetwd(void)
{
int n;
Rune *r;
n = GetCurrentDirectory(0, nil);
r = xmalloc((n+1)*sizeof r[0]);
GetCurrentDirectoryW(n+1, r);
r[n] = '\0';
return r;
}
void
xgetwd(Buf *b)
{
Rune *r;
r = rgetwd();
breset(b);
toutf(b, r);
xfree(r);
}
void
xrealwd(Buf *b, char *path)
{
int n;
Rune *old;
Rune *rnew;
old = rgetwd();
torune(&rnew, path);
if(!SetCurrentDirectoryW(rnew))
fatal("chdir %s: %s", path, errstr());
free(rnew);
xgetwd(b);
if(!SetCurrentDirectoryW(old)) {
breset(b);
toutf(b, old);
fatal("chdir %s: %s", bstr(b), errstr());
}
}
bool
isdir(char *p)
{
int attr;
Rune *r;
torune(&r, p);
attr = GetFileAttributesW(r);
xfree(r);
return attr >= 0 && (attr & FILE_ATTRIBUTE_DIRECTORY);
}
bool
isfile(char *p)
{
int attr;
Rune *r;
torune(&r, p);
attr = GetFileAttributesW(r);
xfree(r);
return attr >= 0 && !(attr & FILE_ATTRIBUTE_DIRECTORY);
}
Time
mtime(char *p)
{
HANDLE h;
WIN32_FIND_DATAW data;
Rune *r;
Time t;
FILETIME *ft;
torune(&r, p);
h = FindFirstFileW(r, &data);
xfree(r);
if(h == INVALID_HANDLE_VALUE)
return 0;
ft = &data.ftLastWriteTime;
return (Time)ft->dwLowDateTime + ((Time)ft->dwHighDateTime<<32);
}
bool
isabs(char *p)
{
// c:/ or c:\
if(('A' <= p[0] && p[0] <= 'Z') || ('a' <= p[0] && p[0] <= 'z'))
return p[1] == ':' && (p[2] == '/' || p[2] == '\\');
// / or \
return p[0] == '/' || p[0] == '\\';
}
void
readfile(Buf *b, char *file)
{
HANDLE h;
Rune *r;
torune(&r, file);
h = CreateFileW(r, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);
if(h == INVALID_HANDLE_VALUE)
fatal("open %s: %s", file, errstr());
breadfrom(b, h);
CloseHandle(h);
}
void
writefile(Buf *b, char *file)
{
HANDLE h;
Rune *r;
DWORD n;
torune(&r, file);
h = CreateFileW(r, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, nil, CREATE_ALWAYS, 0, 0);
if(h == INVALID_HANDLE_VALUE)
fatal("create %s: %s", file, errstr());
n = 0;
if(!WriteFile(h, b->p, b->len, &n, 0))
fatal("write %s: %s", file, errstr());
CloseHandle(h);
}
void
xmkdir(char *p)
{
Rune *r;
torune(&r, p);
if(!CreateDirectoryW(r, nil))
fatal("mkdir %s: %s", p, errstr());
xfree(r);
}
void
xmkdirall(char *p)
{
int c;
char *q, *q2;
if(isdir(p))
return;
q = strrchr(p, '/');
q2 = strrchr(p, '\\');
if(q2 != nil && (q == nil || q < q2))
q = q2;
if(q != nil) {
c = *q;
*q = '\0';
xmkdirall(p);
*q = c;
}
xmkdir(p);
}
void
xremove(char *p)
{
int attr;
Rune *r;
torune(&r, p);
attr = GetFileAttributesW(r);
if(attr >= 0) {
if(attr & FILE_ATTRIBUTE_DIRECTORY)
RemoveDirectoryW(r);
else
DeleteFileW(r);
}
xfree(r);
}
void
xreaddir(Vec *dst, char *dir)
{
Rune *r;
Buf b;
HANDLE h;
WIN32_FIND_DATAW data;
char *p, *q;
binit(&b);
vreset(dst);
bwritestr(&b, dir);
bwritestr(&b, "\\*");
torune(&r, bstr(&b));
h = FindFirstFileW(r, &data);
xfree(r);
if(h == INVALID_HANDLE_VALUE)
goto out;
do{
toutf(&b, data.cFileName);
p = bstr(&b);
q = xstrrchr(p, '\\');
if(q != nil)
p = q+1;
if(!streq(p, ".") && !streq(p, ".."))
vadd(dst, p);
}while(FindNextFileW(h, &data));
FindClose(h);
out:
bfree(&b);
}
char*
xworkdir(void)
{
Rune buf[1024];
Rune tmp[MAX_PATH];
Rune go[3] = {'g', 'o', '\0'};
int n;
Buf b;
n = GetTempPathW(nelem(buf), buf);
if(n <= 0)
fatal("GetTempPath: %s", errstr());
buf[n] = '\0';
if(GetTempFileNameW(buf, go, 0, tmp) == 0)
fatal("GetTempFileName: %s", errstr());
DeleteFileW(tmp);
if(!CreateDirectoryW(tmp, nil))
fatal("create tempdir: %s", errstr());
binit(&b);
toutf(&b, tmp);
return btake(&b);
}
void
xremoveall(char *p)
{
int i;
Buf b;
Vec dir;
Rune *r;
binit(&b);
vinit(&dir);
torune(&r, p);
if(isdir(p)) {
xreaddir(&dir, p);
for(i=0; i<dir.len; i++) {
bprintf(&b, "%s/%s", p, dir.p[i]);
xremoveall(bstr(&b));
}
RemoveDirectoryW(r);
} else {
DeleteFileW(r);
}
xfree(r);
bfree(&b);
vfree(&dir);
}
void
fatal(char *msg, ...)
{
static char buf1[1024];
va_list arg;
va_start(arg, msg);
vsnprintf(buf1, sizeof buf1, msg, arg);
va_end(arg);
fprintf(stderr, "cbuild: %s\n", buf1);
fflush(stderr);
ExitProcess(1);
}
// HEAP is the persistent handle to the default process heap.
static HANDLE HEAP = INVALID_HANDLE_VALUE;
void*
xmalloc(int n)
{
void *p;
if(HEAP == INVALID_HANDLE_VALUE)
HEAP = GetProcessHeap();
p = HeapAlloc(HEAP, 0, n);
if(p == nil)
fatal("out of memory allocating %d: %s", n, errstr());
memset(p, 0, n);
return p;
}
char*
xstrdup(char *p)
{
char *q;
q = xmalloc(strlen(p)+1);
strcpy(q, p);
return q;
}
void
xfree(void *p)
{
if(HEAP == INVALID_HANDLE_VALUE)
HEAP = GetProcessHeap();
HeapFree(HEAP, 0, p);
}
void*
xrealloc(void *p, int n)
{
if(p == nil)
return xmalloc(n);
if(HEAP == INVALID_HANDLE_VALUE)
HEAP = GetProcessHeap();
p = HeapReAlloc(HEAP, HEAP_GENERATE_EXCEPTIONS, p, n);
if(p == nil)
fatal("out of memory reallocating %d", n);
return p;
}
bool
hassuffix(char *p, char *suffix)
{
int np, ns;
np = strlen(p);
ns = strlen(suffix);
return np >= ns && strcmp(p+np-ns, suffix) == 0;
}
bool
hasprefix(char *p, char *prefix)
{
return strncmp(p, prefix, strlen(prefix)) == 0;
}
bool
contains(char *p, char *sep)
{
return strstr(p, sep) != nil;
}
bool
streq(char *p, char *q)
{
return strcmp(p, q) == 0;
}
char*
lastelem(char *p)
{
char *out;
out = p;
for(; *p; p++)
if(*p == '/' || *p == '\\')
out = p+1;
return out;
}
void
xmemmove(void *dst, void *src, int n)
{
memmove(dst, src, n);
}
int
xmemcmp(void *a, void *b, int n)
{
return memcmp(a, b, n);
}
int
xstrlen(char *p)
{
return strlen(p);
}
void
xexit(int n)
{
exit(n);
}
void
xatexit(void (*f)(void))
{
atexit(f);
}
void
xprintf(char *fmt, ...)
{
va_list arg;
static char buf[1024];
DWORD n;
va_start(arg, fmt);
vsnprintf(buf, sizeof buf, fmt, arg);
va_end(arg);
n = 0;
WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), buf, strlen(buf), &n, 0);
fflush(stdout);
}
int
main(int argc, char **argv)
{
char *p;
setvbuf(stdout, nil, _IOLBF, 0);
setvbuf(stderr, nil, _IOLBF, 0);
p = argv[0];
if(hassuffix(p, "bin/go-tool/dist.exe") || hassuffix(p, "bin\\go-tool\\dist.exe")) {
default_goroot = xstrdup(p);
default_goroot[strlen(p)-strlen("bin/go-tool/dist.exe")] = '\0';
}
slash = "\\";
gohostos = "windows";
init();
xmain(argc, argv);
return 0;
}
void
xqsort(void *data, int n, int elemsize, int (*cmp)(const void*, const void*))
{
qsort(data, n, elemsize, cmp);
}
int
xstrcmp(char *a, char *b)
{
return strcmp(a, b);
}
char*
xstrstr(char *a, char *b)
{
return strstr(a, b);
}
char*
xstrrchr(char *p, int c)
{
char *ep;
ep = p+strlen(p);
for(ep=p+strlen(p); ep >= p; ep--)
if(*ep == c)
return ep;
return nil;
}
#endif // __WINDOWS__
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