mirror of https://github.com/golang/go.git
all: merge dev.cc (929f321) into master
This brings in cmd/dist written in Go, which is working on the primary builders. If this breaks your build, you need to get Go 1.4 and put it in $HOME/go1.4 (or, if you want to use some other directory, set $GOROOT_BOOTSTRAP to that directory). To build Go 1.4 from source: git clone -b release-branch.go1.4 $GOROOT $HOME/go1.4 cd $HOME/go1.4/src ./make.bash Or use a binary release: https://golang.org/dl/. See https://golang.org/s/go15bootstrap for more information. Change-Id: Ie4ae834c76ea35e39cc54e9878819a9e51b284d9
This commit is contained in:
commit
9af454c588
|
|
@ -1,45 +1,27 @@
|
|||
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.
|
||||
As of Go 1.5, dist and other parts of the compiler toolchain are written
|
||||
in Go, making bootstrapping a little more involved than in the past.
|
||||
The approach is to build the current release of Go with an earlier one.
|
||||
|
||||
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
|
||||
The process to install Go 1.x, for x ≥ 5, is:
|
||||
|
||||
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(&b2, "another string");
|
||||
vadd(&v1, bstr(&b2)); // 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.
|
||||
1. Build cmd/dist with Go 1.4.
|
||||
2. Using dist, build Go 1.x compiler toolchain with Go 1.4.
|
||||
3. Using dist, rebuild Go 1.x compiler toolchain with itself.
|
||||
4. Using dist, build Go 1.x cmd/go (as go_bootstrap) with Go 1.x compiler toolchain.
|
||||
5. Using go_bootstrap, build the remaining Go 1.x standard library and commands.
|
||||
|
||||
NOTE: During the transition from the old C-based toolchain to the Go-based one,
|
||||
step 2 also builds the parts of the toolchain written in C, and step 3 does not
|
||||
recompile those.
|
||||
|
||||
Because of backward compatibility, although the steps above say Go 1.4,
|
||||
in practice any release ≥ Go 1.4 but < Go 1.x will work as the bootstrap base.
|
||||
|
||||
See golang.org/s/go15bootstrap for more details.
|
||||
|
||||
Compared to Go 1.4 and earlier, dist will also take over much of what used to
|
||||
be done by make.bash/make.bat/make.rc and all of what used to be done by
|
||||
run.bash/run.bat/run.rc, because it is nicer to implement that logic in Go
|
||||
than in three different scripting languages simultaneously.
|
||||
|
|
|
|||
|
|
@ -1,165 +0,0 @@
|
|||
// 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]))
|
||||
#ifndef PLAN9
|
||||
#define USED(x) ((void)(x))
|
||||
#endif
|
||||
|
||||
// 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 bsubst(Buf *b, char *x, char *y);
|
||||
void bfree(Buf *b);
|
||||
void bgrow(Buf *b, int n);
|
||||
void binit(Buf *b);
|
||||
char* bpathf(Buf *b, char *fmt, ...);
|
||||
char* bprintf(Buf *b, char *fmt, ...);
|
||||
void bwritef(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 *goarch;
|
||||
extern char *gobin;
|
||||
extern char *gochar;
|
||||
extern char *gohostarch;
|
||||
extern char *gohostos;
|
||||
extern char *goos;
|
||||
extern char *goroot;
|
||||
extern char *goroot_final;
|
||||
extern char *goextlinkenabled;
|
||||
extern char *goversion;
|
||||
extern char *defaultcc;
|
||||
extern char *defaultcxxtarget;
|
||||
extern char *defaultcctarget;
|
||||
extern char *workdir;
|
||||
extern char *tooldir;
|
||||
extern char *slash;
|
||||
extern bool rebuildall;
|
||||
extern bool defaultclang;
|
||||
|
||||
int find(char*, char**, int);
|
||||
void init(void);
|
||||
void cmdbanner(int, char**);
|
||||
void cmdbootstrap(int, char**);
|
||||
void cmdclean(int, char**);
|
||||
void cmdenv(int, char**);
|
||||
void cmdinstall(int, char**);
|
||||
void cmdversion(int, char**);
|
||||
|
||||
// buildgc.c
|
||||
void gcopnames(char*, char*);
|
||||
void mkanames(char*, char*);
|
||||
|
||||
// buildruntime.c
|
||||
void mkzasm(char*, char*);
|
||||
void mkzsys(char*, char*);
|
||||
void mkzgoarch(char*, char*);
|
||||
void mkzgoos(char*, char*);
|
||||
void mkzruntimedefs(char*, char*);
|
||||
void mkzversion(char*, char*);
|
||||
void mkzexperiment(char*, char*);
|
||||
|
||||
// buildgo.c
|
||||
void mkzdefaultcc(char*, char*);
|
||||
|
||||
// main.c
|
||||
extern int vflag;
|
||||
extern int sflag;
|
||||
void usage(void);
|
||||
void xmain(int argc, char **argv);
|
||||
|
||||
// portability layer (plan9.c, unix.c, windows.c)
|
||||
bool contains(char *p, char *sep);
|
||||
void errprintf(char*, ...);
|
||||
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 copyfile(char*, char*, int);
|
||||
void run(Buf *b, char *dir, int mode, char *cmd, ...);
|
||||
void runv(Buf *b, char *dir, int mode, Vec *argv);
|
||||
void bgrunv(char *dir, int mode, Vec *argv);
|
||||
void bgwait(void);
|
||||
bool streq(char*, char*);
|
||||
bool cansse2(void);
|
||||
void writefile(Buf*, char*, int);
|
||||
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 xstrlen(char*);
|
||||
char* xstrrchr(char*, int);
|
||||
char* xstrstr(char*, char*);
|
||||
char* xworkdir(void);
|
||||
int xsamefile(char*, char*);
|
||||
char* xgetgoarm(void);
|
||||
int xtryexecfunc(void (*)(void));
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
Derived from Inferno include/kern.h.
|
||||
|
||||
http://code.google.com/p/inferno-os/source/browse/include/kern.h
|
||||
|
||||
Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
|
||||
Revisions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com). All rights reserved.
|
||||
Portions Copyright © 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/* command line */
|
||||
extern char *argv0;
|
||||
#define ARGBEGIN for((argv0=(argv0?argv0:*argv)),argv++,argc--;\
|
||||
argv[0] && argv[0][0]=='-' && argv[0][1];\
|
||||
argc--, argv++) {\
|
||||
char *_args, *_argt;\
|
||||
char _argc;\
|
||||
_args = &argv[0][1];\
|
||||
if(_args[0]=='-' && _args[1]==0){\
|
||||
argc--; argv++; break;\
|
||||
}\
|
||||
while((_argc = *_args++) != 0)\
|
||||
switch(_argc)
|
||||
#define ARGEND _argt=0;USED(_argt);USED(_argc);USED(_args);}USED(argv);USED(argc);
|
||||
#define ARGF() (_argt=_args, _args="",\
|
||||
(*_argt? _argt: argv[1]? (argc--, *++argv): 0))
|
||||
#define EARGF(x) (_argt=_args, _args="",\
|
||||
(*_argt? _argt: argv[1]? (argc--, *++argv): ((x), fatal("usage"), (char*)0)))
|
||||
|
||||
#define ARGC() _argc
|
||||
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
// 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"
|
||||
|
||||
#ifndef __ARMEL__
|
||||
char *
|
||||
xgetgoarm(void)
|
||||
{
|
||||
return "6";
|
||||
}
|
||||
#else
|
||||
static void useVFPv3(void);
|
||||
static void useVFPv1(void);
|
||||
|
||||
char *
|
||||
xgetgoarm(void)
|
||||
{
|
||||
#if defined(__FreeBSD__)
|
||||
// FreeBSD has broken VFP support
|
||||
return "5";
|
||||
#endif
|
||||
// NaCl always has VFP support.
|
||||
if(streq(goos, "nacl") || xtryexecfunc(useVFPv3))
|
||||
return "7";
|
||||
else if(xtryexecfunc(useVFPv1))
|
||||
return "6";
|
||||
return "5";
|
||||
}
|
||||
|
||||
static void
|
||||
useVFPv3(void)
|
||||
{
|
||||
// try to run VFPv3-only "vmov.f64 d0, #112" instruction
|
||||
// we can't use that instruction directly, because we
|
||||
// might be compiling with a soft-float only toolchain.
|
||||
//
|
||||
// some newer toolchains are configured to use thumb
|
||||
// by default, so we need to do some mode changing magic
|
||||
// here.
|
||||
// We can use "bx pc; nop" here, but GNU as(1) insists
|
||||
// on warning us
|
||||
// "use of r15 in bx in ARM mode is not really useful"
|
||||
// so we workaround that by using "bx r0"
|
||||
__asm__ __volatile__ ("mov r0, pc");
|
||||
__asm__ __volatile__ ("bx r0");
|
||||
__asm__ __volatile__ (".word 0xeeb70b00"); // vmov.f64 d0, #112
|
||||
__asm__ __volatile__ (".word 0xe12fff1e"); // bx lr
|
||||
}
|
||||
|
||||
static void
|
||||
useVFPv1(void)
|
||||
{
|
||||
// try to run "vmov.f64 d0, d0" instruction
|
||||
// we can't use that instruction directly, because we
|
||||
// might be compiling with a soft-float only toolchain
|
||||
//
|
||||
// some newer toolchains are configured to use thumb
|
||||
// by default, so we need to do some mode changing magic
|
||||
// here.
|
||||
// We can use "bx pc; nop" here, but GNU as(1) insists
|
||||
// on warning us
|
||||
// "use of r15 in bx in ARM mode is not really useful"
|
||||
// so we workaround that by using "bx r0"
|
||||
__asm__ __volatile__ ("mov r0, pc");
|
||||
__asm__ __volatile__ ("bx r0");
|
||||
__asm__ __volatile__ (".word 0xeeb00b40"); // vomv.f64 d0, d0
|
||||
__asm__ __volatile__ (".word 0xe12fff1e"); // bx lr
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,284 +0,0 @@
|
|||
// 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;
|
||||
}
|
||||
|
||||
// bsubst rewites b to replace all occurrences of x with y.
|
||||
void
|
||||
bsubst(Buf *b, char *x, char *y)
|
||||
{
|
||||
char *p;
|
||||
int nx, ny, pos;
|
||||
|
||||
nx = xstrlen(x);
|
||||
ny = xstrlen(y);
|
||||
|
||||
pos = 0;
|
||||
for(;;) {
|
||||
p = xstrstr(bstr(b)+pos, x);
|
||||
if(p == nil)
|
||||
break;
|
||||
if(nx != ny) {
|
||||
if(nx < ny) {
|
||||
pos = p - b->p;
|
||||
bgrow(b, ny-nx);
|
||||
p = b->p + pos;
|
||||
}
|
||||
xmemmove(p+ny, p+nx, (b->p+b->len)-(p+nx));
|
||||
}
|
||||
xmemmove(p, y, ny);
|
||||
pos = p+ny - b->p;
|
||||
b->len += ny - nx;
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
static 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(n>0 && streq(v->p[i], v->p[n-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. If there is a \r immediately before
|
||||
// each \n, it will be removed.
|
||||
void
|
||||
splitlines(Vec *v, char *p)
|
||||
{
|
||||
int i;
|
||||
char *start;
|
||||
|
||||
vreset(v);
|
||||
start = p;
|
||||
for(i=0; p[i]; i++) {
|
||||
if((p[i] == '\r' && p[i+1] == '\n') || p[i] == '\n') {
|
||||
vaddn(v, start, (p+i+1)-start);
|
||||
if(p[i] == '\r') {
|
||||
v->p[v->len-1][(p+i)-start] = '\n';
|
||||
i++;
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -1,218 +0,0 @@
|
|||
// 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"
|
||||
|
||||
/*
|
||||
* 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, 0);
|
||||
|
||||
bfree(&in);
|
||||
bfree(&b);
|
||||
bfree(&out);
|
||||
vfree(&lines);
|
||||
vfree(&fields);
|
||||
}
|
||||
|
||||
static int
|
||||
xatoi(char *s, char **end)
|
||||
{
|
||||
int val = 0;
|
||||
for(; *s && *s >= '0' && *s <= '9'; ++s)
|
||||
val = val * 10 + (*s - '0');
|
||||
*end = s;
|
||||
return val;
|
||||
}
|
||||
|
||||
// mkanames reads [5689].out.h and writes anames[5689].c
|
||||
// The format is much the same as the Go opcodes above.
|
||||
// It also writes out cnames array for C_* constants and the dnames
|
||||
// array for D_* constants.
|
||||
void
|
||||
mkanames(char *dir, char *file)
|
||||
{
|
||||
int i, j, ch, n, unknown;
|
||||
Buf in, b, out, out2;
|
||||
Vec lines;
|
||||
char *p, *p2;
|
||||
Vec dnames[128];
|
||||
|
||||
binit(&b);
|
||||
binit(&in);
|
||||
binit(&out);
|
||||
binit(&out2);
|
||||
vinit(&lines);
|
||||
for(i=0; i<nelem(dnames); i++)
|
||||
vinit(&dnames[i]);
|
||||
|
||||
ch = file[xstrlen(file)-3];
|
||||
bprintf(&b, "%s/../cmd/%cl/%c.out.h", dir, ch, ch);
|
||||
readfile(&in, bstr(&b));
|
||||
splitlines(&lines, bstr(&in));
|
||||
|
||||
// Include link.h so that the extern declaration there is
|
||||
// checked against the non-extern declaration we are generating.
|
||||
bwritestr(&out, bprintf(&b, "// auto generated by go tool dist\n"));
|
||||
bwritestr(&out, bprintf(&b, "#include <u.h>\n"));
|
||||
bwritestr(&out, bprintf(&b, "#include <libc.h>\n"));
|
||||
bwritestr(&out, bprintf(&b, "#include <bio.h>\n"));
|
||||
bwritestr(&out, bprintf(&b, "#include <link.h>\n"));
|
||||
bwritestr(&out, bprintf(&b, "#include \"../cmd/%cl/%c.out.h\"\n", ch, ch));
|
||||
bwritestr(&out, bprintf(&b, "\n"));
|
||||
|
||||
bwritestr(&out, bprintf(&b, "char* anames%c[] = {\n", ch));
|
||||
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");
|
||||
|
||||
j=0;
|
||||
bprintf(&out2, "char* cnames%c[] = {\n", ch);
|
||||
for(i=0; i<lines.len; i++) {
|
||||
if(hasprefix(lines.p[i], "\tC_")) {
|
||||
p = xstrstr(lines.p[i], ",");
|
||||
if(p)
|
||||
*p = '\0';
|
||||
p = xstrstr(lines.p[i], "\n");
|
||||
if(p)
|
||||
*p = '\0';
|
||||
p = lines.p[i] + 3;
|
||||
bwritestr(&out2, bprintf(&b, "\t\"%s\",\n", p));
|
||||
j++;
|
||||
}
|
||||
}
|
||||
bwritestr(&out2, "};\n");
|
||||
if(j>0)
|
||||
bwriteb(&out, &out2);
|
||||
|
||||
j=unknown=0;
|
||||
n=-1;
|
||||
for(i=0; i<lines.len; i++) {
|
||||
if(hasprefix(lines.p[i], "\tD_")) {
|
||||
p = xstrstr(lines.p[i], ",");
|
||||
if(p)
|
||||
*p = '\0';
|
||||
p = xstrstr(lines.p[i], "\n");
|
||||
if(p)
|
||||
*p = '\0';
|
||||
|
||||
// Parse explicit value, if any
|
||||
p = xstrstr(lines.p[i], "=");
|
||||
if(p) {
|
||||
// Skip space after '='
|
||||
p2 = p + 1;
|
||||
while(*p2 == ' ' || *p2 == '\t')
|
||||
p2++;
|
||||
n = xatoi(p2, &p2);
|
||||
// We can't do anything about
|
||||
// non-numeric values or anything that
|
||||
// follows
|
||||
while(*p2 == ' ' || *p2 == '\t')
|
||||
p2++;
|
||||
if(*p2 != 0) {
|
||||
unknown = 1;
|
||||
continue;
|
||||
}
|
||||
// Truncate space before '='
|
||||
while(*(p-1) == ' ' || *(p-1) == '\t')
|
||||
p--;
|
||||
*p = '\0';
|
||||
unknown = 0;
|
||||
} else {
|
||||
n++;
|
||||
}
|
||||
|
||||
if(unknown || n >= nelem(dnames))
|
||||
continue;
|
||||
|
||||
p = lines.p[i] + 3;
|
||||
if(xstrcmp(p, "LAST") == 0)
|
||||
continue;
|
||||
vadd(&dnames[n], p);
|
||||
j++;
|
||||
}
|
||||
}
|
||||
if(j>0){
|
||||
bwritestr(&out, bprintf(&b, "char* dnames%c[D_LAST] = {\n", ch));
|
||||
for(i=0; i<nelem(dnames); i++) {
|
||||
if(dnames[i].len == 0)
|
||||
continue;
|
||||
bwritestr(&out, bprintf(&b, "\t[D_%s] = \"", dnames[i].p[0]));
|
||||
for(j=0; j<dnames[i].len; j++) {
|
||||
if(j != 0)
|
||||
bwritestr(&out, "/");
|
||||
bwritestr(&out, dnames[i].p[j]);
|
||||
}
|
||||
bwritestr(&out, "\",\n");
|
||||
}
|
||||
bwritestr(&out, "};\n");
|
||||
}
|
||||
|
||||
writefile(&out, file, 0);
|
||||
|
||||
bfree(&b);
|
||||
bfree(&in);
|
||||
bfree(&out);
|
||||
bfree(&out2);
|
||||
vfree(&lines);
|
||||
for(i=0; i<nelem(dnames); i++)
|
||||
vfree(&dnames[i]);
|
||||
}
|
||||
|
|
@ -0,0 +1,170 @@
|
|||
// 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.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
/*
|
||||
* 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.
|
||||
func gcopnames(dir, file string) {
|
||||
var out bytes.Buffer
|
||||
fmt.Fprintf(&out, "// auto generated by go tool dist\n")
|
||||
fmt.Fprintf(&out, "static char *opnames[] = {\n")
|
||||
|
||||
in := readfile(pathf("%s/go.h", dir))
|
||||
lines := splitlines(in)
|
||||
i := 0
|
||||
for i < len(lines) && !strings.Contains(lines[i], "OXXX") {
|
||||
i++
|
||||
}
|
||||
for _, line := range lines[i:] {
|
||||
if i := strings.Index(line, "//"); i >= 0 {
|
||||
line = line[:i]
|
||||
}
|
||||
for _, field := range splitfields(line) {
|
||||
field = strings.TrimPrefix(field, "O")
|
||||
field = strings.TrimSuffix(field, ",")
|
||||
fmt.Fprintf(&out, "\t[O%s] = \"%s\",\n", field, field)
|
||||
}
|
||||
if strings.Contains(line, "OEND") {
|
||||
break
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(&out, "};\n")
|
||||
|
||||
writefile(out.String(), file, 0)
|
||||
}
|
||||
|
||||
// mkanames reads [5689].out.h and writes anames[5689].c
|
||||
// The format is much the same as the Go opcodes above.
|
||||
// It also writes out cnames array for C_* constants and the dnames
|
||||
// array for D_* constants.
|
||||
func mkanames(dir, file string) {
|
||||
ch := file[len(file)-3]
|
||||
targ := pathf("%s/../cmd/%cl/%c.out.h", dir, ch, ch)
|
||||
in := readfile(targ)
|
||||
lines := splitlines(in)
|
||||
|
||||
// Include link.h so that the extern declaration there is
|
||||
// checked against the non-extern declaration we are generating.
|
||||
var out bytes.Buffer
|
||||
fmt.Fprintf(&out, "// auto generated by go tool dist\n")
|
||||
fmt.Fprintf(&out, "#include <u.h>\n")
|
||||
fmt.Fprintf(&out, "#include <libc.h>\n")
|
||||
fmt.Fprintf(&out, "#include <bio.h>\n")
|
||||
fmt.Fprintf(&out, "#include <link.h>\n")
|
||||
fmt.Fprintf(&out, "#include \"../cmd/%cl/%c.out.h\"\n", ch, ch)
|
||||
fmt.Fprintf(&out, "\n")
|
||||
|
||||
fmt.Fprintf(&out, "char* anames%c[] = {\n", ch)
|
||||
for _, line := range lines {
|
||||
if strings.HasPrefix(line, "\tA") {
|
||||
if i := strings.Index(line, ","); i >= 0 {
|
||||
line = line[:i]
|
||||
}
|
||||
if i := strings.Index(line, "\n"); i >= 0 {
|
||||
line = line[:i]
|
||||
}
|
||||
line = line[2:]
|
||||
fmt.Fprintf(&out, "\t\"%s\",\n", line)
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(&out, "};\n")
|
||||
|
||||
j := 0
|
||||
var out2 bytes.Buffer
|
||||
fmt.Fprintf(&out2, "char* cnames%c[] = {\n", ch)
|
||||
for _, line := range lines {
|
||||
if strings.HasPrefix(line, "\tC_") {
|
||||
if i := strings.Index(line, ","); i >= 0 {
|
||||
line = line[:i]
|
||||
}
|
||||
if i := strings.Index(line, "\n"); i >= 0 {
|
||||
line = line[:i]
|
||||
}
|
||||
line = line[3:]
|
||||
fmt.Fprintf(&out2, "\t\"%s\",\n", line)
|
||||
j++
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(&out2, "};\n")
|
||||
if j > 0 {
|
||||
out.Write(out2.Bytes())
|
||||
}
|
||||
|
||||
var dnames [128][]string
|
||||
j = 0
|
||||
unknown := false
|
||||
n := -1
|
||||
for _, line := range lines {
|
||||
if strings.HasPrefix(line, "\tD_") {
|
||||
if i := strings.Index(line, ","); i >= 0 {
|
||||
line = line[:i]
|
||||
}
|
||||
|
||||
// Parse explicit value, if any
|
||||
if i := strings.Index(line, "="); i >= 0 {
|
||||
value := strings.TrimSpace(line[i+1:])
|
||||
line = strings.TrimSpace(line[:i])
|
||||
var err error
|
||||
n, err = strconv.Atoi(value)
|
||||
if err != nil {
|
||||
// We can't do anything about
|
||||
// non-numeric values or anything that
|
||||
// follows.
|
||||
unknown = true
|
||||
continue
|
||||
}
|
||||
unknown = false
|
||||
} else {
|
||||
n++
|
||||
}
|
||||
|
||||
if unknown || n < 0 || n >= len(dnames) {
|
||||
continue
|
||||
}
|
||||
|
||||
line = strings.TrimSpace(line)
|
||||
line = line[len("D_"):]
|
||||
|
||||
if strings.Contains(line, "LAST") {
|
||||
continue
|
||||
}
|
||||
dnames[n] = append(dnames[n], line)
|
||||
j++
|
||||
}
|
||||
}
|
||||
|
||||
if j > 0 {
|
||||
fmt.Fprintf(&out, "char* dnames%c[D_LAST] = {\n", ch)
|
||||
for _, d := range dnames {
|
||||
if len(d) == 0 {
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(&out, "\t[D_%s] = \"", d[0])
|
||||
for k, name := range d {
|
||||
if k > 0 {
|
||||
fmt.Fprintf(&out, "/")
|
||||
}
|
||||
fmt.Fprintf(&out, "%s", name)
|
||||
}
|
||||
fmt.Fprintf(&out, "\",\n")
|
||||
}
|
||||
fmt.Fprintf(&out, "};\n")
|
||||
}
|
||||
|
||||
writefile(out.String(), file, 0)
|
||||
}
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
// 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"
|
||||
|
||||
/*
|
||||
* Helpers for building cmd/go and cmd/cgo.
|
||||
*/
|
||||
|
||||
// mkzdefaultcc writes zdefaultcc.go:
|
||||
//
|
||||
// package main
|
||||
// const defaultCC = <defaultcc>
|
||||
// const defaultCXX = <defaultcxx>
|
||||
//
|
||||
// It is invoked to write cmd/go/zdefaultcc.go
|
||||
// but we also write cmd/cgo/zdefaultcc.go.
|
||||
void
|
||||
mkzdefaultcc(char *dir, char *file)
|
||||
{
|
||||
Buf b, out;
|
||||
|
||||
USED(dir);
|
||||
|
||||
binit(&out);
|
||||
bprintf(&out,
|
||||
"// auto generated by go tool dist\n"
|
||||
"\n"
|
||||
"package main\n"
|
||||
"\n"
|
||||
"const defaultCC = `%s`\n"
|
||||
"const defaultCXX = `%s`\n",
|
||||
defaultcctarget, defaultcxxtarget);
|
||||
|
||||
writefile(&out, file, 0);
|
||||
|
||||
// Convert file name to replace.
|
||||
binit(&b);
|
||||
bwritestr(&b, file);
|
||||
if(slash[0] == '/')
|
||||
bsubst(&b, "/go/zdefaultcc.go", "/cgo/zdefaultcc.go");
|
||||
else
|
||||
bsubst(&b, "\\go\\zdefaultcc.go", "\\cgo\\zdefaultcc.go");
|
||||
writefile(&out, bstr(&b), 0);
|
||||
|
||||
bfree(&b);
|
||||
bfree(&out);
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
// 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.
|
||||
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
/*
|
||||
* Helpers for building cmd/go and cmd/cgo.
|
||||
*/
|
||||
|
||||
// mkzdefaultcc writes zdefaultcc.go:
|
||||
//
|
||||
// package main
|
||||
// const defaultCC = <defaultcc>
|
||||
// const defaultCXX = <defaultcxx>
|
||||
//
|
||||
// It is invoked to write cmd/go/zdefaultcc.go
|
||||
// but we also write cmd/cgo/zdefaultcc.go
|
||||
func mkzdefaultcc(dir, file string) {
|
||||
var out string
|
||||
|
||||
out = fmt.Sprintf(
|
||||
"// auto generated by go tool dist\n"+
|
||||
"\n"+
|
||||
"package main\n"+
|
||||
"\n"+
|
||||
"const defaultCC = `%s`\n"+
|
||||
"const defaultCXX = `%s`\n",
|
||||
defaultcctarget, defaultcxxtarget)
|
||||
|
||||
writefile(out, file, 0)
|
||||
|
||||
// Convert file name to replace: turn go into cgo.
|
||||
i := len(file) - len("go/zdefaultcc.go")
|
||||
file = file[:i] + "c" + file[i:]
|
||||
writefile(out, file, 0)
|
||||
}
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
// 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"
|
||||
|
||||
/*
|
||||
* Helpers for building runtime.
|
||||
*/
|
||||
|
||||
// mkzversion writes zversion.go:
|
||||
//
|
||||
// package runtime
|
||||
// const defaultGoroot = <goroot>
|
||||
// const theVersion = <version>
|
||||
//
|
||||
void
|
||||
mkzversion(char *dir, char *file)
|
||||
{
|
||||
Buf b, out;
|
||||
|
||||
USED(dir);
|
||||
|
||||
binit(&b);
|
||||
binit(&out);
|
||||
|
||||
bwritestr(&out, bprintf(&b,
|
||||
"// auto generated by go tool dist\n"
|
||||
"\n"
|
||||
"package runtime\n"
|
||||
"\n"
|
||||
"const defaultGoroot = `%s`\n"
|
||||
"const theVersion = `%s`\n"
|
||||
"var buildVersion = theVersion\n", goroot_final, goversion));
|
||||
|
||||
writefile(&out, file, 0);
|
||||
|
||||
bfree(&b);
|
||||
bfree(&out);
|
||||
}
|
||||
|
||||
// mkzexperiment writes zaexperiment.h (sic):
|
||||
//
|
||||
// #define GOEXPERIMENT "experiment string"
|
||||
//
|
||||
void
|
||||
mkzexperiment(char *dir, char *file)
|
||||
{
|
||||
Buf b, out, exp;
|
||||
|
||||
USED(dir);
|
||||
|
||||
binit(&b);
|
||||
binit(&out);
|
||||
binit(&exp);
|
||||
|
||||
xgetenv(&exp, "GOEXPERIMENT");
|
||||
bwritestr(&out, bprintf(&b,
|
||||
"// auto generated by go tool dist\n"
|
||||
"\n"
|
||||
"#define GOEXPERIMENT \"%s\"\n", bstr(&exp)));
|
||||
|
||||
writefile(&out, file, 0);
|
||||
|
||||
bfree(&b);
|
||||
bfree(&out);
|
||||
bfree(&exp);
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
// 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.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
/*
|
||||
* Helpers for building runtime.
|
||||
*/
|
||||
|
||||
// mkzversion writes zversion.go:
|
||||
//
|
||||
// package runtime
|
||||
// const defaultGoroot = <goroot>
|
||||
// const theVersion = <version>
|
||||
//
|
||||
func mkzversion(dir, file string) {
|
||||
out := fmt.Sprintf(
|
||||
"// auto generated by go tool dist\n"+
|
||||
"\n"+
|
||||
"package runtime\n"+
|
||||
"\n"+
|
||||
"const defaultGoroot = `%s`\n"+
|
||||
"const theVersion = `%s`\n"+
|
||||
"var buildVersion = theVersion\n", goroot_final, goversion)
|
||||
|
||||
writefile(out, file, 0)
|
||||
}
|
||||
|
||||
// mkzexperiment writes zaexperiment.h (sic):
|
||||
//
|
||||
// #define GOEXPERIMENT "experiment string"
|
||||
//
|
||||
func mkzexperiment(dir, file string) {
|
||||
out := fmt.Sprintf(
|
||||
"// auto generated by go tool dist\n"+
|
||||
"\n"+
|
||||
"#define GOEXPERIMENT \"%s\"\n", os.Getenv("GOEXPERIMENT"))
|
||||
|
||||
writefile(out, file, 0)
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
// Copyright 2015 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.
|
||||
|
||||
TEXT ·cpuid(SB),$0-8
|
||||
MOVL ax+4(FP), AX
|
||||
CPUID
|
||||
MOVL info+0(FP), DI
|
||||
MOVL AX, 0(DI)
|
||||
MOVL BX, 4(DI)
|
||||
MOVL CX, 8(DI)
|
||||
MOVL DX, 12(DI)
|
||||
RET
|
||||
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
// Copyright 2015 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.
|
||||
|
||||
TEXT ·cpuid(SB),$0-12
|
||||
MOVL ax+8(FP), AX
|
||||
CPUID
|
||||
MOVQ info+0(FP), DI
|
||||
MOVL AX, 0(DI)
|
||||
MOVL BX, 4(DI)
|
||||
MOVL CX, 8(DI)
|
||||
MOVL DX, 12(DI)
|
||||
RET
|
||||
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
// Copyright 2015 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.
|
||||
|
||||
// +build !386,!amd64
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
TEXT ·cpuid(SB),NOSPLIT,$0-0
|
||||
RET
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
// 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"
|
||||
|
||||
int vflag;
|
||||
int sflag;
|
||||
char *argv0;
|
||||
|
||||
// cmdtab records the available commands.
|
||||
static struct {
|
||||
char *name;
|
||||
void (*f)(int, char**);
|
||||
} cmdtab[] = {
|
||||
{"banner", cmdbanner},
|
||||
{"bootstrap", cmdbootstrap},
|
||||
{"clean", cmdclean},
|
||||
{"env", cmdenv},
|
||||
{"install", cmdinstall},
|
||||
{"version", cmdversion},
|
||||
};
|
||||
|
||||
// The OS-specific main calls into the portable code here.
|
||||
void
|
||||
xmain(int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
|
||||
if(argc <= 1)
|
||||
usage();
|
||||
|
||||
for(i=0; i<nelem(cmdtab); i++) {
|
||||
if(streq(cmdtab[i].name, argv[1])) {
|
||||
cmdtab[i].f(argc-1, argv+1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
xprintf("unknown command %s\n", argv[1]);
|
||||
usage();
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
// 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.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// cmdtab records the available commands.
|
||||
var cmdtab = []struct {
|
||||
name string
|
||||
f func()
|
||||
}{
|
||||
{"banner", cmdbanner},
|
||||
{"bootstrap", cmdbootstrap},
|
||||
{"clean", cmdclean},
|
||||
{"env", cmdenv},
|
||||
{"install", cmdinstall},
|
||||
{"version", cmdversion},
|
||||
}
|
||||
|
||||
// The OS-specific main calls into the portable code here.
|
||||
func xmain() {
|
||||
if len(os.Args) < 2 {
|
||||
usage()
|
||||
}
|
||||
cmd := os.Args[1]
|
||||
os.Args = os.Args[1:] // for flag parsing during cmd
|
||||
for _, ct := range cmdtab {
|
||||
if ct.name == cmd {
|
||||
flag.Usage = func() {
|
||||
fmt.Fprintf(os.Stderr, "usage: go tool dist %s [options]\n", cmd)
|
||||
flag.PrintDefaults()
|
||||
os.Exit(2)
|
||||
}
|
||||
ct.f()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
xprintf("unknown command %s\n", cmd)
|
||||
usage()
|
||||
}
|
||||
|
||||
func xflagparse(maxargs int) {
|
||||
flag.Var((*count)(&vflag), "v", "verbosity")
|
||||
flag.Parse()
|
||||
if maxargs >= 0 && flag.NArg() > maxargs {
|
||||
flag.Usage()
|
||||
}
|
||||
}
|
||||
|
||||
// count is a flag.Value that is like a flag.Bool and a flag.Int.
|
||||
// If used as -name, it increments the count, but -name=x sets the count.
|
||||
// Used for verbose flag -v.
|
||||
type count int
|
||||
|
||||
func (c *count) String() string {
|
||||
return fmt.Sprint(int(*c))
|
||||
}
|
||||
|
||||
func (c *count) Set(s string) error {
|
||||
switch s {
|
||||
case "true":
|
||||
*c++
|
||||
case "false":
|
||||
*c = 0
|
||||
default:
|
||||
n, err := strconv.Atoi(s)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid count %q", s)
|
||||
}
|
||||
*c = count(n)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *count) IsBoolFlag() bool {
|
||||
return true
|
||||
}
|
||||
|
|
@ -1,758 +0,0 @@
|
|||
// 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 PLAN9
|
||||
|
||||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <stdio.h>
|
||||
#undef nil
|
||||
#undef nelem
|
||||
#include "a.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);
|
||||
}
|
||||
|
||||
// bpathf is the same as bprintf (on windows it turns / into \ after the printf).
|
||||
// It returns a pointer to the NUL-terminated buffer contents.
|
||||
char*
|
||||
bpathf(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);
|
||||
}
|
||||
|
||||
// bwritef is like bprintf but does not reset the buffer
|
||||
// and does not return the NUL-terminated string.
|
||||
void
|
||||
bwritef(Buf *b, char *fmt, ...)
|
||||
{
|
||||
va_list arg;
|
||||
char buf[4096];
|
||||
|
||||
va_start(arg, fmt);
|
||||
vsnprintf(buf, sizeof buf, fmt, arg);
|
||||
va_end(arg);
|
||||
bwritestr(b, buf);
|
||||
}
|
||||
|
||||
// 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");
|
||||
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 != nil)
|
||||
bwritestr(b, p);
|
||||
}
|
||||
|
||||
static void genrun(Buf *b, char *dir, int mode, Vec *argv, int bg);
|
||||
|
||||
// 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)
|
||||
{
|
||||
genrun(b, dir, mode, argv, 1);
|
||||
}
|
||||
|
||||
// bgrunv is like run but runs the command in the background.
|
||||
// bgwait waits for pending bgrunv to finish.
|
||||
void
|
||||
bgrunv(char *dir, int mode, Vec *argv)
|
||||
{
|
||||
genrun(nil, dir, mode, argv, 0);
|
||||
}
|
||||
|
||||
#define MAXBG 4 /* maximum number of jobs to run at once */
|
||||
|
||||
static struct {
|
||||
int pid;
|
||||
int mode;
|
||||
char *cmd;
|
||||
Buf *b;
|
||||
} bg[MAXBG];
|
||||
static int nbg;
|
||||
static int maxnbg = nelem(bg);
|
||||
|
||||
static void bgwait1(void);
|
||||
|
||||
// genrun is the generic run implementation.
|
||||
static void
|
||||
genrun(Buf *b, char *dir, int mode, Vec *argv, int wait)
|
||||
{
|
||||
int i, p[2], pid;
|
||||
Buf b1, cmd;
|
||||
char *q;
|
||||
|
||||
while(nbg >= maxnbg)
|
||||
bgwait1();
|
||||
|
||||
binit(&b1);
|
||||
binit(&cmd);
|
||||
|
||||
if(!isabs(argv->p[0])) {
|
||||
bpathf(&b1, "/bin/%s", argv->p[0]);
|
||||
free(argv->p[0]);
|
||||
argv->p[0] = xstrdup(bstr(&b1));
|
||||
}
|
||||
|
||||
// Generate a copy of the command to show in a log.
|
||||
// Substitute $WORK for the work directory.
|
||||
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);
|
||||
}
|
||||
if(vflag > 1)
|
||||
errprintf("%s\n", bstr(&cmd));
|
||||
|
||||
if(b != nil) {
|
||||
breset(b);
|
||||
if(pipe(p) < 0)
|
||||
fatal("pipe");
|
||||
}
|
||||
|
||||
switch(pid = fork()) {
|
||||
case -1:
|
||||
fatal("fork");
|
||||
case 0:
|
||||
if(b != nil) {
|
||||
close(0);
|
||||
close(p[0]);
|
||||
dup(p[1], 1);
|
||||
dup(p[1], 2);
|
||||
if(p[1] > 2)
|
||||
close(p[1]);
|
||||
}
|
||||
if(dir != nil) {
|
||||
if(chdir(dir) < 0) {
|
||||
fprint(2, "chdir: %r\n");
|
||||
_exits("chdir");
|
||||
}
|
||||
}
|
||||
vadd(argv, nil);
|
||||
exec(argv->p[0], argv->p);
|
||||
fprint(2, "%s\n", bstr(&cmd));
|
||||
fprint(2, "exec: %r\n");
|
||||
_exits("exec");
|
||||
}
|
||||
if(b != nil) {
|
||||
close(p[1]);
|
||||
breadfrom(b, p[0]);
|
||||
close(p[0]);
|
||||
}
|
||||
|
||||
if(nbg < 0)
|
||||
fatal("bad bookkeeping");
|
||||
bg[nbg].pid = pid;
|
||||
bg[nbg].mode = mode;
|
||||
bg[nbg].cmd = btake(&cmd);
|
||||
bg[nbg].b = b;
|
||||
nbg++;
|
||||
|
||||
if(wait)
|
||||
bgwait();
|
||||
|
||||
bfree(&cmd);
|
||||
bfree(&b1);
|
||||
}
|
||||
|
||||
// bgwait1 waits for a single background job.
|
||||
static void
|
||||
bgwait1(void)
|
||||
{
|
||||
Waitmsg *w;
|
||||
int i, mode;
|
||||
char *cmd;
|
||||
Buf *b;
|
||||
|
||||
w = wait();
|
||||
if(w == nil)
|
||||
fatal("wait");
|
||||
|
||||
for(i=0; i<nbg; i++)
|
||||
if(bg[i].pid == w->pid)
|
||||
goto ok;
|
||||
fatal("wait: unexpected pid");
|
||||
|
||||
ok:
|
||||
cmd = bg[i].cmd;
|
||||
mode = bg[i].mode;
|
||||
bg[i].pid = 0;
|
||||
b = bg[i].b;
|
||||
bg[i].b = nil;
|
||||
bg[i] = bg[--nbg];
|
||||
|
||||
if(mode == CheckExit && w->msg[0]) {
|
||||
if(b != nil)
|
||||
xprintf("%s\n", bstr(b));
|
||||
fatal("FAILED: %s", cmd);
|
||||
}
|
||||
xfree(cmd);
|
||||
}
|
||||
|
||||
// bgwait waits for all the background jobs.
|
||||
void
|
||||
bgwait(void)
|
||||
{
|
||||
while(nbg > 0)
|
||||
bgwait1();
|
||||
}
|
||||
|
||||
// xgetwd replaces b with the current directory.
|
||||
void
|
||||
xgetwd(Buf *b)
|
||||
{
|
||||
char buf[4096];
|
||||
|
||||
breset(b);
|
||||
if(getwd(buf, sizeof buf) == nil)
|
||||
fatal("getwd");
|
||||
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)
|
||||
{
|
||||
char buf[4096];
|
||||
int fd;
|
||||
|
||||
fd = open(path, OREAD);
|
||||
if(fd2path(fd, buf, sizeof buf) < 0)
|
||||
fatal("fd2path");
|
||||
close(fd);
|
||||
breset(b);
|
||||
bwritestr(b, buf);
|
||||
}
|
||||
|
||||
// isdir reports whether p names an existing directory.
|
||||
bool
|
||||
isdir(char *p)
|
||||
{
|
||||
Dir *d;
|
||||
ulong mode;
|
||||
|
||||
d = dirstat(p);
|
||||
if(d == nil)
|
||||
return 0;
|
||||
mode = d->mode;
|
||||
free(d);
|
||||
return (mode & DMDIR) == DMDIR;
|
||||
}
|
||||
|
||||
// isfile reports whether p names an existing file.
|
||||
bool
|
||||
isfile(char *p)
|
||||
{
|
||||
Dir *d;
|
||||
ulong mode;
|
||||
|
||||
d = dirstat(p);
|
||||
if(d == nil)
|
||||
return 0;
|
||||
mode = d->mode;
|
||||
free(d);
|
||||
return (mode & DMDIR) == 0;
|
||||
}
|
||||
|
||||
// mtime returns the modification time of the file p.
|
||||
Time
|
||||
mtime(char *p)
|
||||
{
|
||||
Dir *d;
|
||||
ulong t;
|
||||
|
||||
d = dirstat(p);
|
||||
if(d == nil)
|
||||
return 0;
|
||||
t = d->mtime;
|
||||
free(d);
|
||||
return (Time)t;
|
||||
}
|
||||
|
||||
// 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, OREAD);
|
||||
if(fd < 0)
|
||||
fatal("open %s", file);
|
||||
breadfrom(b, fd);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
// writefile writes b to the named file, creating it if needed.
|
||||
void
|
||||
writefile(Buf *b, char *file, int exec)
|
||||
{
|
||||
int fd;
|
||||
Dir d;
|
||||
|
||||
fd = create(file, ORDWR, 0666);
|
||||
if(fd < 0)
|
||||
fatal("create %s", file);
|
||||
if(write(fd, b->p, b->len) != b->len)
|
||||
fatal("short write");
|
||||
if(exec) {
|
||||
nulldir(&d);
|
||||
d.mode = 0755;
|
||||
dirfwstat(fd, &d);
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
|
||||
// xmkdir creates the directory p.
|
||||
void
|
||||
xmkdir(char *p)
|
||||
{
|
||||
int fd;
|
||||
|
||||
if(isdir(p))
|
||||
return;
|
||||
fd = create(p, OREAD, 0777|DMDIR);
|
||||
close(fd);
|
||||
if(fd < 0)
|
||||
fatal("mkdir %s", p);
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
if(vflag > 2)
|
||||
errprintf("rm %s\n", p);
|
||||
remove(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));
|
||||
}
|
||||
}
|
||||
if(vflag > 2)
|
||||
errprintf("rm %s\n", p);
|
||||
remove(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;
|
||||
int fd, i, n;
|
||||
|
||||
vreset(dst);
|
||||
|
||||
fd = open(dir, OREAD);
|
||||
if(fd < 0)
|
||||
fatal("open %s", dir);
|
||||
n = dirreadall(fd, &d);
|
||||
for(i=0; i<n; i++)
|
||||
vadd(dst, d[i].name);
|
||||
free(d);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
// xworkdir creates a new temporary directory to hold object files
|
||||
// and returns the name of that directory.
|
||||
char*
|
||||
xworkdir(void)
|
||||
{
|
||||
Buf b;
|
||||
char *p;
|
||||
int fd, tries;
|
||||
|
||||
binit(&b);
|
||||
|
||||
fd = 0;
|
||||
for(tries=0; tries<1000; tries++) {
|
||||
bprintf(&b, "/tmp/go-cbuild-%06x", nrand((1<<24)-1));
|
||||
fd = create(bstr(&b), OREAD|OEXCL, 0700|DMDIR);
|
||||
if(fd >= 0)
|
||||
goto done;
|
||||
}
|
||||
fatal("xworkdir create");
|
||||
|
||||
done:
|
||||
close(fd);
|
||||
p = btake(&b);
|
||||
|
||||
bfree(&b);
|
||||
return p;
|
||||
}
|
||||
|
||||
// fatal prints an error message to standard error and exits.
|
||||
void
|
||||
fatal(char *msg, ...)
|
||||
{
|
||||
va_list arg;
|
||||
|
||||
fflush(stdout);
|
||||
fprintf(stderr, "go tool dist: ");
|
||||
va_start(arg, msg);
|
||||
vfprintf(stderr, msg, arg);
|
||||
va_end(arg);
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
bgwait();
|
||||
exits(msg);
|
||||
}
|
||||
|
||||
// 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 && streq(p+np-ns, suffix);
|
||||
}
|
||||
|
||||
// hasprefix reports whether p begins with 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)
|
||||
{
|
||||
char buf[32];
|
||||
|
||||
snprintf(buf, sizeof buf, "%d", n);
|
||||
exits(buf);
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// errprintf prints a message to standard output.
|
||||
void
|
||||
errprintf(char *fmt, ...)
|
||||
{
|
||||
va_list arg;
|
||||
|
||||
va_start(arg, fmt);
|
||||
vfprintf(stderr, fmt, arg);
|
||||
va_end(arg);
|
||||
}
|
||||
|
||||
// xsetenv sets the environment variable $name to the given value.
|
||||
void
|
||||
xsetenv(char *name, char *value)
|
||||
{
|
||||
putenv(name, value);
|
||||
}
|
||||
|
||||
// main takes care of OS-specific startup and dispatches to xmain.
|
||||
void
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
Buf b;
|
||||
|
||||
setvbuf(stdout, nil, _IOLBF, BUFSIZ);
|
||||
setvbuf(stderr, nil, _IOLBF, BUFSIZ);
|
||||
|
||||
binit(&b);
|
||||
|
||||
rfork(RFENVG);
|
||||
|
||||
slash = "/";
|
||||
gohostos = "plan9";
|
||||
|
||||
xgetenv(&b, "objtype");
|
||||
if(b.len == 0)
|
||||
fatal("$objtype is unset");
|
||||
gohostarch = btake(&b);
|
||||
|
||||
srand(time(0)+getpid());
|
||||
init();
|
||||
xmain(argc, argv);
|
||||
|
||||
bfree(&b);
|
||||
exits(nil);
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// xsamefile reports whether f1 and f2 are the same file (or dir)
|
||||
int
|
||||
xsamefile(char *f1, char *f2)
|
||||
{
|
||||
return streq(f1, f2); // suffice for now
|
||||
}
|
||||
|
||||
// xtryexecfunc tries to execute function f, if any illegal instruction
|
||||
// signal received in the course of executing that function, it will
|
||||
// return 0, otherwise it will return 1.
|
||||
int
|
||||
xtryexecfunc(void (*f)(void))
|
||||
{
|
||||
USED(f);
|
||||
return 0; // suffice for now
|
||||
}
|
||||
|
||||
bool
|
||||
cansse2(void)
|
||||
{
|
||||
// if we had access to cpuid, could answer this question
|
||||
// less conservatively.
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif // PLAN9
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
// Copyright 2015 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.
|
||||
|
||||
// +build !windows
|
||||
|
||||
package main
|
||||
|
||||
func sysinit() {
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
modkernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||
procGetSystemInfo = modkernel32.NewProc("GetSystemInfo")
|
||||
)
|
||||
|
||||
// see http://msdn.microsoft.com/en-us/library/windows/desktop/ms724958(v=vs.85).aspx
|
||||
type systeminfo struct {
|
||||
wProcessorArchitecture uint16
|
||||
wReserved uint16
|
||||
dwPageSize uint32
|
||||
lpMinimumApplicationAddress uintptr
|
||||
lpMaximumApplicationAddress uintptr
|
||||
dwActiveProcessorMask uintptr
|
||||
dwNumberOfProcessors uint32
|
||||
dwProcessorType uint32
|
||||
dwAllocationGranularity uint32
|
||||
wProcessorLevel uint16
|
||||
wProcessorRevision uint16
|
||||
}
|
||||
|
||||
const (
|
||||
PROCESSOR_ARCHITECTURE_AMD64 = 9
|
||||
PROCESSOR_ARCHITECTURE_INTEL = 0
|
||||
)
|
||||
|
||||
var sysinfo systeminfo
|
||||
|
||||
func sysinit() {
|
||||
syscall.Syscall(procGetSystemInfo.Addr(), 1, uintptr(unsafe.Pointer(&sysinfo)), 0, 0)
|
||||
switch sysinfo.wProcessorArchitecture {
|
||||
case PROCESSOR_ARCHITECTURE_AMD64:
|
||||
gohostarch = "amd64"
|
||||
case PROCESSOR_ARCHITECTURE_INTEL:
|
||||
gohostarch = "386"
|
||||
default:
|
||||
fatal("unknown processor architecture")
|
||||
}
|
||||
}
|
||||
|
|
@ -1,847 +0,0 @@
|
|||
// 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>
|
||||
#include <setjmp.h>
|
||||
#include <signal.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);
|
||||
}
|
||||
|
||||
// bpathf is the same as bprintf (on windows it turns / into \ after the printf).
|
||||
// It returns a pointer to the NUL-terminated buffer contents.
|
||||
char*
|
||||
bpathf(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);
|
||||
}
|
||||
|
||||
// bwritef is like bprintf but does not reset the buffer
|
||||
// and does not return the NUL-terminated string.
|
||||
void
|
||||
bwritef(Buf *b, char *fmt, ...)
|
||||
{
|
||||
va_list arg;
|
||||
char buf[4096];
|
||||
|
||||
va_start(arg, fmt);
|
||||
vsnprintf(buf, sizeof buf, fmt, arg);
|
||||
va_end(arg);
|
||||
bwritestr(b, buf);
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
static void genrun(Buf *b, char *dir, int mode, Vec *argv, int bg);
|
||||
|
||||
// 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)
|
||||
{
|
||||
genrun(b, dir, mode, argv, 1);
|
||||
}
|
||||
|
||||
// bgrunv is like run but runs the command in the background.
|
||||
// bgwait waits for pending bgrunv to finish.
|
||||
void
|
||||
bgrunv(char *dir, int mode, Vec *argv)
|
||||
{
|
||||
genrun(nil, dir, mode, argv, 0);
|
||||
}
|
||||
|
||||
#define MAXBG 4 /* maximum number of jobs to run at once */
|
||||
|
||||
static struct {
|
||||
int pid;
|
||||
int mode;
|
||||
char *cmd;
|
||||
Buf *b;
|
||||
} bg[MAXBG];
|
||||
static int nbg;
|
||||
static int maxnbg = nelem(bg);
|
||||
|
||||
static void bgwait1(void);
|
||||
|
||||
// genrun is the generic run implementation.
|
||||
static void
|
||||
genrun(Buf *b, char *dir, int mode, Vec *argv, int wait)
|
||||
{
|
||||
int i, p[2], pid;
|
||||
Buf cmd;
|
||||
char *q;
|
||||
|
||||
while(nbg >= maxnbg)
|
||||
bgwait1();
|
||||
|
||||
// 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);
|
||||
}
|
||||
if(vflag > 1)
|
||||
errprintf("%s\n", bstr(&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, "%s\n", bstr(&cmd));
|
||||
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]);
|
||||
}
|
||||
|
||||
if(nbg < 0)
|
||||
fatal("bad bookkeeping");
|
||||
bg[nbg].pid = pid;
|
||||
bg[nbg].mode = mode;
|
||||
bg[nbg].cmd = btake(&cmd);
|
||||
bg[nbg].b = b;
|
||||
nbg++;
|
||||
|
||||
if(wait)
|
||||
bgwait();
|
||||
|
||||
bfree(&cmd);
|
||||
}
|
||||
|
||||
// bgwait1 waits for a single background job.
|
||||
static void
|
||||
bgwait1(void)
|
||||
{
|
||||
int i, pid, status, mode;
|
||||
char *cmd;
|
||||
Buf *b;
|
||||
|
||||
errno = 0;
|
||||
while((pid = wait(&status)) < 0) {
|
||||
if(errno != EINTR)
|
||||
fatal("waitpid: %s", strerror(errno));
|
||||
}
|
||||
for(i=0; i<nbg; i++)
|
||||
if(bg[i].pid == pid)
|
||||
goto ok;
|
||||
fatal("waitpid: unexpected pid");
|
||||
|
||||
ok:
|
||||
cmd = bg[i].cmd;
|
||||
mode = bg[i].mode;
|
||||
bg[i].pid = 0;
|
||||
b = bg[i].b;
|
||||
bg[i].b = nil;
|
||||
bg[i] = bg[--nbg];
|
||||
|
||||
if(mode == CheckExit && (!WIFEXITED(status) || WEXITSTATUS(status) != 0)) {
|
||||
if(b != nil)
|
||||
xprintf("%s\n", bstr(b));
|
||||
fatal("FAILED: %s", cmd);
|
||||
}
|
||||
xfree(cmd);
|
||||
}
|
||||
|
||||
// bgwait waits for all the background jobs.
|
||||
void
|
||||
bgwait(void)
|
||||
{
|
||||
while(nbg > 0)
|
||||
bgwait1();
|
||||
}
|
||||
|
||||
// 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. if
|
||||
// exec is non-zero, marks the file as executable.
|
||||
void
|
||||
writefile(Buf *b, char *file, int exec)
|
||||
{
|
||||
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));
|
||||
if(exec)
|
||||
fchmod(fd, 0755);
|
||||
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)
|
||||
{
|
||||
if(vflag > 2)
|
||||
errprintf("rm %s\n", 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));
|
||||
}
|
||||
if(vflag > 2)
|
||||
errprintf("rm %s\n", p);
|
||||
rmdir(p);
|
||||
} else {
|
||||
if(vflag > 2)
|
||||
errprintf("rm %s\n", p);
|
||||
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(streq(dp->d_name, ".") || streq(dp->d_name, ".."))
|
||||
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");
|
||||
if(b.p[b.len-1] != '/')
|
||||
bwrite(&b, "/", 1);
|
||||
bwritestr(&b, "go-cbuild-XXXXXX");
|
||||
p = bstr(&b);
|
||||
if(mkdtemp(p) == nil)
|
||||
fatal("mkdtemp(%s): %s", p, 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;
|
||||
|
||||
fflush(stdout);
|
||||
fprintf(stderr, "go tool dist: ");
|
||||
va_start(arg, msg);
|
||||
vfprintf(stderr, msg, arg);
|
||||
va_end(arg);
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
bgwait();
|
||||
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 && streq(p+np-ns, suffix);
|
||||
}
|
||||
|
||||
// hasprefix reports whether p begins with 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);
|
||||
}
|
||||
|
||||
// errprintf prints a message to standard output.
|
||||
void
|
||||
errprintf(char *fmt, ...)
|
||||
{
|
||||
va_list arg;
|
||||
|
||||
va_start(arg, fmt);
|
||||
vfprintf(stderr, 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)
|
||||
{
|
||||
Buf b;
|
||||
int osx;
|
||||
struct utsname u;
|
||||
|
||||
setvbuf(stdout, nil, _IOLBF, 0);
|
||||
setvbuf(stderr, nil, _IOLBF, 0);
|
||||
|
||||
setenv("TERM", "dumb", 1); // disable escape codes in clang errors
|
||||
|
||||
binit(&b);
|
||||
|
||||
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";
|
||||
#elif defined(__DragonFly__)
|
||||
gohostos = "dragonfly";
|
||||
#elif defined(__FreeBSD__)
|
||||
gohostos = "freebsd";
|
||||
#elif defined(__FreeBSD_kernel__)
|
||||
// detect debian/kFreeBSD.
|
||||
// http://wiki.debian.org/Debian_GNU/kFreeBSD_FAQ#Q._How_do_I_detect_kfreebsd_with_preprocessor_directives_in_a_C_program.3F
|
||||
gohostos = "freebsd";
|
||||
#elif defined(__OpenBSD__)
|
||||
gohostos = "openbsd";
|
||||
#elif defined(__NetBSD__)
|
||||
gohostos = "netbsd";
|
||||
#elif defined(__sun) && defined(__SVR4)
|
||||
gohostos = "solaris";
|
||||
// Even on 64-bit platform, solaris uname -m prints i86pc.
|
||||
run(&b, nil, 0, "isainfo", "-n", nil);
|
||||
if(contains(bstr(&b), "amd64"))
|
||||
gohostarch = "amd64";
|
||||
if(contains(bstr(&b), "i386"))
|
||||
gohostarch = "386";
|
||||
#else
|
||||
fatal("unknown operating system");
|
||||
#endif
|
||||
|
||||
if(gohostarch == nil) {
|
||||
if(uname(&u) < 0)
|
||||
fatal("uname: %s", strerror(errno));
|
||||
if(contains(u.machine, "x86_64") || contains(u.machine, "amd64"))
|
||||
gohostarch = "amd64";
|
||||
else if(hassuffix(u.machine, "86"))
|
||||
gohostarch = "386";
|
||||
else if(contains(u.machine, "arm"))
|
||||
gohostarch = "arm";
|
||||
else if(contains(u.machine, "ppc64le"))
|
||||
gohostarch = "ppc64le";
|
||||
else if(contains(u.machine, "ppc64"))
|
||||
gohostarch = "ppc64";
|
||||
else
|
||||
fatal("unknown architecture: %s", u.machine);
|
||||
}
|
||||
|
||||
if(streq(gohostarch, "arm"))
|
||||
maxnbg = 1;
|
||||
|
||||
// The OS X 10.6 linker does not support external linking mode.
|
||||
// See golang.org/issue/5130.
|
||||
//
|
||||
// OS X 10.6 does not work with clang either, but OS X 10.9 requires it.
|
||||
// It seems to work with OS X 10.8, so we default to clang for 10.8 and later.
|
||||
// See golang.org/issue/5822.
|
||||
//
|
||||
// Roughly, OS X 10.N shows up as uname release (N+4),
|
||||
// so OS X 10.6 is uname version 10 and OS X 10.8 is uname version 12.
|
||||
if(streq(gohostos, "darwin")) {
|
||||
if(uname(&u) < 0)
|
||||
fatal("uname: %s", strerror(errno));
|
||||
osx = atoi(u.release) - 4;
|
||||
if(osx <= 6)
|
||||
goextlinkenabled = "0";
|
||||
if(osx >= 8)
|
||||
defaultclang = 1;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// xsamefile reports whether f1 and f2 are the same file (or dir)
|
||||
int
|
||||
xsamefile(char *f1, char *f2)
|
||||
{
|
||||
return streq(f1, f2); // suffice for now
|
||||
}
|
||||
|
||||
sigjmp_buf sigill_jmpbuf;
|
||||
static void sigillhand(int);
|
||||
|
||||
// xtryexecfunc tries to execute function f, if any illegal instruction
|
||||
// signal received in the course of executing that function, it will
|
||||
// return 0, otherwise it will return 1.
|
||||
// Some systems (notably NetBSD) will spin and spin when executing VFPv3
|
||||
// instructions on VFPv2 system (e.g. Raspberry Pi) without ever triggering
|
||||
// SIGILL, so we set a 1-second alarm to catch that case.
|
||||
int
|
||||
xtryexecfunc(void (*f)(void))
|
||||
{
|
||||
int r;
|
||||
r = 0;
|
||||
signal(SIGILL, sigillhand);
|
||||
signal(SIGALRM, sigillhand);
|
||||
alarm(1);
|
||||
if(sigsetjmp(sigill_jmpbuf, 1) == 0) {
|
||||
f();
|
||||
r = 1;
|
||||
}
|
||||
signal(SIGILL, SIG_DFL);
|
||||
alarm(0);
|
||||
signal(SIGALRM, SIG_DFL);
|
||||
return r;
|
||||
}
|
||||
|
||||
// SIGILL handler helper
|
||||
static void
|
||||
sigillhand(int signum)
|
||||
{
|
||||
USED(signum);
|
||||
siglongjmp(sigill_jmpbuf, 1);
|
||||
}
|
||||
|
||||
static void
|
||||
__cpuid(int dst[4], int ax)
|
||||
{
|
||||
#ifdef __i386__
|
||||
// we need to avoid ebx on i386 (esp. when -fPIC).
|
||||
asm volatile(
|
||||
"mov %%ebx, %%edi\n\t"
|
||||
"cpuid\n\t"
|
||||
"xchgl %%ebx, %%edi"
|
||||
: "=a" (dst[0]), "=D" (dst[1]), "=c" (dst[2]), "=d" (dst[3])
|
||||
: "0" (ax));
|
||||
#elif defined(__x86_64__)
|
||||
asm volatile("cpuid"
|
||||
: "=a" (dst[0]), "=b" (dst[1]), "=c" (dst[2]), "=d" (dst[3])
|
||||
: "0" (ax));
|
||||
#else
|
||||
dst[0] = dst[1] = dst[2] = dst[3] = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
cansse2(void)
|
||||
{
|
||||
int info[4];
|
||||
|
||||
__cpuid(info, 1);
|
||||
return (info[3] & (1<<26)) != 0; // SSE2
|
||||
}
|
||||
|
||||
#endif // PLAN9
|
||||
#endif // __WINDOWS__
|
||||
|
|
@ -0,0 +1,457 @@
|
|||
// 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.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
// pathf is fmt.Sprintf for generating paths
|
||||
// (on windows it turns / into \ after the printf).
|
||||
func pathf(format string, args ...interface{}) string {
|
||||
return filepath.Clean(fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
// filter returns a slice containing the elements x from list for which f(x) == true.
|
||||
func filter(list []string, f func(string) bool) []string {
|
||||
var out []string
|
||||
for _, x := range list {
|
||||
if f(x) {
|
||||
out = append(out, x)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// uniq returns a sorted slice containing the unique elements of list.
|
||||
func uniq(list []string) []string {
|
||||
out := make([]string, len(list))
|
||||
copy(out, list)
|
||||
sort.Strings(out)
|
||||
keep := out[:0]
|
||||
for _, x := range out {
|
||||
if len(keep) == 0 || keep[len(keep)-1] != x {
|
||||
keep = append(keep, x)
|
||||
}
|
||||
}
|
||||
return keep
|
||||
}
|
||||
|
||||
// splitlines returns a slice with the result of splitting
|
||||
// the input p after each \n.
|
||||
func splitlines(p string) []string {
|
||||
return strings.SplitAfter(p, "\n")
|
||||
}
|
||||
|
||||
// splitfields replaces the vector v with the result of splitting
|
||||
// the input p into non-empty fields containing no spaces.
|
||||
func splitfields(p string) []string {
|
||||
return strings.Fields(p)
|
||||
}
|
||||
|
||||
const (
|
||||
CheckExit = 1 << iota
|
||||
ShowOutput
|
||||
Background
|
||||
)
|
||||
|
||||
var outputLock sync.Mutex
|
||||
|
||||
// run runs the command line cmd in dir.
|
||||
// If mode has ShowOutput set, run collects cmd's output and returns it as a string;
|
||||
// otherwise, run prints cmd's output to standard output after the command finishes.
|
||||
// If mode has CheckExit set and the command fails, run calls fatal.
|
||||
// If mode has Background set, this command is being run as a
|
||||
// Background job. Only bgrun should use the Background mode,
|
||||
// not other callers.
|
||||
func run(dir string, mode int, cmd ...string) string {
|
||||
if vflag > 1 {
|
||||
errprintf("run: %s\n", strings.Join(cmd, " "))
|
||||
}
|
||||
|
||||
xcmd := exec.Command(cmd[0], cmd[1:]...)
|
||||
xcmd.Dir = dir
|
||||
var err error
|
||||
data, err := xcmd.CombinedOutput()
|
||||
if err != nil && mode&CheckExit != 0 {
|
||||
outputLock.Lock()
|
||||
if len(data) > 0 {
|
||||
xprintf("%s\n", data)
|
||||
}
|
||||
outputLock.Unlock()
|
||||
atomic.AddInt32(&ndone, +1)
|
||||
die := func() {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
fatal("FAILED: %v", strings.Join(cmd, " "))
|
||||
}
|
||||
if mode&Background != 0 {
|
||||
// This is a background run, and fatal will
|
||||
// wait for it to finish before exiting.
|
||||
// If we call fatal directly, that's a deadlock.
|
||||
// Instead, call fatal in a background goroutine
|
||||
// and let this run return normally, so that
|
||||
// fatal can wait for it to finish.
|
||||
go die()
|
||||
} else {
|
||||
die()
|
||||
}
|
||||
}
|
||||
if mode&ShowOutput != 0 {
|
||||
os.Stdout.Write(data)
|
||||
}
|
||||
return string(data)
|
||||
}
|
||||
|
||||
var maxbg = 4 /* maximum number of jobs to run at once */
|
||||
|
||||
var (
|
||||
bgwork = make(chan func())
|
||||
bgdone = make(chan struct{}, 1e6)
|
||||
nwork int32
|
||||
ndone int32
|
||||
)
|
||||
|
||||
func bginit() {
|
||||
for i := 0; i < maxbg; i++ {
|
||||
go bghelper()
|
||||
}
|
||||
}
|
||||
|
||||
func bghelper() {
|
||||
for {
|
||||
(<-bgwork)()
|
||||
}
|
||||
}
|
||||
|
||||
// bgrun is like run but runs the command in the background.
|
||||
// CheckExit|ShowOutput mode is implied (since output cannot be returned).
|
||||
func bgrun(dir string, cmd ...string) {
|
||||
bgwork <- func() {
|
||||
run(dir, CheckExit|ShowOutput|Background, cmd...)
|
||||
}
|
||||
}
|
||||
|
||||
// bgwait waits for pending bgruns to finish.
|
||||
func bgwait() {
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(maxbg)
|
||||
for i := 0; i < maxbg; i++ {
|
||||
bgwork <- func() {
|
||||
wg.Done()
|
||||
wg.Wait()
|
||||
}
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
// xgetwd returns the current directory.
|
||||
func xgetwd() string {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
fatal("%s", err)
|
||||
}
|
||||
return wd
|
||||
}
|
||||
|
||||
// xrealwd returns the 'real' name for the given path.
|
||||
// real is defined as what xgetwd returns in that directory.
|
||||
func xrealwd(path string) string {
|
||||
old := xgetwd()
|
||||
if err := os.Chdir(path); err != nil {
|
||||
fatal("chdir %s: %v", path, err)
|
||||
}
|
||||
real := xgetwd()
|
||||
if err := os.Chdir(old); err != nil {
|
||||
fatal("chdir %s: %v", old, err)
|
||||
}
|
||||
return real
|
||||
}
|
||||
|
||||
// isdir reports whether p names an existing directory.
|
||||
func isdir(p string) bool {
|
||||
fi, err := os.Stat(p)
|
||||
return err == nil && fi.IsDir()
|
||||
}
|
||||
|
||||
// isfile reports whether p names an existing file.
|
||||
func isfile(p string) bool {
|
||||
fi, err := os.Stat(p)
|
||||
return err == nil && fi.Mode().IsRegular()
|
||||
}
|
||||
|
||||
// mtime returns the modification time of the file p.
|
||||
func mtime(p string) time.Time {
|
||||
fi, err := os.Stat(p)
|
||||
if err != nil {
|
||||
return time.Time{}
|
||||
}
|
||||
return fi.ModTime()
|
||||
}
|
||||
|
||||
// isabs reports whether p is an absolute path.
|
||||
func isabs(p string) bool {
|
||||
return filepath.IsAbs(p)
|
||||
}
|
||||
|
||||
// readfile returns the content of the named file.
|
||||
func readfile(file string) string {
|
||||
data, err := ioutil.ReadFile(file)
|
||||
if err != nil {
|
||||
fatal("%v", err)
|
||||
}
|
||||
return string(data)
|
||||
}
|
||||
|
||||
// writefile writes b to the named file, creating it if needed. if
|
||||
// exec is non-zero, marks the file as executable.
|
||||
func writefile(b, file string, exec int) {
|
||||
mode := os.FileMode(0666)
|
||||
if exec != 0 {
|
||||
mode = 0777
|
||||
}
|
||||
err := ioutil.WriteFile(file, []byte(b), mode)
|
||||
if err != nil {
|
||||
fatal("%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// xmkdir creates the directory p.
|
||||
func xmkdir(p string) {
|
||||
err := os.Mkdir(p, 0777)
|
||||
if err != nil {
|
||||
fatal("%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// xmkdirall creates the directory p and its parents, as needed.
|
||||
func xmkdirall(p string) {
|
||||
err := os.MkdirAll(p, 0777)
|
||||
if err != nil {
|
||||
fatal("%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// xremove removes the file p.
|
||||
func xremove(p string) {
|
||||
if vflag > 2 {
|
||||
errprintf("rm %s\n", p)
|
||||
}
|
||||
os.Remove(p)
|
||||
}
|
||||
|
||||
// xremoveall removes the file or directory tree rooted at p.
|
||||
func xremoveall(p string) {
|
||||
if vflag > 2 {
|
||||
errprintf("rm -r %s\n", p)
|
||||
}
|
||||
os.RemoveAll(p)
|
||||
}
|
||||
|
||||
// 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.
|
||||
func xreaddir(dir string) []string {
|
||||
f, err := os.Open(dir)
|
||||
if err != nil {
|
||||
fatal("%v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
names, err := f.Readdirnames(-1)
|
||||
if err != nil {
|
||||
fatal("reading %s: %v", dir, err)
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
// xworkdir creates a new temporary directory to hold object files
|
||||
// and returns the name of that directory.
|
||||
func xworkdir() string {
|
||||
name, err := ioutil.TempDir("", "go-tool-dist-")
|
||||
if err != nil {
|
||||
fatal("%v", err)
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
// fatal prints an error message to standard error and exits.
|
||||
func fatal(format string, args ...interface{}) {
|
||||
fmt.Fprintf(os.Stderr, "go tool dist: %s\n", fmt.Sprintf(format, args...))
|
||||
bgwait()
|
||||
xexit(2)
|
||||
}
|
||||
|
||||
var atexits []func()
|
||||
|
||||
// xexit exits the process with return code n.
|
||||
func xexit(n int) {
|
||||
for i := len(atexits) - 1; i >= 0; i-- {
|
||||
atexits[i]()
|
||||
}
|
||||
os.Exit(n)
|
||||
}
|
||||
|
||||
// xatexit schedules the exit-handler f to be run when the program exits.
|
||||
func xatexit(f func()) {
|
||||
atexits = append(atexits, f)
|
||||
}
|
||||
|
||||
// xprintf prints a message to standard output.
|
||||
func xprintf(format string, args ...interface{}) {
|
||||
fmt.Printf(format, args...)
|
||||
}
|
||||
|
||||
// errprintf prints a message to standard output.
|
||||
func errprintf(format string, args ...interface{}) {
|
||||
fmt.Fprintf(os.Stderr, format, args...)
|
||||
}
|
||||
|
||||
// main takes care of OS-specific startup and dispatches to xmain.
|
||||
func main() {
|
||||
os.Setenv("TERM", "dumb") // disable escape codes in clang errors
|
||||
|
||||
slash = string(filepath.Separator)
|
||||
|
||||
gohostos = runtime.GOOS
|
||||
switch gohostos {
|
||||
case "darwin":
|
||||
// Even on 64-bit platform, darwin uname -m prints i386.
|
||||
if strings.Contains(run("", CheckExit, "sysctl", "machdep.cpu.extfeatures"), "EM64T") {
|
||||
gohostarch = "amd64"
|
||||
}
|
||||
case "solaris":
|
||||
// Even on 64-bit platform, solaris uname -m prints i86pc.
|
||||
out := run("", CheckExit, "isainfo", "-n")
|
||||
if strings.Contains(out, "amd64") {
|
||||
gohostarch = "amd64"
|
||||
}
|
||||
if strings.Contains(out, "i386") {
|
||||
gohostarch = "386"
|
||||
}
|
||||
case "plan9":
|
||||
gohostarch = os.Getenv("objtype")
|
||||
if gohostarch == "" {
|
||||
fatal("$objtype is unset")
|
||||
}
|
||||
}
|
||||
|
||||
sysinit()
|
||||
|
||||
if gohostarch == "" {
|
||||
// Default Unix system.
|
||||
out := run("", CheckExit, "uname", "-m")
|
||||
switch {
|
||||
case strings.Contains(out, "x86_64"), strings.Contains(out, "amd64"):
|
||||
gohostarch = "amd64"
|
||||
case strings.Contains(out, "86"):
|
||||
gohostarch = "386"
|
||||
case strings.Contains(out, "arm"):
|
||||
gohostarch = "arm"
|
||||
case strings.Contains(out, "ppc64le"):
|
||||
gohostarch = "ppc64le"
|
||||
case strings.Contains(out, "ppc64"):
|
||||
gohostarch = "ppc64"
|
||||
default:
|
||||
fatal("unknown architecture: %s", out)
|
||||
}
|
||||
}
|
||||
|
||||
if gohostarch == "arm" {
|
||||
maxbg = 1
|
||||
}
|
||||
bginit()
|
||||
|
||||
// The OS X 10.6 linker does not support external linking mode.
|
||||
// See golang.org/issue/5130.
|
||||
//
|
||||
// OS X 10.6 does not work with clang either, but OS X 10.9 requires it.
|
||||
// It seems to work with OS X 10.8, so we default to clang for 10.8 and later.
|
||||
// See golang.org/issue/5822.
|
||||
//
|
||||
// Roughly, OS X 10.N shows up as uname release (N+4),
|
||||
// so OS X 10.6 is uname version 10 and OS X 10.8 is uname version 12.
|
||||
if gohostos == "darwin" {
|
||||
rel := run("", CheckExit, "uname", "-r")
|
||||
if i := strings.Index(rel, "."); i >= 0 {
|
||||
rel = rel[:i]
|
||||
}
|
||||
osx, _ := strconv.Atoi(rel)
|
||||
if osx <= 6+4 {
|
||||
goextlinkenabled = "0"
|
||||
}
|
||||
if osx >= 8+4 {
|
||||
defaultclang = true
|
||||
}
|
||||
}
|
||||
|
||||
xinit()
|
||||
xmain()
|
||||
}
|
||||
|
||||
// xsamefile reports whether f1 and f2 are the same file (or dir)
|
||||
func xsamefile(f1, f2 string) bool {
|
||||
fi1, err1 := os.Stat(f1)
|
||||
fi2, err2 := os.Stat(f2)
|
||||
if err1 != nil || err2 != nil {
|
||||
return f1 == f2
|
||||
}
|
||||
return os.SameFile(fi1, fi2)
|
||||
}
|
||||
|
||||
func cpuid(info *[4]uint32, ax uint32)
|
||||
|
||||
func cansse2() bool {
|
||||
if gohostarch != "386" && gohostarch != "amd64" {
|
||||
return false
|
||||
}
|
||||
|
||||
var info [4]uint32
|
||||
cpuid(&info, 1)
|
||||
return info[3]&(1<<26) != 0 // SSE2
|
||||
}
|
||||
|
||||
func xgetgoarm() string {
|
||||
if goos == "nacl" {
|
||||
// NaCl guarantees VFPv3 and is always cross-compiled.
|
||||
return "7"
|
||||
}
|
||||
if gohostarch != "arm" || goos != gohostos {
|
||||
// Conservative default for cross-compilation.
|
||||
return "5"
|
||||
}
|
||||
if goos == "freebsd" {
|
||||
// FreeBSD has broken VFP support.
|
||||
return "5"
|
||||
}
|
||||
if xtryexecfunc(useVFPv3) {
|
||||
return "7"
|
||||
}
|
||||
if xtryexecfunc(useVFPv1) {
|
||||
return "6"
|
||||
}
|
||||
return "5"
|
||||
}
|
||||
|
||||
func xtryexecfunc(f func()) bool {
|
||||
// TODO(rsc): Implement.
|
||||
// The C cmd/dist used this to test whether certain assembly
|
||||
// sequences could be executed properly. It used signals and
|
||||
// timers and sigsetjmp, which is basically not possible in Go.
|
||||
// We probably have to invoke ourselves as a subprocess instead,
|
||||
// to contain the fault/timeout.
|
||||
return false
|
||||
}
|
||||
|
||||
func useVFPv1()
|
||||
func useVFPv3()
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
// Copyright 2015 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 "textflag.h"
|
||||
|
||||
// try to run "vmov.f64 d0, d0" instruction
|
||||
TEXT ·useVFPv1(SB),NOSPLIT,$0
|
||||
WORD $0xeeb00b40 // vomv.f64 d0, d0
|
||||
RET
|
||||
|
||||
// try to run VFPv3-only "vmov.f64 d0, #112" instruction
|
||||
TEXT ·useVFPv3(SB),NOSPLIT,$0
|
||||
WORD $0xeeb70b00 // vmov.f64 d0, #112
|
||||
RET
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
// Copyright 2015 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.
|
||||
|
||||
// +build !arm
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
TEXT ·useVFPv1(SB),NOSPLIT,$0
|
||||
RET
|
||||
|
||||
TEXT ·useVFPv3(SB),NOSPLIT,$0
|
||||
RET
|
||||
|
||||
|
|
@ -1,989 +0,0 @@
|
|||
// 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)
|
||||
{
|
||||
Rune *r, *w;
|
||||
|
||||
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|FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
void
|
||||
bwritef(Buf *b, char *fmt, ...)
|
||||
{
|
||||
va_list arg;
|
||||
char buf[4096];
|
||||
|
||||
// no reset
|
||||
va_start(arg, fmt);
|
||||
vsnprintf(buf, sizeof buf, fmt, arg);
|
||||
va_end(arg);
|
||||
bwritestr(b, buf);
|
||||
}
|
||||
|
||||
// bpathf is like bprintf but replaces / with \ in the result,
|
||||
// to make it a canonical windows file path.
|
||||
char*
|
||||
bpathf(Buf *b, char *fmt, ...)
|
||||
{
|
||||
int i;
|
||||
va_list arg;
|
||||
char buf[4096];
|
||||
|
||||
breset(b);
|
||||
va_start(arg, fmt);
|
||||
vsnprintf(buf, sizeof buf, fmt, arg);
|
||||
va_end(arg);
|
||||
bwritestr(b, buf);
|
||||
|
||||
for(i=0; i<b->len; i++)
|
||||
if(b->p[i] == '/')
|
||||
b->p[i] = '\\';
|
||||
|
||||
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)) {
|
||||
// Happens for pipe reads.
|
||||
break;
|
||||
}
|
||||
if(n == 0)
|
||||
break;
|
||||
b->len += n;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static void genrun(Buf*, char*, int, Vec*, int);
|
||||
|
||||
void
|
||||
runv(Buf *b, char *dir, int mode, Vec *argv)
|
||||
{
|
||||
genrun(b, dir, mode, argv, 1);
|
||||
}
|
||||
|
||||
void
|
||||
bgrunv(char *dir, int mode, Vec *argv)
|
||||
{
|
||||
genrun(nil, dir, mode, argv, 0);
|
||||
}
|
||||
|
||||
#define MAXBG 4 /* maximum number of jobs to run at once */
|
||||
|
||||
static struct {
|
||||
PROCESS_INFORMATION pi;
|
||||
int mode;
|
||||
char *cmd;
|
||||
} bg[MAXBG];
|
||||
|
||||
static int nbg;
|
||||
|
||||
static void bgwait1(void);
|
||||
|
||||
static void
|
||||
genrun(Buf *b, char *dir, int mode, Vec *argv, int wait)
|
||||
{
|
||||
// Another copy of this logic is in ../../lib9/run_windows.c.
|
||||
// If there's a bug here, fix the logic there too.
|
||||
int i, j, nslash;
|
||||
Buf cmd;
|
||||
char *q;
|
||||
Rune *rcmd, *rexe, *rdir;
|
||||
STARTUPINFOW si;
|
||||
PROCESS_INFORMATION pi;
|
||||
HANDLE p[2];
|
||||
|
||||
while(nbg >= nelem(bg))
|
||||
bgwait1();
|
||||
|
||||
binit(&cmd);
|
||||
|
||||
for(i=0; i<argv->len; i++) {
|
||||
q = argv->p[i];
|
||||
if(i == 0 && streq(q, "hg"))
|
||||
bwritestr(&cmd, "cmd.exe /c ");
|
||||
if(i > 0)
|
||||
bwritestr(&cmd, " ");
|
||||
if(contains(q, " ") || contains(q, "\t") || contains(q, "\"") || contains(q, "\\\\") || hassuffix(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);
|
||||
}
|
||||
}
|
||||
if(vflag > 1)
|
||||
errprintf("%s\n", bstr(&cmd));
|
||||
|
||||
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 {
|
||||
SECURITY_ATTRIBUTES seci;
|
||||
|
||||
memset(&seci, 0, sizeof seci);
|
||||
seci.nLength = sizeof seci;
|
||||
seci.bInheritHandle = 1;
|
||||
breset(b);
|
||||
if(!CreatePipe(&p[0], &p[1], &seci, 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]);
|
||||
}
|
||||
|
||||
if(nbg < 0)
|
||||
fatal("bad bookkeeping");
|
||||
bg[nbg].pi = pi;
|
||||
bg[nbg].mode = mode;
|
||||
bg[nbg].cmd = btake(&cmd);
|
||||
nbg++;
|
||||
|
||||
if(wait)
|
||||
bgwait();
|
||||
|
||||
bfree(&cmd);
|
||||
}
|
||||
|
||||
// closes the background job for bgwait1
|
||||
static void
|
||||
bgwaitclose(int i)
|
||||
{
|
||||
if(i < 0 || i >= nbg)
|
||||
return;
|
||||
|
||||
CloseHandle(bg[i].pi.hProcess);
|
||||
CloseHandle(bg[i].pi.hThread);
|
||||
|
||||
bg[i] = bg[--nbg];
|
||||
}
|
||||
|
||||
// bgwait1 waits for a single background job
|
||||
static void
|
||||
bgwait1(void)
|
||||
{
|
||||
int i, mode;
|
||||
char *cmd;
|
||||
HANDLE bgh[MAXBG];
|
||||
DWORD code;
|
||||
|
||||
if(nbg == 0)
|
||||
fatal("bgwait1: nothing left");
|
||||
|
||||
for(i=0; i<nbg; i++)
|
||||
bgh[i] = bg[i].pi.hProcess;
|
||||
i = WaitForMultipleObjects(nbg, bgh, FALSE, INFINITE);
|
||||
if(i < 0 || i >= nbg)
|
||||
fatal("WaitForMultipleObjects: %s", errstr());
|
||||
|
||||
cmd = bg[i].cmd;
|
||||
mode = bg[i].mode;
|
||||
if(!GetExitCodeProcess(bg[i].pi.hProcess, &code)) {
|
||||
bgwaitclose(i);
|
||||
fatal("GetExitCodeProcess: %s", errstr());
|
||||
return;
|
||||
}
|
||||
|
||||
if(mode==CheckExit && code != 0) {
|
||||
bgwaitclose(i);
|
||||
fatal("FAILED: %s", cmd);
|
||||
return;
|
||||
}
|
||||
|
||||
bgwaitclose(i);
|
||||
}
|
||||
|
||||
void
|
||||
bgwait(void)
|
||||
{
|
||||
while(nbg > 0)
|
||||
bgwait1();
|
||||
}
|
||||
|
||||
// rgetwd returns a rune string form of the current directory's path.
|
||||
static Rune*
|
||||
rgetwd(void)
|
||||
{
|
||||
int n;
|
||||
Rune *r;
|
||||
|
||||
n = GetCurrentDirectoryW(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)
|
||||
{
|
||||
Rune *old;
|
||||
Rune *rnew;
|
||||
|
||||
old = rgetwd();
|
||||
torune(&rnew, path);
|
||||
if(!SetCurrentDirectoryW(rnew))
|
||||
fatal("chdir %s: %s", path, errstr());
|
||||
xfree(rnew);
|
||||
xgetwd(b);
|
||||
if(!SetCurrentDirectoryW(old)) {
|
||||
breset(b);
|
||||
toutf(b, old);
|
||||
fatal("chdir %s: %s", bstr(b), errstr());
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
isdir(char *p)
|
||||
{
|
||||
DWORD attr;
|
||||
Rune *r;
|
||||
|
||||
torune(&r, p);
|
||||
attr = GetFileAttributesW(r);
|
||||
xfree(r);
|
||||
return attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY);
|
||||
}
|
||||
|
||||
bool
|
||||
isfile(char *p)
|
||||
{
|
||||
DWORD attr;
|
||||
Rune *r;
|
||||
|
||||
torune(&r, p);
|
||||
attr = GetFileAttributesW(r);
|
||||
xfree(r);
|
||||
return attr != INVALID_FILE_ATTRIBUTES && !(attr & FILE_ATTRIBUTE_DIRECTORY);
|
||||
}
|
||||
|
||||
Time
|
||||
mtime(char *p)
|
||||
{
|
||||
HANDLE h;
|
||||
WIN32_FIND_DATAW data;
|
||||
Rune *r;
|
||||
FILETIME *ft;
|
||||
|
||||
torune(&r, p);
|
||||
h = FindFirstFileW(r, &data);
|
||||
xfree(r);
|
||||
if(h == INVALID_HANDLE_VALUE)
|
||||
return 0;
|
||||
FindClose(h);
|
||||
ft = &data.ftLastWriteTime;
|
||||
return (Time)ft->dwLowDateTime + ((Time)ft->dwHighDateTime<<32);
|
||||
}
|
||||
|
||||
bool
|
||||
isabs(char *p)
|
||||
{
|
||||
// c:/ or c:\ at beginning
|
||||
if(('A' <= p[0] && p[0] <= 'Z') || ('a' <= p[0] && p[0] <= 'z'))
|
||||
return p[1] == ':' && (p[2] == '/' || p[2] == '\\');
|
||||
// / or \ at beginning
|
||||
return p[0] == '/' || p[0] == '\\';
|
||||
}
|
||||
|
||||
void
|
||||
readfile(Buf *b, char *file)
|
||||
{
|
||||
HANDLE h;
|
||||
Rune *r;
|
||||
|
||||
breset(b);
|
||||
if(vflag > 2)
|
||||
errprintf("read %s\n", file);
|
||||
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, int exec)
|
||||
{
|
||||
HANDLE h;
|
||||
Rune *r;
|
||||
DWORD n;
|
||||
|
||||
USED(exec);
|
||||
|
||||
if(vflag > 2)
|
||||
errprintf("write %s\n", file);
|
||||
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);
|
||||
|
||||
errprintf("go tool dist: %s\n", buf1);
|
||||
|
||||
bgwait();
|
||||
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, 0, 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 && streq(p+np-ns, suffix);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
ExitProcess(n);
|
||||
}
|
||||
|
||||
void
|
||||
xatexit(void (*f)(void))
|
||||
{
|
||||
atexit(f);
|
||||
}
|
||||
|
||||
void
|
||||
xprintf(char *fmt, ...)
|
||||
{
|
||||
va_list arg;
|
||||
|
||||
va_start(arg, fmt);
|
||||
vprintf(fmt, arg);
|
||||
va_end(arg);
|
||||
}
|
||||
|
||||
void
|
||||
errprintf(char *fmt, ...)
|
||||
{
|
||||
va_list arg;
|
||||
|
||||
va_start(arg, fmt);
|
||||
vfprintf(stderr, fmt, arg);
|
||||
va_end(arg);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
SYSTEM_INFO si;
|
||||
|
||||
setvbuf(stdout, nil, _IOLBF, 0);
|
||||
setvbuf(stderr, nil, _IOLBF, 0);
|
||||
|
||||
slash = "\\";
|
||||
gohostos = "windows";
|
||||
|
||||
GetSystemInfo(&si);
|
||||
switch(si.wProcessorArchitecture) {
|
||||
case PROCESSOR_ARCHITECTURE_AMD64:
|
||||
gohostarch = "amd64";
|
||||
break;
|
||||
case PROCESSOR_ARCHITECTURE_INTEL:
|
||||
gohostarch = "386";
|
||||
break;
|
||||
default:
|
||||
fatal("unknown processor architecture");
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// xsamefile reports whether f1 and f2 are the same file (or dir)
|
||||
int
|
||||
xsamefile(char *f1, char *f2)
|
||||
{
|
||||
Rune *ru;
|
||||
HANDLE fd1, fd2;
|
||||
BY_HANDLE_FILE_INFORMATION fi1, fi2;
|
||||
int r;
|
||||
|
||||
// trivial case
|
||||
if(streq(f1, f2))
|
||||
return 1;
|
||||
|
||||
torune(&ru, f1);
|
||||
// refer to ../../os/stat_windows.go:/sameFile
|
||||
fd1 = CreateFileW(ru, 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
|
||||
xfree(ru);
|
||||
if(fd1 == INVALID_HANDLE_VALUE)
|
||||
return 0;
|
||||
torune(&ru, f2);
|
||||
fd2 = CreateFileW(ru, 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
|
||||
xfree(ru);
|
||||
if(fd2 == INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(fd1);
|
||||
return 0;
|
||||
}
|
||||
r = GetFileInformationByHandle(fd1, &fi1) != 0 && GetFileInformationByHandle(fd2, &fi2) != 0;
|
||||
CloseHandle(fd2);
|
||||
CloseHandle(fd1);
|
||||
if(r != 0 &&
|
||||
fi1.dwVolumeSerialNumber == fi2.dwVolumeSerialNumber &&
|
||||
fi1.nFileIndexHigh == fi2.nFileIndexHigh &&
|
||||
fi1.nFileIndexLow == fi2.nFileIndexLow)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// xtryexecfunc tries to execute function f, if any illegal instruction
|
||||
// signal received in the course of executing that function, it will
|
||||
// return 0, otherwise it will return 1.
|
||||
int
|
||||
xtryexecfunc(void (*f)(void))
|
||||
{
|
||||
return 0; // suffice for now
|
||||
}
|
||||
|
||||
static void
|
||||
cpuid(int dst[4], int ax)
|
||||
{
|
||||
// NOTE: This asm statement is for mingw.
|
||||
// If we ever support MSVC, use __cpuid(dst, ax)
|
||||
// to use the built-in.
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
asm volatile("cpuid"
|
||||
: "=a" (dst[0]), "=b" (dst[1]), "=c" (dst[2]), "=d" (dst[3])
|
||||
: "0" (ax));
|
||||
#else
|
||||
dst[0] = dst[1] = dst[2] = dst[3] = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
cansse2(void)
|
||||
{
|
||||
int info[4];
|
||||
|
||||
cpuid(info, 1);
|
||||
return (info[3] & (1<<26)) != 0; // SSE2
|
||||
}
|
||||
|
||||
|
||||
#endif // __WINDOWS__
|
||||
|
|
@ -3,6 +3,8 @@
|
|||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file.
|
||||
|
||||
# See golang.org/s/go15bootstrap for an overview of the build process.
|
||||
|
||||
# Environment variables that control make.bash:
|
||||
#
|
||||
# GOROOT_FINAL: The expected final Go root, baked into binaries.
|
||||
|
|
@ -110,26 +112,16 @@ rm -f ./runtime/runtime_defs.go
|
|||
|
||||
# Finally! Run the build.
|
||||
|
||||
echo '##### Building C bootstrap tool.'
|
||||
echo '##### Building Go bootstrap tool.'
|
||||
echo cmd/dist
|
||||
export GOROOT="$(cd .. && pwd)"
|
||||
GOROOT_FINAL="${GOROOT_FINAL:-$GOROOT}"
|
||||
DEFGOROOT='-DGOROOT_FINAL="'"$GOROOT_FINAL"'"'
|
||||
|
||||
mflag=""
|
||||
case "$GOHOSTARCH" in
|
||||
386) mflag=-m32;;
|
||||
amd64) mflag=-m64;;
|
||||
esac
|
||||
if [ "$(uname)" == "Darwin" ]; then
|
||||
# golang.org/issue/5261
|
||||
mflag="$mflag -mmacosx-version-min=10.6"
|
||||
GOROOT_BOOTSTRAP=${GOROOT_BOOTSTRAP:-$HOME/go1.4}
|
||||
if [ ! -x "$GOROOT_BOOTSTRAP/bin/go" ]; then
|
||||
echo "ERROR: Cannot find $GOROOT_BOOTSTRAP/bin/go." >&2
|
||||
echo "Set \$GOROOT_BOOTSTRAP to a working Go tree >= Go 1.4." >&2
|
||||
fi
|
||||
# if gcc does not exist and $CC is not set, try clang if available.
|
||||
if [ -z "$CC" -a -z "$(type -t gcc)" -a -n "$(type -t clang)" ]; then
|
||||
export CC=clang CXX=clang++
|
||||
fi
|
||||
${CC:-gcc} $mflag -O2 -Wall -Werror -o cmd/dist/dist -Icmd/dist "$DEFGOROOT" cmd/dist/*.c
|
||||
rm -f cmd/dist/dist
|
||||
GOROOT="$GOROOT_BOOTSTRAP" GOOS="" GOARCH="" "$GOROOT_BOOTSTRAP/bin/go" build -o cmd/dist/dist ./cmd/dist
|
||||
|
||||
# -e doesn't propagate out of eval, so check success by hand.
|
||||
eval $(./cmd/dist/dist env -p || echo FAIL=true)
|
||||
|
|
|
|||
26
src/make.bat
26
src/make.bat
|
|
@ -45,24 +45,24 @@ goto fail
|
|||
:: Clean old generated file that will cause problems in the build.
|
||||
del /F ".\pkg\runtime\runtime_defs.go" 2>NUL
|
||||
|
||||
:: Grab default GOROOT_FINAL and set GOROOT for build.
|
||||
:: The expression %VAR:\=\\% means to take %VAR%
|
||||
:: and apply the substitution \ = \\, escaping the
|
||||
:: backslashes. Then we wrap that in quotes to create
|
||||
:: a C string.
|
||||
:: Set GOROOT for build.
|
||||
cd ..
|
||||
set GOROOT=%CD%
|
||||
cd src
|
||||
if "x%GOROOT_FINAL%"=="x" set GOROOT_FINAL=%GOROOT%
|
||||
set DEFGOROOT=-DGOROOT_FINAL="\"%GOROOT_FINAL:\=\\%\""
|
||||
|
||||
echo ##### Building C bootstrap tool.
|
||||
echo ##### Building Go bootstrap tool.
|
||||
echo cmd/dist
|
||||
if not exist ..\bin\tool mkdir ..\bin\tool
|
||||
:: Windows has no glob expansion, so spell out cmd/dist/*.c.
|
||||
gcc -O2 -Wall -Werror -o cmd/dist/dist.exe -Icmd/dist %DEFGOROOT% cmd/dist/buf.c cmd/dist/build.c cmd/dist/buildgc.c cmd/dist/buildgo.c cmd/dist/buildruntime.c cmd/dist/main.c cmd/dist/windows.c cmd/dist/arm.c
|
||||
if "x%GOROOT_BOOTSTRAP%"=="x" set GOROOT_BOOTSTRAP=%HOMEDRIVE%%HOMEPATH%\Go1.4
|
||||
if not exist "%GOROOT_BOOTSTRAP%\bin\go.exe" goto bootstrapfail
|
||||
setlocal
|
||||
set GOROOT=%GOROOT_BOOTSTRAP%
|
||||
set GOOS=
|
||||
set GOARCH=
|
||||
"%GOROOT_BOOTSTRAP%\bin\go" build -o cmd\dist\dist.exe .\cmd\dist
|
||||
endlocal
|
||||
if errorlevel 1 goto fail
|
||||
.\cmd\dist\dist env -wp >env.bat
|
||||
.\cmd\dist\dist env -w -p >env.bat
|
||||
if errorlevel 1 goto fail
|
||||
call env.bat
|
||||
del env.bat
|
||||
|
|
@ -113,6 +113,10 @@ mkdir "%GOTOOLDIR%" 2>NUL
|
|||
copy cmd\dist\dist.exe "%GOTOOLDIR%\"
|
||||
goto end
|
||||
|
||||
:bootstrapfail
|
||||
echo ERROR: Cannot find %GOROOT_BOOTSTRAP%\bin\go.exe
|
||||
echo "Set GOROOT_BOOTSTRAP to a working Go tree >= Go 1.4."
|
||||
|
||||
:fail
|
||||
set GOBUILDFAIL=1
|
||||
if x%GOBUILDEXIT%==x1 exit %GOBUILDFAIL%
|
||||
|
|
|
|||
21
src/make.rc
21
src/make.rc
|
|
@ -3,6 +3,8 @@
|
|||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file.
|
||||
|
||||
# See golang.org/s/go15bootstrap for an overview of the build process.
|
||||
|
||||
# Environment variables that control make.rc:
|
||||
#
|
||||
# GOROOT_FINAL: The expected final Go root, baked into binaries.
|
||||
|
|
@ -45,17 +47,18 @@ rm -f ./runtime/runtime_defs.go
|
|||
# Determine the host compiler toolchain.
|
||||
eval `{grep '^(CC|LD|O)=' /$objtype/mkfile}
|
||||
|
||||
echo '# Building C bootstrap tool.'
|
||||
echo '# Building Go bootstrap tool.'
|
||||
echo cmd/dist
|
||||
GOROOT = `{cd .. && pwd}
|
||||
if(! ~ $#GOROOT_FINAL 1)
|
||||
GOROOT_FINAL = $GOROOT
|
||||
DEFGOROOT='-DGOROOT_FINAL="'$GOROOT_FINAL'"'
|
||||
|
||||
for(i in cmd/dist/*.c)
|
||||
$CC -FTVwp+ -DPLAN9 $DEFGOROOT $i
|
||||
$LD -o cmd/dist/dist *.$O
|
||||
rm *.$O
|
||||
if(! ~ $#GOROOT_BOOTSTRAP 1)
|
||||
GOROOT_BOOTSTRAP = $home/go1.4
|
||||
if(! test -x $GOROOT_BOOTSTRAP/bin/go){
|
||||
echo 'ERROR: Cannot find '$GOROOT_BOOTSTRAP'/bin/go.' >[1=2]
|
||||
echo 'Set $GOROOT_BOOTSTRAP to a working Go tree >= Go 1.4.' >[1=2]
|
||||
exit bootstrap
|
||||
}
|
||||
rm -f cmd/dist/dist
|
||||
GOROOT=$GOROOT_BOOTSTRAP GOOS='' GOARCH='' $GOROOT_BOOTSTRAP/bin/go build -o cmd/dist/dist ./cmd/dist
|
||||
|
||||
eval `{./cmd/dist/dist env -9}
|
||||
echo
|
||||
|
|
|
|||
|
|
@ -1,41 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
# Copyright 2009 The Go Authors. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file.
|
||||
|
||||
set -e
|
||||
|
||||
case "`uname`" in
|
||||
Darwin)
|
||||
;;
|
||||
*)
|
||||
exit 0
|
||||
esac
|
||||
|
||||
# Check that the go command exists
|
||||
if ! go help >/dev/null 2>&1; then
|
||||
echo "The go command is not in your PATH." >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
eval $(go env)
|
||||
if ! [ -x $GOTOOLDIR/prof ]; then
|
||||
echo "You don't need to run sudo.bash." >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if [[ ! -d /usr/local/bin ]]; then
|
||||
echo 1>&2 'sudo.bash: problem with /usr/local/bin; cannot install tools.'
|
||||
exit 2
|
||||
fi
|
||||
|
||||
cd $(dirname $0)
|
||||
for i in prof
|
||||
do
|
||||
# Remove old binaries if present
|
||||
sudo rm -f /usr/local/bin/6$i
|
||||
# Install new binaries
|
||||
sudo cp $GOTOOLDIR/$i /usr/local/bin/go$i
|
||||
sudo chgrp procmod /usr/local/bin/go$i
|
||||
sudo chmod g+s /usr/local/bin/go$i
|
||||
done
|
||||
Loading…
Reference in New Issue