mirror of
https://github.com/golang/go
synced 2024-11-25 03:47:57 -07:00
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
This commit is contained in:
parent
bf89d58e73
commit
3dd1e5be54
45
src/cmd/dist/README
vendored
Normal file
45
src/cmd/dist/README
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
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.
|
||||
|
125
src/cmd/dist/a.h
vendored
Normal file
125
src/cmd/dist/a.h
vendored
Normal file
@ -0,0 +1,125 @@
|
||||
// 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);
|
250
src/cmd/dist/buf.c
vendored
Normal file
250
src/cmd/dist/buf.c
vendored
Normal file
@ -0,0 +1,250 @@
|
||||
// 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);
|
||||
}
|
||||
}
|
916
src/cmd/dist/build.c
vendored
Normal file
916
src/cmd/dist/build.c
vendored
Normal file
@ -0,0 +1,916 @@
|
||||
// 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]);
|
||||
}
|
106
src/cmd/dist/buildgc.c
vendored
Normal file
106
src/cmd/dist/buildgc.c
vendored
Normal file
@ -0,0 +1,106 @@
|
||||
// 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);
|
||||
}
|
38
src/cmd/dist/main.c
vendored
Normal file
38
src/cmd/dist/main.c
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
// 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]);
|
||||
}
|
596
src/cmd/dist/unix.c
vendored
Normal file
596
src/cmd/dist/unix.c
vendored
Normal file
@ -0,0 +1,596 @@
|
||||
// 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__
|
774
src/cmd/dist/windows.c
vendored
Normal file
774
src/cmd/dist/windows.c
vendored
Normal file
@ -0,0 +1,774 @@
|
||||
// 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__
|
Loading…
Reference in New Issue
Block a user