Commit 9af454c5 authored by Russ Cox's avatar Russ Cox

all: merge dev.cc (929f3210) 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
parents d5d4e82f 929f3210
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.
// 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));
/*
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
// 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
// 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);
}
}
......@@ -2,47 +2,58 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "a.h"
#include "arg.h"
package main
/*
* Initialization for any invocation.
*/
import (
"bytes"
"flag"
"fmt"
"os"
"path/filepath"
"runtime"
"strings"
)
// Initialization for any invocation.
// The usual variables.
char *goarch;
char *gobin;
char *gohostarch;
char *gohostchar;
char *gohostos;
char *goos;
char *goarm;
char *go386;
char *goroot = GOROOT_FINAL;
char *goroot_final = GOROOT_FINAL;
char *goextlinkenabled = "";
char *workdir;
char *tooldir;
char *gochar;
char *goversion;
char *slash; // / for unix, \ for windows
char *defaultcc;
char *defaultcflags;
char *defaultldflags;
char *defaultcxxtarget;
char *defaultcctarget;
bool rebuildall;
bool defaultclang;
static bool shouldbuild(char*, char*);
static void dopack(char*, char*, char**, int);
static char *findgoversion(void);
var (
goarch string
gobin string
gohostarch string
gohostchar string
gohostos string
goos string
goarm string
go386 string
goroot string
goroot_final string
goextlinkenabled string
workdir string
tooldir string
gochar string
goversion string
oldgoos string
oldgoarch string
oldgochar string
slash string
defaultcc string
defaultcflags string
defaultldflags string
defaultcxxtarget string
defaultcctarget string
rebuildall bool
defaultclang bool
sflag bool // build static binaries
vflag int // verbosity
)
// The known architecture letters.
static char *gochars = "566899";
var gochars = "566899"
// The known architectures.
static char *okgoarch[] = {
var okgoarch = []string{
// same order as gochars
"arm",
"amd64",
......@@ -50,10 +61,10 @@ static char *okgoarch[] = {
"386",
"ppc64",
"ppc64le",
};
}
// The known operating systems.
static char *okgoos[] = {
var okgoos = []string{
"darwin",
"dragonfly",
"linux",
......@@ -65,294 +76,252 @@ static char *okgoos[] = {
"openbsd",
"plan9",
"windows",
};
static void rmworkdir(void);
}
// find reports the first index of p in l[0:n], or else -1.
int
find(char *p, char **l, int n)
{
int i;
for(i=0; i<n; i++)
if(streq(p, l[i]))
return i;
return -1;
func find(p string, l []string) int {
for i, s := range l {
if p == s {
return i
}
}
return -1
}
// init handles initialization of the various global state, like goroot and goarch.
void
init(void)
{
char *p;
int i;
Buf b;
binit(&b);
xgetenv(&b, "GOROOT");
if(b.len > 0) {
// if not "/", then strip trailing path separator
if(b.len >= 2 && b.p[b.len - 1] == slash[0])
b.len--;
goroot = btake(&b);
}
xgetenv(&b, "GOBIN");
if(b.len == 0)
bprintf(&b, "%s%sbin", goroot, slash);
gobin = btake(&b);
xgetenv(&b, "GOOS");
if(b.len == 0)
bwritestr(&b, gohostos);
goos = btake(&b);
if(find(goos, okgoos, nelem(okgoos)) < 0)
fatal("unknown $GOOS %s", goos);
xgetenv(&b, "GOARM");
if(b.len == 0)
bwritestr(&b, xgetgoarm());
goarm = btake(&b);
xgetenv(&b, "GO386");
if(b.len == 0) {
if(cansse2())
bwritestr(&b, "sse2");
else
bwritestr(&b, "387");
}
go386 = btake(&b);
p = bpathf(&b, "%s/include/u.h", goroot);
if(!isfile(p)) {
fatal("$GOROOT is not set correctly or not exported\n"
"\tGOROOT=%s\n"
"\t%s does not exist", goroot, p);
}
xgetenv(&b, "GOHOSTARCH");
if(b.len > 0)
gohostarch = btake(&b);
i = find(gohostarch, okgoarch, nelem(okgoarch));
if(i < 0)
fatal("unknown $GOHOSTARCH %s", gohostarch);
bprintf(&b, "%c", gochars[i]);
gohostchar = btake(&b);
xgetenv(&b, "GOARCH");
if(b.len == 0)
bwritestr(&b, gohostarch);
goarch = btake(&b);
i = find(goarch, okgoarch, nelem(okgoarch));
if(i < 0)
fatal("unknown $GOARCH %s", goarch);
bprintf(&b, "%c", gochars[i]);
gochar = btake(&b);
xgetenv(&b, "GO_EXTLINK_ENABLED");
if(b.len > 0) {
goextlinkenabled = btake(&b);
if(!streq(goextlinkenabled, "0") && !streq(goextlinkenabled, "1"))
fatal("unknown $GO_EXTLINK_ENABLED %s", goextlinkenabled);
}
xgetenv(&b, "CC");
if(b.len == 0) {
// xinit handles initialization of the various global state, like goroot and goarch.
func xinit() {
goroot = os.Getenv("GOROOT")
if slash == "/" && len(goroot) > 1 || slash == `\` && len(goroot) > 3 {
// if not "/" or "c:\", then strip trailing path separator
goroot = strings.TrimSuffix(goroot, slash)
}
if goroot == "" {
fatal("$GOROOT must be set")
}
goroot_final = os.Getenv("GOROOT_FINAL")
if goroot_final == "" {
goroot_final = goroot
}
b := os.Getenv("GOBIN")
if b == "" {
b = goroot + slash + "bin"
}
gobin = b
b = os.Getenv("GOOS")
if b == "" {
b = gohostos
}
goos = b
if find(goos, okgoos) < 0 {
fatal("unknown $GOOS %s", goos)
}
b = os.Getenv("GOARM")
if b == "" {
b = xgetgoarm()
}
goarm = b
b = os.Getenv("GO386")
if b == "" {
if cansse2() {
b = "sse2"
} else {
b = "387"
}
}
go386 = b
p := pathf("%s/include/u.h", goroot)
if !isfile(p) {
fatal("$GOROOT is not set correctly or not exported\n"+
"\tGOROOT=%s\n"+
"\t%s does not exist", goroot, p)
}
b = os.Getenv("GOHOSTARCH")
if b != "" {
gohostarch = b
}
i := find(gohostarch, okgoarch)
if i < 0 {
fatal("unknown $GOHOSTARCH %s", gohostarch)
}
gohostchar = gochars[i : i+1]
b = os.Getenv("GOARCH")
if b == "" {
b = gohostarch
}
goarch = b
i = find(goarch, okgoarch)
if i < 0 {
fatal("unknown $GOARCH %s", goarch)
}
gochar = gochars[i : i+1]
b = os.Getenv("GO_EXTLINK_ENABLED")
if b != "" {
if b != "0" && b != "1" {
fatal("unknown $GO_EXTLINK_ENABLED %s", b)
}
goextlinkenabled = b
}
b = os.Getenv("CC")
if b == "" {
// Use clang on OS X, because gcc is deprecated there.
// Xcode for OS X 10.9 Mavericks will ship a fake "gcc" binary that
// actually runs clang. We prepare different command
// lines for the two binaries, so it matters what we call it.
// See golang.org/issue/5822.
if(defaultclang)
bprintf(&b, "clang");
else
bprintf(&b, "gcc");
if defaultclang {
b = "clang"
} else {
b = "gcc"
}
}
defaultcc = btake(&b);
defaultcc = b
xgetenv(&b, "CFLAGS");
defaultcflags = btake(&b);
defaultcflags = os.Getenv("CFLAGS")
xgetenv(&b, "LDFLAGS");
defaultldflags = btake(&b);
defaultldflags = os.Getenv("LDFLAGS")
xgetenv(&b, "CC_FOR_TARGET");
if(b.len == 0) {
bprintf(&b, defaultcc);
b = os.Getenv("CC_FOR_TARGET")
if b == "" {
b = defaultcc
}
defaultcctarget = btake(&b);
xgetenv(&b, "CXX_FOR_TARGET");
if(b.len == 0) {
xgetenv(&b, "CXX");
if(b.len == 0) {
if(defaultclang)
bprintf(&b, "clang++");
else
bprintf(&b, "g++");
defaultcctarget = b
b = os.Getenv("CXX_FOR_TARGET")
if b == "" {
b = os.Getenv("CXX")
if b == "" {
if defaultclang {
b = "clang++"
} else {
b = "g++"
}
}
}
defaultcxxtarget = btake(&b);
xsetenv("GOROOT", goroot);
xsetenv("GOARCH", goarch);
xsetenv("GOOS", goos);
xsetenv("GOARM", goarm);
xsetenv("GO386", go386);
defaultcxxtarget = b
// For tools being invoked but also for os.ExpandEnv.
os.Setenv("GO386", go386)
os.Setenv("GOARCH", goarch)
os.Setenv("GOARM", goarm)
os.Setenv("GOHOSTARCH", gohostarch)
os.Setenv("GOHOSTOS", gohostos)
os.Setenv("GOOS", goos)
os.Setenv("GOROOT", goroot)
os.Setenv("GOROOT_FINAL", goroot_final)
// Make the environment more predictable.
xsetenv("LANG", "C");
xsetenv("LANGUAGE", "en_US.UTF8");
goversion = findgoversion();
os.Setenv("LANG", "C")
os.Setenv("LANGUAGE", "en_US.UTF8")
workdir = xworkdir();
xatexit(rmworkdir);
goversion = findgoversion()
bpathf(&b, "%s/pkg/tool/%s_%s", goroot, gohostos, gohostarch);
tooldir = btake(&b);
workdir = xworkdir()
xatexit(rmworkdir)
bfree(&b);
tooldir = pathf("%s/pkg/tool/%s_%s", goroot, gohostos, gohostarch)
}
// rmworkdir deletes the work directory.
static void
rmworkdir(void)
{
if(vflag > 1)
errprintf("rm -rf %s\n", workdir);
xremoveall(workdir);
func rmworkdir() {
if vflag > 1 {
errprintf("rm -rf %s\n", workdir)
}
xremoveall(workdir)
}
// Remove trailing spaces.
static void
chomp(Buf *b)
{
int c;
while(b->len > 0 && ((c=b->p[b->len-1]) == ' ' || c == '\t' || c == '\r' || c == '\n'))
b->len--;
func chomp(s string) string {
return strings.TrimRight(s, " \t\r\n")
}
static char*
branchtag(char *branch, bool *precise)
{
char *tag, *p, *q;
int i;
Buf b, arg;
Vec tags;
binit(&b);
binit(&arg);
vinit(&tags);
bprintf(&arg, "master..%s", branch);
run(&b, goroot, CheckExit, "git", "log", "--decorate=full", "--format=format:%d", bstr(&arg), nil);
splitlines(&tags, bstr(&b));
tag = branch;
for(i=0; i < tags.len; i++) {
func branchtag(branch string) (tag string, precise bool) {
b := run(goroot, CheckExit, "git", "log", "--decorate=full", "--format=format:%d", "master.."+branch)
tag = branch
for _, line := range splitlines(b) {
// Each line is either blank, or looks like
// (tag: refs/tags/go1.4rc2, refs/remotes/origin/release-branch.go1.4, refs/heads/release-branch.go1.4)
// We need to find an element starting with refs/tags/.
p = xstrstr(tags.p[i], " refs/tags/");
if(p == nil)
continue;
p += xstrlen(" refs/tags/");
i := strings.Index(line, " refs/tags/")
if i < 0 {
continue
}
i += len(" refs/tags/")
// The tag name ends at a comma or paren (prefer the first).
q = xstrstr(p, ",");
if(q == nil)
q = xstrstr(p, ")");
if(q == nil)
continue; // malformed line; ignore it
*q = '\0';
tag = xstrdup(p);
if(i == 0)
*precise = 1; // tag denotes HEAD
break;
}
bfree(&b);
bfree(&arg);
vfree(&tags);
return tag;
j := strings.Index(line[i:], ",")
if j < 0 {
j = strings.Index(line[i:], ")")
}
if j < 0 {
continue // malformed line; ignore it
}
tag = line[i : i+j]
if i == 0 {
precise = true // tag denotes HEAD
}
break
}
return
}
// findgoversion determines the Go version to use in the version string.
static char*
findgoversion(void)
{
char *tag, *p;
bool precise;
Buf b, path, bmore, branch;
binit(&b);
binit(&path);
binit(&bmore);
binit(&branch);
func findgoversion() string {
// The $GOROOT/VERSION file takes priority, for distributions
// without the source repo.
bpathf(&path, "%s/VERSION", goroot);
if(isfile(bstr(&path))) {
readfile(&b, bstr(&path));
chomp(&b);
path := pathf("%s/VERSION", goroot)
if isfile(path) {
b := chomp(readfile(path))
// Commands such as "dist version > VERSION" will cause
// the shell to create an empty VERSION file and set dist's
// stdout to its fd. dist in turn looks at VERSION and uses
// its content if available, which is empty at this point.
if(b.len > 0)
goto done;
// Only use the VERSION file if it is non-empty.
if b != "" {
return b
}
}
// The $GOROOT/VERSION.cache file is a cache to avoid invoking
// git every time we run this command. Unlike VERSION, it gets
// deleted by the clean command.
bpathf(&path, "%s/VERSION.cache", goroot);
if(isfile(bstr(&path))) {
readfile(&b, bstr(&path));
chomp(&b);
goto done;
path = pathf("%s/VERSION.cache", goroot)
if isfile(path) {
return chomp(readfile(path))
}
// Otherwise, use Git.
// What is the current branch?
run(&branch, goroot, CheckExit, "git", "rev-parse", "--abbrev-ref", "HEAD", nil);
chomp(&branch);
branch := chomp(run(goroot, CheckExit, "git", "rev-parse", "--abbrev-ref", "HEAD"))
// What are the tags along the current branch?
tag = "devel";
precise = 0;
tag := "devel"
precise := false
// If we're on a release branch, use the closest matching tag
// that is on the release branch (and not on the master branch).
if(hasprefix(bstr(&branch), "release-branch."))
tag = branchtag(bstr(&branch), &precise);
if strings.HasPrefix(branch, "release-branch.") {
tag, precise = branchtag(branch)
}
bprintf(&b, "%s", tag);
if(!precise) {
if !precise {
// Tag does not point at HEAD; add hash and date to version.
run(&bmore, goroot, CheckExit, "git", "log", "-n", "1", "--format=format: +%h %cd", "HEAD", nil);
chomp(&bmore);
bwriteb(&b, &bmore);
tag += chomp(run(goroot, CheckExit, "git", "log", "-n", "1", "--format=format: +%h %cd", "HEAD"))
}
// Cache version.
writefile(&b, bstr(&path), 0);
done:
p = btake(&b);
writefile(tag, path, 0)
bfree(&b);
bfree(&path);
bfree(&bmore);
bfree(&branch);
return p;
return tag
}
/*
......@@ -360,7 +329,7 @@ done:
*/
// The old tools that no longer live in $GOBIN or $GOROOT/bin.
static char *oldtool[] = {
var oldtool = []string{
"5a", "5c", "5g", "5l",
"6a", "6c", "6g", "6l",
"8a", "8c", "8g", "8l",
......@@ -381,44 +350,40 @@ static char *oldtool[] = {
"govet",
"goyacc",
"quietgcc",
};
}
// Unreleased directories (relative to $GOROOT) that should
// not be in release branches.
static char *unreleased[] = {
var unreleased = []string{
"src/cmd/link",
"src/debug/goobj",
"src/old",
};
}
// setup sets up the tree for the initial build.
static void
setup(void)
{
int i;
Buf b;
char *p;
binit(&b);
func setup() {
// Create bin directory.
p = bpathf(&b, "%s/bin", goroot);
if(!isdir(p))
xmkdir(p);
if p := pathf("%s/bin", goroot); !isdir(p) {
xmkdir(p)
}
// Create package directory.
p = bpathf(&b, "%s/pkg", goroot);
if(!isdir(p))
xmkdir(p);
p = bpathf(&b, "%s/pkg/%s_%s", goroot, gohostos, gohostarch);
if(rebuildall)
xremoveall(p);
xmkdirall(p);
if(!streq(goos, gohostos) || !streq(goarch, gohostarch)) {
p = bpathf(&b, "%s/pkg/%s_%s", goroot, goos, goarch);
if(rebuildall)
xremoveall(p);
xmkdirall(p);
if p := pathf("%s/pkg", goroot); !isdir(p) {
xmkdir(p)
}
p := pathf("%s/pkg/%s_%s", goroot, gohostos, gohostarch)
if rebuildall {
xremoveall(p)
}
xmkdirall(p)
if goos != gohostos || goarch != gohostarch {
p := pathf("%s/pkg/%s_%s", goroot, goos, goarch)
if rebuildall {
xremoveall(p)
}
xmkdirall(p)
}
// Create object directory.
......@@ -426,44 +391,48 @@ setup(void)
// are in one tree. If pkg/obj/libgc.a exists, it is a dreg from
// before we used subdirectories of obj. Delete all of obj
// to clean up.
bpathf(&b, "%s/pkg/obj/libgc.a", goroot);
if(isfile(bstr(&b)))
xremoveall(bpathf(&b, "%s/pkg/obj", goroot));
p = bpathf(&b, "%s/pkg/obj/%s_%s", goroot, gohostos, gohostarch);
if(rebuildall)
xremoveall(p);
xmkdirall(p);
if p := pathf("%s/pkg/obj/libgc.a", goroot); isfile(p) {
xremoveall(pathf("%s/pkg/obj", goroot))
}
p = pathf("%s/pkg/obj/%s_%s", goroot, gohostos, gohostarch)
if rebuildall {
xremoveall(p)
}
xmkdirall(p)
// Create tool directory.
// We keep it in pkg/, just like the object directory above.
if(rebuildall)
xremoveall(tooldir);
xmkdirall(tooldir);
if rebuildall {
xremoveall(tooldir)
}
xmkdirall(tooldir)
// Remove tool binaries from before the tool/gohostos_gohostarch
xremoveall(bpathf(&b, "%s/bin/tool", goroot));
xremoveall(pathf("%s/bin/tool", goroot))
// Remove old pre-tool binaries.
for(i=0; i<nelem(oldtool); i++)
xremove(bpathf(&b, "%s/bin/%s", goroot, oldtool[i]));
for _, old := range oldtool {
xremove(pathf("%s/bin/%s", goroot, old))
}
// If $GOBIN is set and has a Go compiler, it must be cleaned.
for(i=0; gochars[i]; i++) {
if(isfile(bprintf(&b, "%s%s%c%s", gobin, slash, gochars[i], "g"))) {
for(i=0; i<nelem(oldtool); i++)
xremove(bprintf(&b, "%s%s%s", gobin, slash, oldtool[i]));
break;
for _, char := range gochars {
if isfile(pathf("%s%s%c%s", gobin, slash, char, "g")) {
for _, old := range oldtool {
xremove(pathf("%s/%s", gobin, old))
}
break
}
}
// For release, make sure excluded things are excluded.
if(hasprefix(goversion, "release.") || (hasprefix(goversion, "go") && !contains(goversion, "beta"))) {
for(i=0; i<nelem(unreleased); i++)
if(isdir(bpathf(&b, "%s/%s", goroot, unreleased[i])))
fatal("%s should not exist in release build", bstr(&b));
if strings.HasPrefix(goversion, "release.") || (strings.HasPrefix(goversion, "go") && !strings.Contains(goversion, "beta")) {
for _, dir := range unreleased {
if p := pathf("%s/%s", goroot, dir); isdir(p) {
fatal("%s should not exist in release build", p)
}
}
}
bfree(&b);
}
/*
......@@ -471,7 +440,7 @@ setup(void)
*/
// gccargs is the gcc command line to use for compiling a single C file.
static char *proto_gccargs[] = {
var proto_gccargs = []string{
"-Wall",
// native Plan 9 compilers don't like non-standard prototypes
// so let gcc catch them.
......@@ -489,34 +458,36 @@ static char *proto_gccargs[] = {
"-fno-common",
"-ggdb",
"-pipe",
};
}
// gccargs2 is the second part of gccargs.
// it is used if the environment isn't defining CFLAGS.
static char *proto_gccargs2[] = {
var proto_gccargs2 = []string{
// on older versions of GCC, -Wuninitialized is not supported
// without -O, so put it here together with -O settings in case
// the user's $CFLAGS doesn't include -O.
"-Wuninitialized",
#if defined(__NetBSD__) && defined(__arm__)
// GCC 4.5.4 (NetBSD nb1 20120916) on ARM is known to mis-optimize gc/mparith3.c
// Fix available at http://patchwork.ozlabs.org/patch/64562/.
"-O1",
#else
"-O2",
#endif
};
}
func init() {
if runtime.GOOS == "netbsd" && runtime.GOARCH == "arm" {
// GCC 4.5.4 (NetBSD nb1 20120916) on ARM is known to mis-optimize gc/mparith3.c
// Fix available at http://patchwork.ozlabs.org/patch/64562/.
proto_gccargs2[1] = "-O1"
}
}
static Vec gccargs, ldargs;
var gccargs, ldargs []string
// deptab lists changes to the default dependencies for a given prefix.
// deps ending in /* read the whole directory; deps beginning with -
// exclude files with that prefix.
static struct {
char *prefix; // prefix of target
char *dep[20]; // dependency tweaks for targets with that prefix
} deptab[] = {
{"lib9", {
var deptab = []struct {
prefix string // prefix of target
dep []string // dependency tweaks for targets with that prefix
}{
{"lib9", []string{
"$GOROOT/include/u.h",
"$GOROOT/include/utf.h",
"$GOROOT/include/fmt.h",
......@@ -524,14 +495,14 @@ static struct {
"fmt/*",
"utf/*",
}},
{"libbio", {
{"libbio", []string{
"$GOROOT/include/u.h",
"$GOROOT/include/utf.h",
"$GOROOT/include/fmt.h",
"$GOROOT/include/libc.h",
"$GOROOT/include/bio.h",
}},
{"liblink", {
{"liblink", []string{
"$GOROOT/include/u.h",
"$GOROOT/include/utf.h",
"$GOROOT/include/fmt.h",
......@@ -544,85 +515,85 @@ static struct {
"anames8.c",
"anames9.c",
}},
{"cmd/gc", {
{"cmd/gc", []string{
"-cplx.c",
"-pgen.c",
"-plive.c",
"-popt.c",
"-y1.tab.c", // makefile dreg
"-y1.tab.c", // makefile dreg
"opnames.h",
}},
{"cmd/5g", {
{"cmd/5g", []string{
"../gc/cplx.c",
"../gc/pgen.c",
"../gc/plive.c",
"../gc/popt.c",
"../gc/popt.h",
"$GOROOT/pkg/obj/$GOHOSTOS_$GOHOSTARCH/libgc.a",
"$GOROOT/pkg/obj/${GOHOSTOS}_$GOHOSTARCH/libgc.a",
}},
{"cmd/6g", {
{"cmd/6g", []string{
"../gc/cplx.c",
"../gc/pgen.c",
"../gc/plive.c",
"../gc/popt.c",
"../gc/popt.h",
"$GOROOT/pkg/obj/$GOHOSTOS_$GOHOSTARCH/libgc.a",
"$GOROOT/pkg/obj/${GOHOSTOS}_$GOHOSTARCH/libgc.a",
}},
{"cmd/8g", {
{"cmd/8g", []string{
"../gc/cplx.c",
"../gc/pgen.c",
"../gc/plive.c",
"../gc/popt.c",
"../gc/popt.h",
"$GOROOT/pkg/obj/$GOHOSTOS_$GOHOSTARCH/libgc.a",
"$GOROOT/pkg/obj/${GOHOSTOS}_$GOHOSTARCH/libgc.a",
}},
{"cmd/9g", {
{"cmd/9g", []string{
"../gc/cplx.c",
"../gc/pgen.c",
"../gc/plive.c",
"../gc/popt.c",
"../gc/popt.h",
"$GOROOT/pkg/obj/$GOHOSTOS_$GOHOSTARCH/libgc.a",
"$GOROOT/pkg/obj/${GOHOSTOS}_$GOHOSTARCH/libgc.a",
}},
{"cmd/5l", {
{"cmd/5l", []string{
"../ld/*",
}},
{"cmd/6l", {
{"cmd/6l", []string{
"../ld/*",
}},
{"cmd/8l", {
{"cmd/8l", []string{
"../ld/*",
}},
{"cmd/9l", {
{"cmd/9l", []string{
"../ld/*",
}},
{"cmd/go", {
{"cmd/go", []string{
"zdefaultcc.go",
}},
{"cmd/", {
"$GOROOT/pkg/obj/$GOHOSTOS_$GOHOSTARCH/liblink.a",
"$GOROOT/pkg/obj/$GOHOSTOS_$GOHOSTARCH/libbio.a",
"$GOROOT/pkg/obj/$GOHOSTOS_$GOHOSTARCH/lib9.a",
{"cmd/", []string{
"$GOROOT/pkg/obj/${GOHOSTOS}_$GOHOSTARCH/liblink.a",
"$GOROOT/pkg/obj/${GOHOSTOS}_$GOHOSTARCH/libbio.a",
"$GOROOT/pkg/obj/${GOHOSTOS}_$GOHOSTARCH/lib9.a",
}},
{"runtime", {
{"runtime", []string{
"zaexperiment.h",
"zversion.go",
}},
};
}
// depsuffix records the allowed suffixes for source files.
char *depsuffix[] = {
var depsuffix = []string{
".c",
".h",
".s",
".go",
};
}
// gentab records how to generate some trivial files.
static struct {
char *nameprefix;
void (*gen)(char*, char*);
} gentab[] = {
var gentab = []struct {
nameprefix string
gen func(string, string)
}{
{"opnames.h", gcopnames},
{"anames5.c", mkanames},
{"anames6.c", mkanames},
......@@ -634,272 +605,238 @@ static struct {
// not generated anymore, but delete the file if we see it
{"enam.c", nil},
};
}
// install installs the library, package, or binary associated with dir,
// which is relative to $GOROOT/src.
static void
install(char *dir)
{
char *name, *p, *elem, *prefix, *exe;
bool islib, ispkg, isgo, stale, ispackcmd;
Buf b, b1, path, final_path, final_name, archive;
Vec compile, files, link, go, missing, clean, lib, extra;
Time ttarg, t;
int i, j, k, n, doclean, targ;
if(vflag) {
if(!streq(goos, gohostos) || !streq(goarch, gohostarch))
errprintf("%s (%s/%s)\n", dir, goos, goarch);
else
errprintf("%s\n", dir);
}
binit(&b);
binit(&b1);
binit(&path);
binit(&final_path);
binit(&final_name);
binit(&archive);
vinit(&compile);
vinit(&files);
vinit(&link);
vinit(&go);
vinit(&missing);
vinit(&clean);
vinit(&lib);
vinit(&extra);
func install(dir string) {
if vflag > 0 {
if goos != gohostos || goarch != gohostarch {
errprintf("%s (%s/%s)\n", dir, goos, goarch)
} else {
errprintf("%s\n", dir)
}
}
var clean []string
defer func() {
for _, name := range clean {
xremove(name)
}
}()
// path = full path to dir.
bpathf(&path, "%s/src/%s", goroot, dir);
bpathf(&final_path, "%s/src/%s", goroot_final, dir);
name = lastelem(dir);
path := pathf("%s/src/%s", goroot, dir)
name := filepath.Base(dir)
// set up gcc command line on first run.
if(gccargs.len == 0) {
bprintf(&b, "%s %s", defaultcc, defaultcflags);
splitfields(&gccargs, bstr(&b));
for(i=0; i<nelem(proto_gccargs); i++)
vadd(&gccargs, proto_gccargs[i]);
if(defaultcflags[0] == '\0') {
for(i=0; i<nelem(proto_gccargs2); i++)
vadd(&gccargs, proto_gccargs2[i]);
if gccargs == nil {
gccargs = splitfields(defaultcc + " " + defaultcflags)
gccargs = append(gccargs, proto_gccargs...)
if defaultcflags == "" {
gccargs = append(gccargs, proto_gccargs2...)
}
if(contains(gccargs.p[0], "clang")) {
if strings.Contains(gccargs[0], "clang") {
// disable ASCII art in clang errors, if possible
vadd(&gccargs, "-fno-caret-diagnostics");
gccargs = append(gccargs, "-fno-caret-diagnostics")
// clang is too smart about unused command-line arguments
vadd(&gccargs, "-Qunused-arguments");
gccargs = append(gccargs, "-Qunused-arguments")
}
// disable word wrapping in error messages
vadd(&gccargs, "-fmessage-length=0");
if(streq(gohostos, "darwin")) {
gccargs = append(gccargs, "-fmessage-length=0")
if gohostos == "darwin" {
// golang.org/issue/5261
vadd(&gccargs, "-mmacosx-version-min=10.6");
gccargs = append(gccargs, "-mmacosx-version-min=10.6")
}
}
if(ldargs.len == 0 && defaultldflags[0] != '\0') {
bprintf(&b, "%s", defaultldflags);
splitfields(&ldargs, bstr(&b));
if ldargs == nil && defaultldflags != "" {
ldargs = splitfields(defaultldflags)
}
islib = hasprefix(dir, "lib") || streq(dir, "cmd/gc");
ispkg = !islib && !hasprefix(dir, "cmd/");
isgo = ispkg || streq(dir, "cmd/go") || streq(dir, "cmd/cgo");
islib := strings.HasPrefix(dir, "lib") || dir == "cmd/gc"
ispkg := !islib && !strings.HasPrefix(dir, "cmd/")
isgo := ispkg || dir == "cmd/go" || dir == "cmd/cgo"
exe = "";
if(streq(gohostos, "windows"))
exe = ".exe";
exe := ""
if gohostos == "windows" {
exe = ".exe"
}
// Start final link command line.
// Note: code below knows that link.p[targ] is the target.
ispackcmd = 0;
if(islib) {
var (
link []string
targ int
ispackcmd bool
)
switch {
case islib:
// C library.
vadd(&link, "ar");
if(streq(gohostos, "plan9"))
vadd(&link, "rc");
else
vadd(&link, "rsc");
prefix = "";
if(!hasprefix(name, "lib"))
prefix = "lib";
targ = link.len;
vadd(&link, bpathf(&b, "%s/pkg/obj/%s_%s/%s%s.a", goroot, gohostos, gohostarch, prefix, name));
} else if(ispkg) {
prefix := ""
if !strings.HasPrefix(name, "lib") {
prefix = "lib"
}
link = []string{"ar", "rsc", pathf("%s/pkg/obj/%s_%s/%s%s.a", goroot, gohostos, gohostarch, prefix, name)}
if gohostos == "plan9" {
link[1] = "rc"
}
targ = len(link) - 1
case ispkg:
// Go library (package).
ispackcmd = 1;
vadd(&link, "pack"); // program name - unused here, but all the other cases record one
p = bprintf(&b, "%s/pkg/%s_%s/%s", goroot, goos, goarch, dir);
*xstrrchr(p, '/') = '\0';
xmkdirall(p);
targ = link.len;
vadd(&link, bpathf(&b, "%s/pkg/%s_%s/%s.a", goroot, goos, goarch, dir));
} else if(streq(dir, "cmd/go") || streq(dir, "cmd/cgo")) {
ispackcmd = true
link = []string{"pack", pathf("%s/pkg/%s_%s/%s.a", goroot, goos, goarch, dir)}
targ = len(link) - 1
xmkdirall(filepath.Dir(link[targ]))
case dir == "cmd/go" || dir == "cmd/cgo":
// Go command.
vadd(&link, bpathf(&b, "%s/%sl", tooldir, gochar));
vadd(&link, "-o");
elem = name;
if(streq(elem, "go"))
elem = "go_bootstrap";
targ = link.len;
vadd(&link, bpathf(&b, "%s/%s%s", tooldir, elem, exe));
} else {
elem := name
if elem == "go" {
elem = "go_bootstrap"
}
link = []string{fmt.Sprintf("%s/%sl", tooldir, gochar), "-o", pathf("%s/%s%s", tooldir, elem, exe)}
targ = len(link) - 1
default:
// C command. Use gccargs and ldargs.
if(streq(gohostos, "plan9")) {
vadd(&link, bprintf(&b, "%sl", gohostchar));
vadd(&link, "-o");
targ = link.len;
vadd(&link, bpathf(&b, "%s/%s", tooldir, name));
if gohostos == "plan9" {
link = []string{fmt.Sprintf("%sl", gohostchar), "-o", pathf("%s/%s", tooldir, name)}
targ = len(link) - 1
} else {
vcopy(&link, gccargs.p, gccargs.len);
vcopy(&link, ldargs.p, ldargs.len);
if(sflag)
vadd(&link, "-static");
vadd(&link, "-o");
targ = link.len;
vadd(&link, bpathf(&b, "%s/%s%s", tooldir, name, exe));
if(streq(gohostarch, "amd64"))
vadd(&link, "-m64");
else if(streq(gohostarch, "386"))
vadd(&link, "-m32");
link = append(link, gccargs...)
link = append(link, ldargs...)
if sflag {
link = append(link, "-static")
}
link = append(link, "-o", pathf("%s/%s%s", tooldir, name, exe))
targ = len(link) - 1
switch gohostarch {
case "amd64":
link = append(link, "-m64")
case "386":
link = append(link, "-m32")
}
}
}
ttarg = mtime(link.p[targ]);
ttarg := mtime(link[targ])
// Gather files that are sources for this target.
// Everything in that directory, and any target-specific
// additions.
xreaddir(&files, bstr(&path));
files := xreaddir(path)
// Remove files beginning with . or _,
// which are likely to be editor temporary files.
// This is the same heuristic build.ScanDir uses.
// There do exist real C files beginning with _,
// so limit that check to just Go files.
n = 0;
for(i=0; i<files.len; i++) {
p = files.p[i];
if(hasprefix(p, ".") || (hasprefix(p, "_") && hassuffix(p, ".go")))
xfree(p);
else
files.p[n++] = p;
}
files.len = n;
for(i=0; i<nelem(deptab); i++) {
if(streq(dir, deptab[i].prefix) ||
(hassuffix(deptab[i].prefix, "/") && hasprefix(dir, deptab[i].prefix))) {
for(j=0; (p=deptab[i].dep[j])!=nil; j++) {
breset(&b1);
bwritestr(&b1, p);
bsubst(&b1, "$GOROOT", goroot);
bsubst(&b1, "$GOOS", goos);
bsubst(&b1, "$GOARCH", goarch);
bsubst(&b1, "$GOHOSTOS", gohostos);
bsubst(&b1, "$GOHOSTARCH", gohostarch);
p = bstr(&b1);
if(hassuffix(p, ".a")) {
vadd(&lib, bpathf(&b, "%s", p));
continue;
}
if(hassuffix(p, "/*")) {
bpathf(&b, "%s/%s", bstr(&path), p);
b.len -= 2;
xreaddir(&extra, bstr(&b));
bprintf(&b, "%s", p);
b.len -= 2;
for(k=0; k<extra.len; k++)
vadd(&files, bpathf(&b1, "%s/%s", bstr(&b), extra.p[k]));
continue;
}
if(hasprefix(p, "-")) {
p++;
n = 0;
for(k=0; k<files.len; k++) {
if(hasprefix(files.p[k], p))
xfree(files.p[k]);
else
files.p[n++] = files.p[k];
files = filter(files, func(p string) bool {
return !strings.HasPrefix(p, ".") && (!strings.HasPrefix(p, "_") || !strings.HasSuffix(p, ".go"))
})
var libs []string
for _, dt := range deptab {
if dir == dt.prefix || strings.HasSuffix(dt.prefix, "/") && strings.HasPrefix(dir, dt.prefix) {
for _, p := range dt.dep {
p = os.ExpandEnv(p)
switch {
case strings.HasSuffix(p, ".a"):
libs = append(libs, p)
case strings.HasSuffix(p, "/*"):
dir := strings.TrimSuffix(p, "/*")
for _, name := range xreaddir(pathf("%s/%s", path, dir)) {
files = append(files, pathf("%s/%s", dir, name))
}
files.len = n;
continue;
case strings.HasPrefix(p, "-"):
files = filter(files, func(s string) bool {
return !strings.HasPrefix(s, p[1:])
})
default:
files = append(files, p)
}
vadd(&files, p);
}
}
}
vuniq(&files);
files = uniq(files)
// Convert to absolute paths.
for(i=0; i<files.len; i++) {
if(!isabs(files.p[i])) {
bpathf(&b, "%s/%s", bstr(&path), files.p[i]);
xfree(files.p[i]);
files.p[i] = btake(&b);
for i, p := range files {
if !isabs(p) {
files[i] = pathf("%s/%s", path, p)
}
}
// Is the target up-to-date?
stale = rebuildall;
n = 0;
for(i=0; i<files.len; i++) {
p = files.p[i];
for(j=0; j<nelem(depsuffix); j++)
if(hassuffix(p, depsuffix[j]))
goto ok;
xfree(files.p[i]);
continue;
var gofiles, missing []string
stale := rebuildall
files = filter(files, func(p string) bool {
for _, suf := range depsuffix {
if strings.HasSuffix(p, suf) {
goto ok
}
}
return false
ok:
t = mtime(p);
if(t != 0 && !hassuffix(p, ".a") && !shouldbuild(p, dir)) {
xfree(files.p[i]);
continue;
t := mtime(p)
if !t.IsZero() && !strings.HasSuffix(p, ".a") && !shouldbuild(p, dir) {
return false
}
if(hassuffix(p, ".go"))
vadd(&go, p);
if(t > ttarg)
stale = 1;
if(t == 0) {
vadd(&missing, p);
files.p[n++] = files.p[i];
continue;
if strings.HasSuffix(p, ".go") {
gofiles = append(gofiles, p)
}
files.p[n++] = files.p[i];
}
files.len = n;
if t.After(ttarg) {
stale = true
}
if t.IsZero() {
missing = append(missing, p)
}
return true
})
// If there are no files to compile, we're done.
if(files.len == 0)
goto out;
for(i=0; i<lib.len && !stale; i++)
if(mtime(lib.p[i]) > ttarg)
stale = 1;
if len(files) == 0 {
return
}
if(!stale)
goto out;
if !stale {
for _, p := range libs {
if mtime(p).After(ttarg) {
stale = true
break
}
}
}
if !stale {
return
}
// For package runtime, copy some files into the work space.
if(streq(dir, "runtime")) {
copyfile(bpathf(&b, "%s/pkg/%s_%s/textflag.h", goroot, goos, goarch),
bpathf(&b1, "%s/src/cmd/ld/textflag.h", goroot), 0);
copyfile(bpathf(&b, "%s/pkg/%s_%s/funcdata.h", goroot, goos, goarch),
bpathf(&b1, "%s/src/runtime/funcdata.h", goroot), 0);
if dir == "runtime" {
// For use by assembly and C files.
copyfile(pathf("%s/pkg/%s_%s/textflag.h", goroot, goos, goarch),
pathf("%s/src/cmd/ld/textflag.h", goroot), 0)
copyfile(pathf("%s/pkg/%s_%s/funcdata.h", goroot, goos, goarch),
pathf("%s/src/runtime/funcdata.h", goroot), 0)
}
// Generate any missing files; regenerate existing ones.
for(i=0; i<files.len; i++) {
p = files.p[i];
elem = lastelem(p);
for(j=0; j<nelem(gentab); j++) {
if(gentab[j].gen == nil)
continue;
if(hasprefix(elem, gentab[j].nameprefix)) {
if(vflag > 1)
errprintf("generate %s\n", p);
gentab[j].gen(bstr(&path), p);
for _, p := range files {
elem := filepath.Base(p)
for _, gt := range gentab {
if gt.gen == nil {
continue
}
if strings.HasPrefix(elem, gt.nameprefix) {
if vflag > 1 {
errprintf("generate %s\n", p)
}
gt.gen(path, p)
// Do not add generated file to clean list.
// In runtime, we want to be able to
// build the package with the go tool,
......@@ -907,222 +844,173 @@ install(char *dir)
// exist (it does not know how to build them).
// The 'clean' command can remove
// the generated files.
goto built;
goto built
}
}
// Did not rebuild p.
if(find(p, missing.p, missing.len) >= 0)
fatal("missing file %s", p);
built:;
if find(p, missing) >= 0 {
fatal("missing file %s", p)
}
built:
}
if((!streq(goos, gohostos) || !streq(goarch, gohostarch)) && isgo) {
if (goos != gohostos || goarch != gohostarch) && isgo {
// We've generated the right files; the go command can do the build.
if(vflag > 1)
errprintf("skip build for cross-compile %s\n", dir);
goto nobuild;
if vflag > 1 {
errprintf("skip build for cross-compile %s\n", dir)
}
return
}
if(isgo) {
var archive string
if isgo {
// The next loop will compile individual non-Go files.
// Hand the Go files to the compiler en masse.
// For package runtime, this writes go_asm.h, which
// the assembly files will need.
vreset(&compile);
vadd(&compile, bpathf(&b, "%s/%sg", tooldir, gochar));
bpathf(&b, "%s/_go_.a", workdir);
vadd(&compile, "-pack");
vadd(&compile, "-o");
vadd(&compile, bstr(&b));
vadd(&clean, bstr(&b));
if(!ispackcmd)
vadd(&link, bstr(&b));
else
bwriteb(&archive, &b);
vadd(&compile, "-p");
if(hasprefix(dir, "cmd/"))
vadd(&compile, "main");
else
vadd(&compile, dir);
if(streq(dir, "runtime")) {
vadd(&compile, "-+");
vadd(&compile, "-asmhdr");
bpathf(&b1, "%s/go_asm.h", workdir);
vadd(&compile, bstr(&b1));
pkg := dir
if strings.HasPrefix(dir, "cmd/") {
pkg = "main"
}
vcopy(&compile, go.p, go.len);
runv(nil, bstr(&path), CheckExit, &compile);
b := pathf("%s/_go_.a", workdir)
clean = append(clean, b)
if !ispackcmd {
link = append(link, b)
} else {
archive = b
}
compile := []string{pathf("%s/%sg", tooldir, gochar), "-pack", "-o", b, "-p", pkg}
if dir == "runtime" {
compile = append(compile, "-+", "-asmhdr", pathf("%s/go_asm.h", workdir))
}
compile = append(compile, gofiles...)
run(path, CheckExit|ShowOutput, compile...)
}
// Compile the files.
for(i=0; i<files.len; i++) {
if(!hassuffix(files.p[i], ".c") && !hassuffix(files.p[i], ".s"))
continue;
name = lastelem(files.p[i]);
for _, p := range files {
if !strings.HasSuffix(p, ".c") && !strings.HasSuffix(p, ".s") {
continue
}
name := filepath.Base(p)
vreset(&compile);
if(!isgo) {
var compile []string
if !isgo {
// C library or tool.
if(streq(gohostos, "plan9")) {
vadd(&compile, bprintf(&b, "%sc", gohostchar));
vadd(&compile, "-FTVwp");
vadd(&compile, "-DPLAN9");
vadd(&compile, "-D__STDC__=1");
vadd(&compile, "-D__SIZE_TYPE__=ulong"); // for GNU Bison
vadd(&compile, bpathf(&b, "-I%s/include/plan9", goroot));
vadd(&compile, bpathf(&b, "-I%s/include/plan9/%s", goroot, gohostarch));
if gohostos == "plan9" {
compile = []string{
gohostchar + "c", "-FTVwp",
"-DPLAN9",
"-D__STDC__=1",
"-D__SIZE_TYPE__=ulong", // for GNU bison
pathf("-I%s/include/plan9", goroot),
pathf("-I%s/include/plan9/%s", goroot, gohostarch),
}
} else {
vcopy(&compile, gccargs.p, gccargs.len);
vadd(&compile, "-c");
if(streq(gohostarch, "amd64"))
vadd(&compile, "-m64");
else if(streq(gohostarch, "386"))
vadd(&compile, "-m32");
vadd(&compile, "-I");
vadd(&compile, bpathf(&b, "%s/include", goroot));
compile = gccargs[0:len(gccargs):len(gccargs)]
compile = append(compile, "-c")
switch gohostarch {
case "amd64":
compile = append(compile, "-m64")
case "386":
compile = append(compile, "-m32")
}
compile = append(compile, "-I", pathf("%s/include", goroot))
}
if(streq(dir, "lib9"))
vadd(&compile, "-DPLAN9PORT");
if dir == "lib9" {
compile = append(compile, "-DPLAN9PORT")
}
vadd(&compile, "-I");
vadd(&compile, bstr(&path));
compile = append(compile, "-I", path)
// lib9/goos.c gets the default constants hard-coded.
if(streq(name, "goos.c")) {
vadd(&compile, "-D");
vadd(&compile, bprintf(&b, "GOOS=\"%s\"", goos));
vadd(&compile, "-D");
vadd(&compile, bprintf(&b, "GOARCH=\"%s\"", goarch));
bprintf(&b1, "%s", goroot_final);
bsubst(&b1, "\\", "\\\\"); // turn into C string
vadd(&compile, "-D");
vadd(&compile, bprintf(&b, "GOROOT=\"%s\"", bstr(&b1)));
vadd(&compile, "-D");
vadd(&compile, bprintf(&b, "GOVERSION=\"%s\"", goversion));
vadd(&compile, "-D");
vadd(&compile, bprintf(&b, "GOARM=\"%s\"", goarm));
vadd(&compile, "-D");
vadd(&compile, bprintf(&b, "GO386=\"%s\"", go386));
vadd(&compile, "-D");
vadd(&compile, bprintf(&b, "GO_EXTLINK_ENABLED=\"%s\"", goextlinkenabled));
if name == "goos.c" {
compile = append(compile,
"-D", fmt.Sprintf("GOOS=%q", goos),
"-D", fmt.Sprintf("GOARCH=%q", goarch),
"-D", fmt.Sprintf("GOROOT=%q", goroot_final),
"-D", fmt.Sprintf("GOVERSION=%q", goversion),
"-D", fmt.Sprintf("GOARM=%q", goarm),
"-D", fmt.Sprintf("GO386=%q", go386),
"-D", fmt.Sprintf("GO_EXTLINK_ENABLED=%q", goextlinkenabled),
)
}
// gc/lex.c records the GOEXPERIMENT setting used during the build.
if(streq(name, "lex.c")) {
xgetenv(&b, "GOEXPERIMENT");
vadd(&compile, "-D");
vadd(&compile, bprintf(&b1, "GOEXPERIMENT=\"%s\"", bstr(&b)));
if name == "lex.c" {
compile = append(compile,
"-D", fmt.Sprintf("GOEXPERIMENT=%q", os.Getenv("GOEXPERIMENT")))
}
} else {
// Supporting files for a Go package.
if(hassuffix(files.p[i], ".s"))
vadd(&compile, bpathf(&b, "%s/%sa", tooldir, gochar));
else {
vadd(&compile, bpathf(&b, "%s/%sc", tooldir, gochar));
vadd(&compile, "-F");
vadd(&compile, "-V");
vadd(&compile, "-w");
// Assembly file for a Go package.
compile = []string{
pathf("%s/%sa", tooldir, gochar),
"-I", workdir,
"-I", pathf("%s/pkg/%s_%s", goroot, goos, goarch),
"-D", "GOOS_" + goos,
"-D", "GOARCH_" + goarch,
"-D", "GOOS_GOARCH_" + goos + "_" + goarch,
}
vadd(&compile, "-I");
vadd(&compile, workdir);
vadd(&compile, "-I");
vadd(&compile, bprintf(&b, "%s/pkg/%s_%s", goroot, goos, goarch));
vadd(&compile, "-D");
vadd(&compile, bprintf(&b, "GOOS_%s", goos));
vadd(&compile, "-D");
vadd(&compile, bprintf(&b, "GOARCH_%s", goarch));
vadd(&compile, "-D");
vadd(&compile, bprintf(&b, "GOOS_GOARCH_%s_%s", goos, goarch));
}
bpathf(&b, "%s/%s", workdir, lastelem(files.p[i]));
doclean = 1;
if(!isgo && streq(gohostos, "darwin")) {
doclean := true
b := pathf("%s/%s", workdir, filepath.Base(p))
if !isgo && gohostos == "darwin" {
// To debug C programs on OS X, it is not enough to say -ggdb
// on the command line. You have to leave the object files
// lying around too. Leave them in pkg/obj/, which does not
// get removed when this tool exits.
bpathf(&b1, "%s/pkg/obj/%s", goroot, dir);
xmkdirall(bstr(&b1));
bpathf(&b, "%s/%s", bstr(&b1), lastelem(files.p[i]));
doclean = 0;
obj := pathf("%s/pkg/obj/%s", goroot, dir)
xmkdirall(obj)
b = pathf("%s/%s", obj, filepath.Base(p))
doclean = false
}
// Change the last character of the output file (which was c or s).
if(streq(gohostos, "plan9"))
b.p[b.len-1] = gohostchar[0];
else
b.p[b.len-1] = 'o';
vadd(&compile, "-o");
vadd(&compile, bstr(&b));
vadd(&compile, files.p[i]);
bgrunv(bstr(&path), CheckExit, &compile);
if gohostos == "plan9" {
b = b[:len(b)-1] + gohostchar
} else {
b = b[:len(b)-1] + "o"
}
compile = append(compile, "-o", b, p)
bgrun(path, compile...)
vadd(&link, bstr(&b));
if(doclean)
vadd(&clean, bstr(&b));
link = append(link, b)
if doclean {
clean = append(clean, b)
}
}
bgwait();
bgwait()
if(isgo && ispackcmd) {
xremove(link.p[targ]);
dopack(link.p[targ], bstr(&archive), &link.p[targ+1], link.len - (targ+1));
goto nobuild;
if isgo && ispackcmd {
xremove(link[targ])
dopack(link[targ], archive, link[targ+1:])
return
}
if(!islib && !isgo) {
if !islib && !isgo {
// C binaries need the libraries explicitly, and -lm.
vcopy(&link, lib.p, lib.len);
if(!streq(gohostos, "plan9"))
vadd(&link, "-lm");
link = append(link, libs...)
if gohostos != "plan9" {
link = append(link, "-lm")
}
}
// Remove target before writing it.
xremove(link.p[targ]);
runv(nil, nil, CheckExit, &link);
nobuild:
out:
for(i=0; i<clean.len; i++)
xremove(clean.p[i]);
bfree(&b);
bfree(&b1);
bfree(&path);
bfree(&archive);
vfree(&compile);
vfree(&files);
vfree(&link);
vfree(&go);
vfree(&missing);
vfree(&clean);
vfree(&lib);
vfree(&extra);
xremove(link[targ])
run("", CheckExit|ShowOutput, link...)
}
// matchfield reports whether the field matches this build.
static bool
matchfield(char *f)
{
char *p;
bool res;
p = xstrrchr(f, ',');
if(p == nil)
return streq(f, goos) || streq(f, goarch) || streq(f, "cmd_go_bootstrap") || streq(f, "go1.1") || (streq(goos, "android") && streq(f, "linux"));
*p = 0;
res = matchfield(f) && matchfield(p+1);
*p = ',';
return res;
func matchfield(f string) bool {
for _, tag := range strings.Split(f, ",") {
if tag == goos || tag == goarch || tag == "cmd_go_bootstrap" || tag == "go1.1" || (goos == "android" && tag == "linux") {
continue
}
return false
}
return true
}
// shouldbuild reports whether we should build this file.
......@@ -1132,162 +1020,117 @@ matchfield(char *f)
// In particular, they can be the entire file name (like windows.c).
// We also allow the special tag cmd_go_bootstrap.
// See ../go/bootstrap.go and package go/build.
static bool
shouldbuild(char *file, char *dir)
{
char *name, *p;
int i, j, ret;
Buf b;
Vec lines, fields;
func shouldbuild(file, dir string) bool {
// Check file name for GOOS or GOARCH.
name = lastelem(file);
for(i=0; i<nelem(okgoos); i++) {
if(streq(okgoos[i], goos))
continue;
p = xstrstr(name, okgoos[i]);
if(p == nil)
continue;
p += xstrlen(okgoos[i]);
if(*p == '.' || *p == '_' || *p == '\0')
return 0;
}
for(i=0; i<nelem(okgoarch); i++) {
if(streq(okgoarch[i], goarch))
continue;
p = xstrstr(name, okgoarch[i]);
if(p == nil)
continue;
p += xstrlen(okgoarch[i]);
if(*p == '.' || *p == '_' || *p == '\0')
return 0;
name := filepath.Base(file)
excluded := func(list []string, ok string) bool {
for _, x := range list {
if x == ok {
continue
}
i := strings.Index(name, x)
if i < 0 {
continue
}
i += len(x)
if i == len(name) || name[i] == '.' || name[i] == '_' {
return true
}
}
return false
}
if excluded(okgoos, goos) || excluded(okgoarch, goarch) {
return false
}
// Omit test files.
if(contains(name, "_test"))
return 0;
if strings.Contains(name, "_test") {
return false
}
// cmd/go/doc.go has a giant /* */ comment before
// it gets to the important detail that it is not part of
// package main. We don't parse those comments,
// so special case that file.
if(hassuffix(file, "cmd/go/doc.go") || hassuffix(file, "cmd\\go\\doc.go"))
return 0;
if(hassuffix(file, "cmd/cgo/doc.go") || hassuffix(file, "cmd\\cgo\\doc.go"))
return 0;
if strings.HasSuffix(file, "cmd/go/doc.go") || strings.HasSuffix(file, "cmd\\go\\doc.go") {
return false
}
if strings.HasSuffix(file, "cmd/cgo/doc.go") || strings.HasSuffix(file, "cmd\\cgo\\doc.go") {
return false
}
// Check file contents for // +build lines.
binit(&b);
vinit(&lines);
vinit(&fields);
ret = 1;
readfile(&b, file);
splitlines(&lines, bstr(&b));
for(i=0; i<lines.len; i++) {
p = lines.p[i];
while(*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n')
p++;
if(*p == '\0')
continue;
if(contains(p, "package documentation")) {
ret = 0;
goto out;
for _, p := range splitlines(readfile(file)) {
p = strings.TrimSpace(p)
if p == "" {
continue
}
if strings.Contains(p, "package documentation") {
return false
}
if(contains(p, "package main") && !streq(dir, "cmd/go") && !streq(dir, "cmd/cgo")) {
ret = 0;
goto out;
if strings.Contains(p, "package main") && dir != "cmd/go" && dir != "cmd/cgo" {
return false
}
if(!hasprefix(p, "//"))
break;
if(!contains(p, "+build"))
continue;
splitfields(&fields, lines.p[i]);
if(fields.len < 2 || !streq(fields.p[1], "+build"))
continue;
for(j=2; j<fields.len; j++) {
p = fields.p[j];
if((*p == '!' && !matchfield(p+1)) || matchfield(p))
goto fieldmatch;
if !strings.HasPrefix(p, "//") {
break
}
ret = 0;
goto out;
fieldmatch:;
if !strings.Contains(p, "+build") {
continue
}
fields := splitfields(p)
if len(fields) < 2 || fields[1] != "+build" {
continue
}
for _, p := range fields[2:] {
if (p[0] == '!' && !matchfield(p[1:])) || matchfield(p) {
goto fieldmatch
}
}
return false
fieldmatch:
}
out:
bfree(&b);
vfree(&lines);
vfree(&fields);
return ret;
return true
}
// copy copies the file src to dst, via memory (so only good for small files).
void
copyfile(char *dst, char *src, int exec)
{
Buf b;
if(vflag > 1)
errprintf("cp %s %s\n", src, dst);
binit(&b);
readfile(&b, src);
writefile(&b, dst, exec);
bfree(&b);
func copyfile(dst, src string, exec int) {
if vflag > 1 {
errprintf("cp %s %s\n", src, dst)
}
writefile(readfile(src), dst, exec)
}
// dopack copies the package src to dst,
// appending the files listed in extra.
// The archive format is the traditional Unix ar format.
static void
dopack(char *dst, char *src, char **extra, int nextra)
{
int i;
char c, *p, *q;
Buf b, bdst;
binit(&b);
binit(&bdst);
readfile(&bdst, src);
for(i=0; i<nextra; i++) {
readfile(&b, extra[i]);
func dopack(dst, src string, extra []string) {
bdst := bytes.NewBufferString(readfile(src))
for _, file := range extra {
b := readfile(file)
// find last path element for archive member name
p = xstrrchr(extra[i], '/');
if(p)
p++;
q = xstrrchr(extra[i], '\\');
if(q) {
q++;
if(p == nil || q > p)
p = q;
i := strings.LastIndex(file, "/") + 1
j := strings.LastIndex(file, `\`) + 1
if i < j {
i = j
}
if(p == nil)
p = extra[i];
bwritef(&bdst, "%-16.16s%-12d%-6d%-6d%-8o%-10d`\n", p, 0, 0, 0, 0644, b.len);
bwriteb(&bdst, &b);
if(b.len&1) {
c = 0;
bwrite(&bdst, &c, 1);
fmt.Fprintf(bdst, "%-16.16s%-12d%-6d%-6d%-8o%-10d`\n", file[i:], 0, 0, 0, 0644, len(b))
bdst.WriteString(b)
if len(b)&1 != 0 {
bdst.WriteByte(0)
}
}
writefile(&bdst, dst, 0);
bfree(&b);
bfree(&bdst);
writefile(bdst.String(), dst, 0)
}
// buildorder records the order of builds for the 'go bootstrap' command.
static char *buildorder[] = {
var buildorder = []string{
"lib9",
"libbio",
"liblink",
"cmd/gc", // must be before g
"cmd/%sl", // must be before a, g
"cmd/%sl", // must be before a, g
"cmd/%sa",
"cmd/%sg",
......@@ -1337,12 +1180,12 @@ static char *buildorder[] = {
"go/doc",
"go/build",
"cmd/go",
};
}
// cleantab records the directories to clean in 'go clean'.
// It is bigger than the buildorder because we clean all the
// compilers but build only the $GOARCH ones.
static char *cleantab[] = {
var cleantab = []string{
// Commands and C libraries.
"cmd/5a",
"cmd/5g",
......@@ -1357,7 +1200,7 @@ static char *cleantab[] = {
"cmd/9g",
"cmd/9l",
"cmd/gc",
"cmd/go",
"cmd/go",
"lib9",
"libbio",
"liblink",
......@@ -1403,383 +1246,246 @@ static char *cleantab[] = {
"unicode",
"unicode/utf16",
"unicode/utf8",
};
}
static char *runtimegen[] = {
var runtimegen = []string{
"zaexperiment.h",
"zversion.go",
};
static void
clean(void)
{
int i, j, k;
Buf b, path;
Vec dir;
binit(&b);
binit(&path);
vinit(&dir);
for(i=0; i<nelem(cleantab); i++) {
bpathf(&path, "%s/src/%s", goroot, cleantab[i]);
xreaddir(&dir, bstr(&path));
}
func clean() {
for _, name := range cleantab {
path := pathf("%s/src/%s", goroot, name)
// Remove generated files.
for(j=0; j<dir.len; j++) {
for(k=0; k<nelem(gentab); k++) {
if(hasprefix(dir.p[j], gentab[k].nameprefix))
xremove(bpathf(&b, "%s/%s", bstr(&path), dir.p[j]));
for _, elem := range xreaddir(path) {
for _, gt := range gentab {
if strings.HasPrefix(elem, gt.nameprefix) {
xremove(pathf("%s/%s", path, elem))
}
}
}
// Remove generated binary named for directory.
if(hasprefix(cleantab[i], "cmd/"))
xremove(bpathf(&b, "%s/%s", bstr(&path), cleantab[i]+4));
if strings.HasPrefix(name, "cmd/") {
xremove(pathf("%s/%s", path, name[4:]))
}
}
// remove src/runtime/zaexperiment.h and
// except leave zgoos and zgoarch, now maintained with go generate.
bpathf(&path, "%s/src/runtime", goroot);
for(j=0; j<nelem(runtimegen); j++)
xremove(bpathf(&b, "%s/%s", bstr(&path), runtimegen[j]));
// remove runtimegen files.
path := pathf("%s/src/runtime", goroot)
for _, elem := range runtimegen {
xremove(pathf("%s/%s", path, elem))
}
if(rebuildall) {
if rebuildall {
// Remove object tree.
xremoveall(bpathf(&b, "%s/pkg/obj/%s_%s", goroot, gohostos, gohostarch));
xremoveall(pathf("%s/pkg/obj/%s_%s", goroot, gohostos, gohostarch))
// Remove installed packages and tools.
xremoveall(bpathf(&b, "%s/pkg/%s_%s", goroot, gohostos, gohostarch));
xremoveall(bpathf(&b, "%s/pkg/%s_%s", goroot, goos, goarch));
xremoveall(tooldir);
xremoveall(pathf("%s/pkg/%s_%s", goroot, gohostos, gohostarch))
xremoveall(pathf("%s/pkg/%s_%s", goroot, goos, goarch))
xremoveall(tooldir)
// Remove cached version info.
xremove(bpathf(&b, "%s/VERSION.cache", goroot));
xremove(pathf("%s/VERSION.cache", goroot))
}
bfree(&b);
bfree(&path);
vfree(&dir);
}
/*
* command implementations
*/
void
usage(void)
{
xprintf("usage: go tool dist [command]\n"
"Commands are:\n"
"\n"
"banner print installation banner\n"
"bootstrap rebuild everything\n"
"clean deletes all built files\n"
"env [-p] print environment (-p: include $PATH)\n"
"install [dir] install individual directory\n"
"version print Go version\n"
"\n"
"All commands take -v flags to emit extra information.\n"
);
xexit(2);
func usage() {
xprintf("usage: go tool dist [command]\n" +
"Commands are:\n" +
"\n" +
"banner print installation banner\n" +
"bootstrap rebuild everything\n" +
"clean deletes all built files\n" +
"env [-p] print environment (-p: include $PATH)\n" +
"install [dir] install individual directory\n" +
"version print Go version\n" +
"\n" +
"All commands take -v flags to emit extra information.\n",
)
xexit(2)
}
// The env command prints the default environment.
void
cmdenv(int argc, char **argv)
{
bool pflag;
char *sep;
Buf b, b1;
char *format;
binit(&b);
binit(&b1);
format = "%s=\"%s\"\n";
pflag = 0;
ARGBEGIN{
case '9':
format = "%s='%s'\n";
break;
case 'p':
pflag = 1;
break;
case 'v':
vflag++;
break;
case 'w':
format = "set %s=%s\r\n";
break;
default:
usage();
}ARGEND
if(argc > 0)
usage();
xprintf(format, "CC", defaultcc);
xprintf(format, "CC_FOR_TARGET", defaultcctarget);
xprintf(format, "GOROOT", goroot);
xprintf(format, "GOBIN", gobin);
xprintf(format, "GOARCH", goarch);
xprintf(format, "GOOS", goos);
xprintf(format, "GOHOSTARCH", gohostarch);
xprintf(format, "GOHOSTOS", gohostos);
xprintf(format, "GOTOOLDIR", tooldir);
xprintf(format, "GOCHAR", gochar);
if(streq(goarch, "arm"))
xprintf(format, "GOARM", goarm);
if(streq(goarch, "386"))
xprintf(format, "GO386", go386);
if(pflag) {
sep = ":";
if(streq(gohostos, "windows"))
sep = ";";
xgetenv(&b, "PATH");
bprintf(&b1, "%s%s%s", gobin, sep, bstr(&b));
xprintf(format, "PATH", bstr(&b1));
}
bfree(&b);
bfree(&b1);
func cmdenv() {
path := flag.Bool("p", false, "emit updated PATH")
plan9 := flag.Bool("9", false, "emit plan 9 syntax")
windows := flag.Bool("w", false, "emit windows syntax")
xflagparse(0)
format := "%s=\"%s\"\n"
switch {
case *plan9:
format = "%s='%s'\n"
case *windows:
format = "set %s=%s\r\n"
}
xprintf(format, "CC", defaultcc)
xprintf(format, "CC_FOR_TARGET", defaultcctarget)
xprintf(format, "GOROOT", goroot)
xprintf(format, "GOBIN", gobin)
xprintf(format, "GOARCH", goarch)
xprintf(format, "GOOS", goos)
xprintf(format, "GOHOSTARCH", gohostarch)
xprintf(format, "GOHOSTOS", gohostos)
xprintf(format, "GOTOOLDIR", tooldir)
xprintf(format, "GOCHAR", gochar)
if goarch == "arm" {
xprintf(format, "GOARM", goarm)
}
if goarch == "386" {
xprintf(format, "GO386", go386)
}
if *path {
sep := ":"
if gohostos == "windows" {
sep = ";"
}
xprintf(format, "PATH", fmt.Sprintf("%s%s%s", gobin, sep, os.Getenv("PATH")))
}
}
// The bootstrap command runs a build from scratch,
// stopping at having installed the go_bootstrap command.
void
cmdbootstrap(int argc, char **argv)
{
int i;
Buf b;
char *oldgoos, *oldgoarch, *oldgochar;
binit(&b);
ARGBEGIN{
case 'a':
rebuildall = 1;
break;
case 's':
sflag++;
break;
case 'v':
vflag++;
break;
default:
usage();
}ARGEND
if(argc > 0)
usage();
func cmdbootstrap() {
flag.BoolVar(&rebuildall, "a", rebuildall, "rebuild all")
flag.BoolVar(&sflag, "s", sflag, "build static binaries")
xflagparse(0)
if isdir(pathf("%s/src/pkg", goroot)) {
fatal("\n\n"+
"The Go package sources have moved to $GOROOT/src.\n"+
"*** %s still exists. ***\n"+
"It probably contains stale files that may confuse the build.\n"+
"Please (check what's there and) remove it and try again.\n"+
"See http://golang.org/s/go14nopkg\n",
pathf("%s/src/pkg", goroot))
}
if(isdir(bpathf(&b, "%s/src/pkg", goroot))) {
fatal("\n\n"
"The Go package sources have moved to $GOROOT/src.\n"
"*** %s still exists. ***\n"
"It probably contains stale files that may confuse the build.\n"
"Please (check what's there and) remove it and try again.\n"
"See http://golang.org/s/go14nopkg\n", bpathf(&b, "%s/src/pkg", goroot));
if rebuildall {
clean()
}
if(rebuildall)
clean();
goversion = findgoversion();
setup();
xsetenv("GOROOT", goroot);
xsetenv("GOROOT_FINAL", goroot_final);
setup()
// For the main bootstrap, building for host os/arch.
oldgoos = goos;
oldgoarch = goarch;
oldgochar = gochar;
goos = gohostos;
goarch = gohostarch;
gochar = gohostchar;
xsetenv("GOARCH", goarch);
xsetenv("GOOS", goos);
for(i=0; i<nelem(buildorder); i++) {
install(bprintf(&b, buildorder[i], gohostchar));
if(!streq(oldgochar, gohostchar) && xstrstr(buildorder[i], "%s"))
install(bprintf(&b, buildorder[i], oldgochar));
}
goos = oldgoos;
goarch = oldgoarch;
gochar = oldgochar;
xsetenv("GOARCH", goarch);
xsetenv("GOOS", goos);
oldgoos = goos
oldgoarch = goarch
oldgochar = gochar
goos = gohostos
goarch = gohostarch
gochar = gohostchar
os.Setenv("GOHOSTARCH", gohostarch)
os.Setenv("GOHOSTOS", gohostos)
os.Setenv("GOARCH", goarch)
os.Setenv("GOOS", goos)
for _, pattern := range buildorder {
dir := pattern
if strings.Contains(pattern, "%s") {
dir = fmt.Sprintf(pattern, gohostchar)
}
install(dir)
if oldgochar != gohostchar && strings.Contains(pattern, "%s") {
install(fmt.Sprintf(pattern, oldgochar))
}
}
// Build runtime for actual goos/goarch too.
if(!streq(goos, gohostos) || !streq(goarch, gohostarch))
install("runtime");
goos = oldgoos
goarch = oldgoarch
gochar = oldgochar
os.Setenv("GOARCH", goarch)
os.Setenv("GOOS", goos)
bfree(&b);
// Build runtime for actual goos/goarch too.
if goos != gohostos || goarch != gohostarch {
install("runtime")
}
}
static char*
defaulttarg(void)
{
char *p;
Buf pwd, src, real_src;
binit(&pwd);
binit(&src);
binit(&real_src);
func defaulttarg() string {
// xgetwd might return a path with symlinks fully resolved, and if
// there happens to be symlinks in goroot, then the hasprefix test
// will never succeed. Instead, we use xrealwd to get a canonical
// goroot/src before the comparison to avoid this problem.
xgetwd(&pwd);
p = btake(&pwd);
bpathf(&src, "%s/src/", goroot);
xrealwd(&real_src, bstr(&src));
if(!hasprefix(p, bstr(&real_src)))
fatal("current directory %s is not under %s", p, bstr(&real_src));
p += real_src.len;
pwd := xgetwd()
src := pathf("%s/src/", goroot)
real_src := xrealwd(src)
if !strings.HasPrefix(pwd, real_src) {
fatal("current directory %s is not under %s", pwd, real_src)
}
pwd = pwd[len(real_src):]
// guard againt xrealwd return the directory without the trailing /
if(*p == slash[0])
p++;
bfree(&pwd);
bfree(&src);
bfree(&real_src);
pwd = strings.TrimPrefix(pwd, "/")
return p;
return pwd
}
// Install installs the list of packages named on the command line.
void
cmdinstall(int argc, char **argv)
{
int i;
ARGBEGIN{
case 's':
sflag++;
break;
case 'v':
vflag++;
break;
default:
usage();
}ARGEND
func cmdinstall() {
flag.BoolVar(&sflag, "s", sflag, "build static binaries")
xflagparse(-1)
if(argc == 0)
install(defaulttarg());
if flag.NArg() == 0 {
install(defaulttarg())
}
for(i=0; i<argc; i++)
install(argv[i]);
for _, arg := range flag.Args() {
install(arg)
}
}
// Clean deletes temporary objects.
// Clean -i deletes the installed objects too.
void
cmdclean(int argc, char **argv)
{
ARGBEGIN{
case 'v':
vflag++;
break;
default:
usage();
}ARGEND
if(argc > 0)
usage();
clean();
func cmdclean() {
xflagparse(0)
clean()
}
// Banner prints the 'now you've installed Go' banner.
void
cmdbanner(int argc, char **argv)
{
char *pathsep, *pid, *ns;
Buf b, b1, search, path;
ARGBEGIN{
case 'v':
vflag++;
break;
default:
usage();
}ARGEND
if(argc > 0)
usage();
binit(&b);
binit(&b1);
binit(&search);
binit(&path);
func cmdbanner() {
xflagparse(0)
xprintf("\n");
xprintf("---\n");
xprintf("Installed Go for %s/%s in %s\n", goos, goarch, goroot);
xprintf("Installed commands in %s\n", gobin);
xprintf("\n")
xprintf("---\n")
xprintf("Installed Go for %s/%s in %s\n", goos, goarch, goroot)
xprintf("Installed commands in %s\n", gobin)
if(!xsamefile(goroot_final, goroot)) {
if !xsamefile(goroot_final, goroot) {
// If the files are to be moved, don't check that gobin
// is on PATH; assume they know what they are doing.
} else if(streq(gohostos, "plan9")) {
} else if gohostos == "plan9" {
// Check that gobin is bound before /bin.
readfile(&b, "#c/pid");
bsubst(&b, " ", "");
pid = btake(&b);
bprintf(&b, "/proc/%s/ns", pid);
ns = btake(&b);
readfile(&b, ns);
bprintf(&search, "bind -b %s /bin\n", gobin);
if(xstrstr(bstr(&b), bstr(&search)) == nil)
xprintf("*** You need to bind %s before /bin.\n", gobin);
pid := strings.Replace(readfile("#c/pid"), " ", "", -1)
ns := fmt.Sprintf("/proc/%s/ns", pid)
if !strings.Contains(readfile(ns), fmt.Sprintf("bind -b %s /bin", gobin)) {
xprintf("*** You need to bind %s before /bin.\n", gobin)
}
} else {
// Check that gobin appears in $PATH.
xgetenv(&b, "PATH");
pathsep = ":";
if(streq(gohostos, "windows"))
pathsep = ";";
bprintf(&b1, "%s%s%s", pathsep, bstr(&b), pathsep);
bprintf(&search, "%s%s%s", pathsep, gobin, pathsep);
if(xstrstr(bstr(&b1), bstr(&search)) == nil)
xprintf("*** You need to add %s to your PATH.\n", gobin);
}
if(streq(gohostos, "darwin")) {
if(isfile(bpathf(&path, "%s/cov", tooldir)))
xprintf("\n"
"On OS X the debuggers must be installed setgid procmod.\n"
"Read and run ./sudo.bash to install the debuggers.\n");
pathsep := ":"
if gohostos == "windows" {
pathsep = ";"
}
if !strings.Contains(pathsep+os.Getenv("PATH")+pathsep, pathsep+gobin+pathsep) {
xprintf("*** You need to add %s to your PATH.\n", gobin)
}
}
if(!xsamefile(goroot_final, goroot)) {
xprintf("\n"
if !xsamefile(goroot_final, goroot) {
xprintf("\n"+
"The binaries expect %s to be copied or moved to %s\n",
goroot, goroot_final);
goroot, goroot_final)
}
bfree(&b);
bfree(&b1);
bfree(&search);
bfree(&path);
}
// Version prints the Go version.
void
cmdversion(int argc, char **argv)
{
ARGBEGIN{
case 'v':
vflag++;
break;
default:
usage();
}ARGEND
if(argc > 0)
usage();
xprintf("%s\n", goversion);
func cmdversion() {
xflagparse(0)
xprintf("%s\n", goversion)
}
// 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]);
}
// 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)
}
......@@ -2,7 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "a.h"
package main
import "fmt"
/*
* Helpers for building cmd/go and cmd/cgo.
......@@ -15,35 +17,23 @@
// 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);
// but we also write cmd/cgo/zdefaultcc.go
func mkzdefaultcc(dir, file string) {
var out string
writefile(&out, file, 0);
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)
// 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);
writefile(out, file, 0)
bfree(&b);
bfree(&out);
// 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)
}
......@@ -2,7 +2,12 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "a.h"
package main
import (
"fmt"
"os"
)
/*
* Helpers for building runtime.
......@@ -14,55 +19,28 @@
// 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);
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"
//
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)));
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);
bfree(&b);
bfree(&out);
bfree(&exp);
writefile(out, file, 0)
}
// 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
// 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
// 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
// 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();
}
// 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
}
// 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
// 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() {
}
// 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")
}
}
// 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__
// 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()
// 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
// 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
// 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"
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++
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
${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)
......
......@@ -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%
......
......@@ -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
......
#!/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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment