Roll gofrontend to a6e10414311a

This takes us to Go 1.4. Also includes a couple of changes to the test
suite, both in the runtime package:

- Disable TestSetPanicOnFault. We cannot support this scenario at all,
  due to LLVM's lack of non-call exceptions.

- Tweak TestFinalizerType. This test only passes with two GC runs.

Differential Revision: http://reviews.llvm.org/D8828

llvm-svn: 234134
This commit is contained in:
Peter Collingbourne 2015-04-05 23:30:42 +00:00
parent 7d39641c80
commit 93c73ebcbd
617 changed files with 30038 additions and 6811 deletions

View File

@ -56,6 +56,7 @@ set(LLGO_GO_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/third_party/gofrontend/libgo/go/cmd/go/env.go ${CMAKE_CURRENT_SOURCE_DIR}/third_party/gofrontend/libgo/go/cmd/go/env.go
${CMAKE_CURRENT_SOURCE_DIR}/third_party/gofrontend/libgo/go/cmd/go/fix.go ${CMAKE_CURRENT_SOURCE_DIR}/third_party/gofrontend/libgo/go/cmd/go/fix.go
${CMAKE_CURRENT_SOURCE_DIR}/third_party/gofrontend/libgo/go/cmd/go/fmt.go ${CMAKE_CURRENT_SOURCE_DIR}/third_party/gofrontend/libgo/go/cmd/go/fmt.go
${CMAKE_CURRENT_SOURCE_DIR}/third_party/gofrontend/libgo/go/cmd/go/generate.go
${CMAKE_CURRENT_SOURCE_DIR}/third_party/gofrontend/libgo/go/cmd/go/get.go ${CMAKE_CURRENT_SOURCE_DIR}/third_party/gofrontend/libgo/go/cmd/go/get.go
${CMAKE_CURRENT_SOURCE_DIR}/third_party/gofrontend/libgo/go/cmd/go/go11.go ${CMAKE_CURRENT_SOURCE_DIR}/third_party/gofrontend/libgo/go/cmd/go/go11.go
${CMAKE_CURRENT_SOURCE_DIR}/third_party/gofrontend/libgo/go/cmd/go/help.go ${CMAKE_CURRENT_SOURCE_DIR}/third_party/gofrontend/libgo/go/cmd/go/help.go

View File

@ -1600,6 +1600,7 @@ const (
gccgoRuntimeTypeKindSTRING = 24 gccgoRuntimeTypeKindSTRING = 24
gccgoRuntimeTypeKindSTRUCT = 25 gccgoRuntimeTypeKindSTRUCT = 25
gccgoRuntimeTypeKindUNSAFE_POINTER = 26 gccgoRuntimeTypeKindUNSAFE_POINTER = 26
gccgoRuntimeTypeKindDIRECT_IFACE = (1 << 5)
gccgoRuntimeTypeKindNO_POINTERS = (1 << 7) gccgoRuntimeTypeKindNO_POINTERS = (1 << 7)
) )
@ -1669,7 +1670,7 @@ func runtimeTypeKind(t types.Type) (k uint8) {
case types.String: case types.String:
k = gccgoRuntimeTypeKindSTRING k = gccgoRuntimeTypeKindSTRING
case types.UnsafePointer: case types.UnsafePointer:
k = gccgoRuntimeTypeKindUNSAFE_POINTER k = gccgoRuntimeTypeKindUNSAFE_POINTER | gccgoRuntimeTypeKindDIRECT_IFACE
default: default:
panic("unrecognized builtin type") panic("unrecognized builtin type")
} }
@ -1680,7 +1681,7 @@ func runtimeTypeKind(t types.Type) (k uint8) {
case *types.Struct: case *types.Struct:
k = gccgoRuntimeTypeKindSTRUCT k = gccgoRuntimeTypeKindSTRUCT
case *types.Pointer: case *types.Pointer:
k = gccgoRuntimeTypeKindPTR k = gccgoRuntimeTypeKindPTR | gccgoRuntimeTypeKindDIRECT_IFACE
case *types.Signature: case *types.Signature:
k = gccgoRuntimeTypeKindFUNC k = gccgoRuntimeTypeKindFUNC
case *types.Interface: case *types.Interface:

View File

@ -14,7 +14,7 @@
package irgen package irgen
const ( const (
goVersion = "go1.3" goVersion = "go1.4"
) )
// GoVersion returns the version of Go that we are targeting. // GoVersion returns the version of Go that we are targeting.

View File

@ -1,8 +1,7 @@
diff --git a/libgo/Makefile.am b/libgo/Makefile.am diff -r a6e10414311a libgo/Makefile.am
index 526b656..31c206e 100644 --- a/libgo/Makefile.am Fri Jan 16 07:57:02 2015 -0800
--- a/libgo/Makefile.am +++ b/libgo/Makefile.am Fri Apr 03 15:07:47 2015 -0700
+++ b/libgo/Makefile.am @@ -3738,7 +3738,6 @@
@@ -3688,7 +3688,6 @@ TEST_PACKAGES = \
os/check \ os/check \
path/check \ path/check \
reflect/check \ reflect/check \
@ -10,7 +9,7 @@ index 526b656..31c206e 100644
runtime/check \ runtime/check \
sort/check \ sort/check \
strconv/check \ strconv/check \
@@ -3787,7 +3786,6 @@ TEST_PACKAGES = \ @@ -3838,7 +3837,6 @@
os/user/check \ os/user/check \
path/filepath/check \ path/filepath/check \
regexp/syntax/check \ regexp/syntax/check \
@ -18,11 +17,10 @@ index 526b656..31c206e 100644
sync/atomic/check \ sync/atomic/check \
text/scanner/check \ text/scanner/check \
text/tabwriter/check \ text/tabwriter/check \
diff --git a/libgo/Makefile.in b/libgo/Makefile.in diff -r a6e10414311a libgo/Makefile.in
index 2254478..d2482d1 100644 --- a/libgo/Makefile.in Fri Jan 16 07:57:02 2015 -0800
--- a/libgo/Makefile.in +++ b/libgo/Makefile.in Fri Apr 03 15:07:47 2015 -0700
+++ b/libgo/Makefile.in @@ -2212,7 +2212,6 @@
@@ -2193,7 +2193,6 @@ TEST_PACKAGES = \
os/check \ os/check \
path/check \ path/check \
reflect/check \ reflect/check \
@ -30,7 +28,7 @@ index 2254478..d2482d1 100644
runtime/check \ runtime/check \
sort/check \ sort/check \
strconv/check \ strconv/check \
@@ -2292,7 +2291,6 @@ TEST_PACKAGES = \ @@ -2312,7 +2311,6 @@
os/user/check \ os/user/check \
path/filepath/check \ path/filepath/check \
regexp/syntax/check \ regexp/syntax/check \
@ -38,3 +36,26 @@ index 2254478..d2482d1 100644
sync/atomic/check \ sync/atomic/check \
text/scanner/check \ text/scanner/check \
text/tabwriter/check \ text/tabwriter/check \
diff -r a6e10414311a libgo/go/runtime/mfinal_test.go
--- a/libgo/go/runtime/mfinal_test.go Fri Jan 16 07:57:02 2015 -0800
+++ b/libgo/go/runtime/mfinal_test.go Fri Apr 03 15:07:47 2015 -0700
@@ -62,6 +62,7 @@
}()
<-done
runtime.GC()
+ runtime.GC()
select {
case <-ch:
case <-time.After(time.Second * 4):
diff -r a6e10414311a libgo/go/runtime/runtime_test.go
--- a/libgo/go/runtime/runtime_test.go Fri Jan 16 07:57:02 2015 -0800
+++ b/libgo/go/runtime/runtime_test.go Fri Apr 03 15:07:47 2015 -0700
@@ -176,6 +176,8 @@
}
func TestSetPanicOnFault(t *testing.T) {
+ t.Skip("skipping for llgo due to lack of non-call exception support")
+
// This currently results in a fault in the signal trampoline on
// dragonfly/386 - see issue 7421.
if GOOS == "dragonfly" && GOARCH == "386" {

View File

@ -1,6 +1,6 @@
diff -r 0fde0b6a7eb2 libgo/runtime/chan.goc diff -r a6e10414311a libgo/runtime/chan.goc
--- a/libgo/runtime/chan.goc Fri Jan 09 17:00:26 2015 -0800 --- a/libgo/runtime/chan.goc Fri Jan 16 07:57:02 2015 -0800
+++ b/libgo/runtime/chan.goc Fri Feb 13 12:12:23 2015 -0800 +++ b/libgo/runtime/chan.goc Fri Feb 13 16:36:07 2015 -0800
@@ -111,7 +111,7 @@ @@ -111,7 +111,7 @@
mysg.releasetime = -1; mysg.releasetime = -1;
} }
@ -207,9 +207,9 @@ diff -r 0fde0b6a7eb2 libgo/runtime/chan.goc
} }
void void
diff -r 0fde0b6a7eb2 libgo/runtime/chan.h diff -r a6e10414311a libgo/runtime/chan.h
--- a/libgo/runtime/chan.h Fri Jan 09 17:00:26 2015 -0800 --- a/libgo/runtime/chan.h Fri Jan 16 07:57:02 2015 -0800
+++ b/libgo/runtime/chan.h Fri Feb 13 12:12:23 2015 -0800 +++ b/libgo/runtime/chan.h Fri Feb 13 16:36:07 2015 -0800
@@ -39,7 +39,7 @@ @@ -39,7 +39,7 @@
uintgo recvx; // receive index uintgo recvx; // receive index
WaitQ recvq; // list of recv waiters WaitQ recvq; // list of recv waiters
@ -219,9 +219,9 @@ diff -r 0fde0b6a7eb2 libgo/runtime/chan.h
}; };
// Buffer follows Hchan immediately in memory. // Buffer follows Hchan immediately in memory.
diff -r 0fde0b6a7eb2 libgo/runtime/heapdump.c diff -r a6e10414311a libgo/runtime/heapdump.c
--- a/libgo/runtime/heapdump.c Fri Jan 09 17:00:26 2015 -0800 --- a/libgo/runtime/heapdump.c Fri Jan 16 07:57:02 2015 -0800
+++ b/libgo/runtime/heapdump.c Fri Feb 13 12:12:23 2015 -0800 +++ b/libgo/runtime/heapdump.c Fri Feb 13 16:36:07 2015 -0800
@@ -387,7 +387,7 @@ @@ -387,7 +387,7 @@
if(sp->kind != KindSpecialFinalizer) if(sp->kind != KindSpecialFinalizer)
continue; continue;
@ -240,10 +240,10 @@ diff -r 0fde0b6a7eb2 libgo/runtime/heapdump.c
dumpint(TagAllocSample); dumpint(TagAllocSample);
dumpint((uintptr)p); dumpint((uintptr)p);
dumpint((uintptr)spp->b); dumpint((uintptr)spp->b);
diff -r 0fde0b6a7eb2 libgo/runtime/malloc.goc diff -r a6e10414311a libgo/runtime/malloc.goc
--- a/libgo/runtime/malloc.goc Fri Jan 09 17:00:26 2015 -0800 --- a/libgo/runtime/malloc.goc Fri Jan 16 07:57:02 2015 -0800
+++ b/libgo/runtime/malloc.goc Fri Feb 13 12:12:23 2015 -0800 +++ b/libgo/runtime/malloc.goc Fri Feb 13 16:36:07 2015 -0800
@@ -436,9 +436,9 @@ @@ -437,9 +437,9 @@
m->mcache->local_nlookup++; m->mcache->local_nlookup++;
if (sizeof(void*) == 4 && m->mcache->local_nlookup >= (1<<30)) { if (sizeof(void*) == 4 && m->mcache->local_nlookup >= (1<<30)) {
// purge cache stats to prevent overflow // purge cache stats to prevent overflow
@ -255,7 +255,7 @@ diff -r 0fde0b6a7eb2 libgo/runtime/malloc.goc
} }
s = runtime_MHeap_LookupMaybe(&runtime_mheap, v); s = runtime_MHeap_LookupMaybe(&runtime_mheap, v);
@@ -735,7 +735,7 @@ @@ -736,7 +736,7 @@
static struct static struct
{ {
@ -264,7 +264,7 @@ diff -r 0fde0b6a7eb2 libgo/runtime/malloc.goc
byte* pos; byte* pos;
byte* end; byte* end;
} persistent; } persistent;
@@ -764,19 +764,19 @@ @@ -765,19 +765,19 @@
align = 8; align = 8;
if(size >= PersistentAllocMaxBlock) if(size >= PersistentAllocMaxBlock)
return runtime_SysAlloc(size, stat); return runtime_SysAlloc(size, stat);
@ -287,9 +287,9 @@ diff -r 0fde0b6a7eb2 libgo/runtime/malloc.goc
if(stat != &mstats.other_sys) { if(stat != &mstats.other_sys) {
// reaccount the allocation against provided stat // reaccount the allocation against provided stat
runtime_xadd64(stat, size); runtime_xadd64(stat, size);
diff -r 0fde0b6a7eb2 libgo/runtime/malloc.h diff -r a6e10414311a libgo/runtime/malloc.h
--- a/libgo/runtime/malloc.h Fri Jan 09 17:00:26 2015 -0800 --- a/libgo/runtime/malloc.h Fri Jan 16 07:57:02 2015 -0800
+++ b/libgo/runtime/malloc.h Fri Feb 13 12:12:23 2015 -0800 +++ b/libgo/runtime/malloc.h Fri Feb 13 16:36:07 2015 -0800
@@ -390,7 +390,7 @@ @@ -390,7 +390,7 @@
typedef struct SpecialFinalizer SpecialFinalizer; typedef struct SpecialFinalizer SpecialFinalizer;
struct SpecialFinalizer struct SpecialFinalizer
@ -335,9 +335,9 @@ diff -r 0fde0b6a7eb2 libgo/runtime/malloc.h
byte pad[64]; byte pad[64];
} central[NumSizeClasses]; } central[NumSizeClasses];
diff -r 0fde0b6a7eb2 libgo/runtime/mcache.c diff -r a6e10414311a libgo/runtime/mcache.c
--- a/libgo/runtime/mcache.c Fri Jan 09 17:00:26 2015 -0800 --- a/libgo/runtime/mcache.c Fri Jan 16 07:57:02 2015 -0800
+++ b/libgo/runtime/mcache.c Fri Feb 13 12:12:23 2015 -0800 +++ b/libgo/runtime/mcache.c Fri Feb 13 16:36:07 2015 -0800
@@ -23,9 +23,9 @@ @@ -23,9 +23,9 @@
MCache *c; MCache *c;
int32 i; int32 i;
@ -410,9 +410,9 @@ diff -r 0fde0b6a7eb2 libgo/runtime/mcache.c
l->list = nil; l->list = nil;
l->nlist = 0; l->nlist = 0;
} }
diff -r 0fde0b6a7eb2 libgo/runtime/mcentral.c diff -r a6e10414311a libgo/runtime/mcentral.c
--- a/libgo/runtime/mcentral.c Fri Jan 09 17:00:26 2015 -0800 --- a/libgo/runtime/mcentral.c Fri Jan 16 07:57:02 2015 -0800
+++ b/libgo/runtime/mcentral.c Fri Feb 13 12:12:23 2015 -0800 +++ b/libgo/runtime/mcentral.c Fri Feb 13 16:36:07 2015 -0800
@@ -39,14 +39,14 @@ @@ -39,14 +39,14 @@
int32 cap, n; int32 cap, n;
uint32 sg; uint32 sg;
@ -554,10 +554,10 @@ diff -r 0fde0b6a7eb2 libgo/runtime/mcentral.c
runtime_unmarkspan((byte*)(s->start<<PageShift), s->npages<<PageShift); runtime_unmarkspan((byte*)(s->start<<PageShift), s->npages<<PageShift);
runtime_MHeap_Free(&runtime_mheap, s, 0); runtime_MHeap_Free(&runtime_mheap, s, 0);
} }
diff -r 0fde0b6a7eb2 libgo/runtime/mgc0.c diff -r a6e10414311a libgo/runtime/mgc0.c
--- a/libgo/runtime/mgc0.c Fri Jan 09 17:00:26 2015 -0800 --- a/libgo/runtime/mgc0.c Fri Jan 16 07:57:02 2015 -0800
+++ b/libgo/runtime/mgc0.c Fri Feb 13 12:12:23 2015 -0800 +++ b/libgo/runtime/mgc0.c Fri Feb 13 16:36:07 2015 -0800
@@ -224,7 +224,7 @@ @@ -225,7 +225,7 @@
Note alldone; Note alldone;
ParFor *markfor; ParFor *markfor;
@ -566,7 +566,7 @@ diff -r 0fde0b6a7eb2 libgo/runtime/mgc0.c
byte *chunk; byte *chunk;
uintptr nchunk; uintptr nchunk;
} work __attribute__((aligned(8))); } work __attribute__((aligned(8)));
@@ -1336,7 +1336,7 @@ @@ -1337,7 +1337,7 @@
// retain everything it points to. // retain everything it points to.
spf = (SpecialFinalizer*)sp; spf = (SpecialFinalizer*)sp;
// A finalizer can be set for an inner byte of an object, find object beginning. // A finalizer can be set for an inner byte of an object, find object beginning.
@ -575,7 +575,7 @@ diff -r 0fde0b6a7eb2 libgo/runtime/mgc0.c
enqueue1(&wbuf, (Obj){p, s->elemsize, 0}); enqueue1(&wbuf, (Obj){p, s->elemsize, 0});
enqueue1(&wbuf, (Obj){(void*)&spf->fn, PtrSize, 0}); enqueue1(&wbuf, (Obj){(void*)&spf->fn, PtrSize, 0});
enqueue1(&wbuf, (Obj){(void*)&spf->ft, PtrSize, 0}); enqueue1(&wbuf, (Obj){(void*)&spf->ft, PtrSize, 0});
@@ -1377,7 +1377,7 @@ @@ -1378,7 +1378,7 @@
b = (Workbuf*)runtime_lfstackpop(&work.empty); b = (Workbuf*)runtime_lfstackpop(&work.empty);
if(b == nil) { if(b == nil) {
// Need to allocate. // Need to allocate.
@ -584,7 +584,7 @@ diff -r 0fde0b6a7eb2 libgo/runtime/mgc0.c
if(work.nchunk < sizeof *b) { if(work.nchunk < sizeof *b) {
work.nchunk = 1<<20; work.nchunk = 1<<20;
work.chunk = runtime_SysAlloc(work.nchunk, &mstats.gc_sys); work.chunk = runtime_SysAlloc(work.nchunk, &mstats.gc_sys);
@@ -1387,7 +1387,7 @@ @@ -1388,7 +1388,7 @@
b = (Workbuf*)work.chunk; b = (Workbuf*)work.chunk;
work.chunk += sizeof *b; work.chunk += sizeof *b;
work.nchunk -= sizeof *b; work.nchunk -= sizeof *b;
@ -593,7 +593,7 @@ diff -r 0fde0b6a7eb2 libgo/runtime/mgc0.c
} }
b->nobj = 0; b->nobj = 0;
return b; return b;
@@ -1801,7 +1801,7 @@ @@ -1802,7 +1802,7 @@
c->local_nsmallfree[cl] += nfree; c->local_nsmallfree[cl] += nfree;
c->local_cachealloc -= nfree * size; c->local_cachealloc -= nfree * size;
runtime_xadd64(&mstats.next_gc, -(uint64)(nfree * size * (gcpercent + 100)/100)); runtime_xadd64(&mstats.next_gc, -(uint64)(nfree * size * (gcpercent + 100)/100));
@ -602,7 +602,7 @@ diff -r 0fde0b6a7eb2 libgo/runtime/mgc0.c
//MCentral_FreeSpan updates sweepgen //MCentral_FreeSpan updates sweepgen
} }
return res; return res;
@@ -2146,10 +2146,10 @@ @@ -2147,10 +2147,10 @@
return; return;
if(gcpercent == GcpercentUnknown) { // first time through if(gcpercent == GcpercentUnknown) { // first time through
@ -615,7 +615,7 @@ diff -r 0fde0b6a7eb2 libgo/runtime/mgc0.c
} }
if(gcpercent < 0) if(gcpercent < 0)
return; return;
@@ -2420,7 +2420,7 @@ @@ -2423,7 +2423,7 @@
// Pass back: pauses, last gc (absolute time), number of gc, total pause ns. // Pass back: pauses, last gc (absolute time), number of gc, total pause ns.
p = (uint64*)pauses->array; p = (uint64*)pauses->array;
@ -624,7 +624,7 @@ diff -r 0fde0b6a7eb2 libgo/runtime/mgc0.c
n = mstats.numgc; n = mstats.numgc;
if(n > nelem(mstats.pause_ns)) if(n > nelem(mstats.pause_ns))
n = nelem(mstats.pause_ns); n = nelem(mstats.pause_ns);
@@ -2435,7 +2435,7 @@ @@ -2438,7 +2438,7 @@
p[n] = mstats.last_gc; p[n] = mstats.last_gc;
p[n+1] = mstats.numgc; p[n+1] = mstats.numgc;
p[n+2] = mstats.pause_total_ns; p[n+2] = mstats.pause_total_ns;
@ -633,7 +633,7 @@ diff -r 0fde0b6a7eb2 libgo/runtime/mgc0.c
pauses->__count = n+3; pauses->__count = n+3;
} }
@@ -2443,14 +2443,14 @@ @@ -2446,14 +2446,14 @@
runtime_setgcpercent(int32 in) { runtime_setgcpercent(int32 in) {
int32 out; int32 out;
@ -650,9 +650,9 @@ diff -r 0fde0b6a7eb2 libgo/runtime/mgc0.c
return out; return out;
} }
diff -r 0fde0b6a7eb2 libgo/runtime/mheap.c diff -r a6e10414311a libgo/runtime/mheap.c
--- a/libgo/runtime/mheap.c Fri Jan 09 17:00:26 2015 -0800 --- a/libgo/runtime/mheap.c Fri Jan 16 07:57:02 2015 -0800
+++ b/libgo/runtime/mheap.c Fri Feb 13 12:12:23 2015 -0800 +++ b/libgo/runtime/mheap.c Fri Feb 13 16:36:07 2015 -0800
@@ -70,7 +70,7 @@ @@ -70,7 +70,7 @@
runtime_MSpanList_Init(&h->freelarge); runtime_MSpanList_Init(&h->freelarge);
runtime_MSpanList_Init(&h->busylarge); runtime_MSpanList_Init(&h->busylarge);
@ -837,9 +837,9 @@ diff -r 0fde0b6a7eb2 libgo/runtime/mheap.c
+ runtime_unlock(&h->lock); + runtime_unlock(&h->lock);
} }
} }
diff -r 0fde0b6a7eb2 libgo/runtime/netpoll.goc diff -r a6e10414311a libgo/runtime/netpoll.goc
--- a/libgo/runtime/netpoll.goc Fri Jan 09 17:00:26 2015 -0800 --- a/libgo/runtime/netpoll.goc Fri Jan 16 07:57:02 2015 -0800
+++ b/libgo/runtime/netpoll.goc Fri Feb 13 12:12:23 2015 -0800 +++ b/libgo/runtime/netpoll.goc Fri Feb 13 16:36:07 2015 -0800
@@ -53,7 +53,7 @@ @@ -53,7 +53,7 @@
// pollReset, pollWait, pollWaitCanceled and runtime_netpollready (IO rediness notification) // pollReset, pollWait, pollWaitCanceled and runtime_netpollready (IO rediness notification)
// proceed w/o taking the lock. So closing, rg, rd, wg and wd are manipulated // proceed w/o taking the lock. So closing, rg, rd, wg and wd are manipulated
@ -901,7 +901,7 @@ diff -r 0fde0b6a7eb2 libgo/runtime/netpoll.goc
return; return;
} }
pd->seq++; // invalidate current timers pd->seq++; // invalidate current timers
@@ -223,7 +223,7 @@ @@ -226,7 +226,7 @@
rg = netpollunblock(pd, 'r', false); rg = netpollunblock(pd, 'r', false);
if(pd->wd < 0) if(pd->wd < 0)
wg = netpollunblock(pd, 'w', false); wg = netpollunblock(pd, 'w', false);
@ -910,7 +910,7 @@ diff -r 0fde0b6a7eb2 libgo/runtime/netpoll.goc
if(rg) if(rg)
runtime_ready(rg); runtime_ready(rg);
if(wg) if(wg)
@@ -233,7 +233,7 @@ @@ -236,7 +236,7 @@
func runtime_pollUnblock(pd *PollDesc) { func runtime_pollUnblock(pd *PollDesc) {
G *rg, *wg; G *rg, *wg;
@ -919,7 +919,7 @@ diff -r 0fde0b6a7eb2 libgo/runtime/netpoll.goc
if(pd->closing) if(pd->closing)
runtime_throw("runtime_pollUnblock: already closing"); runtime_throw("runtime_pollUnblock: already closing");
pd->closing = true; pd->closing = true;
@@ -249,7 +249,7 @@ @@ -252,7 +252,7 @@
runtime_deltimer(&pd->wt); runtime_deltimer(&pd->wt);
pd->wt.fv = nil; pd->wt.fv = nil;
} }
@ -928,7 +928,7 @@ diff -r 0fde0b6a7eb2 libgo/runtime/netpoll.goc
if(rg) if(rg)
runtime_ready(rg); runtime_ready(rg);
if(wg) if(wg)
@@ -277,13 +277,13 @@ @@ -280,13 +280,13 @@
void void
runtime_netpolllock(PollDesc *pd) runtime_netpolllock(PollDesc *pd)
{ {
@ -944,12 +944,14 @@ diff -r 0fde0b6a7eb2 libgo/runtime/netpoll.goc
} }
// make pd ready, newly runnable goroutines (if any) are enqueued info gpp list // make pd ready, newly runnable goroutines (if any) are enqueued info gpp list
@@ -401,10 +401,10 @@ @@ -399,12 +399,12 @@
// If it's stale, ignore the timer event.
seq = (uintptr)arg.type; pd = (PollDesc*)arg.data;
rg = wg = nil; rg = wg = nil;
- runtime_lock(pd); - runtime_lock(pd);
+ runtime_lock(&pd->lock); + runtime_lock(&pd->lock);
// Seq arg is seq when the timer was set.
// If it's stale, ignore the timer event.
if(seq != pd->seq) { if(seq != pd->seq) {
// The descriptor was reused or timers were reset. // The descriptor was reused or timers were reset.
- runtime_unlock(pd); - runtime_unlock(pd);
@ -983,9 +985,9 @@ diff -r 0fde0b6a7eb2 libgo/runtime/netpoll.goc
+ runtime_unlock(&pollcache.lock); + runtime_unlock(&pollcache.lock);
return pd; return pd;
} }
diff -r 0fde0b6a7eb2 libgo/runtime/proc.c diff -r a6e10414311a libgo/runtime/proc.c
--- a/libgo/runtime/proc.c Fri Jan 09 17:00:26 2015 -0800 --- a/libgo/runtime/proc.c Fri Jan 16 07:57:02 2015 -0800
+++ b/libgo/runtime/proc.c Fri Feb 13 12:12:23 2015 -0800 +++ b/libgo/runtime/proc.c Fri Feb 13 16:36:07 2015 -0800
@@ -302,7 +302,7 @@ @@ -302,7 +302,7 @@
typedef struct Sched Sched; typedef struct Sched Sched;
@ -1500,9 +1502,9 @@ diff -r 0fde0b6a7eb2 libgo/runtime/proc.c
return out; return out;
} }
diff -r 0fde0b6a7eb2 libgo/runtime/runtime.h diff -r a6e10414311a libgo/runtime/runtime.h
--- a/libgo/runtime/runtime.h Fri Jan 09 17:00:26 2015 -0800 --- a/libgo/runtime/runtime.h Fri Jan 16 07:57:02 2015 -0800
+++ b/libgo/runtime/runtime.h Fri Feb 13 12:12:23 2015 -0800 +++ b/libgo/runtime/runtime.h Fri Feb 13 16:36:07 2015 -0800
@@ -286,7 +286,7 @@ @@ -286,7 +286,7 @@
struct P struct P
@ -1521,9 +1523,9 @@ diff -r 0fde0b6a7eb2 libgo/runtime/runtime.h
G *timerproc; G *timerproc;
bool sleeping; bool sleeping;
bool rescheduling; bool rescheduling;
diff -r 0fde0b6a7eb2 libgo/runtime/sema.goc diff -r a6e10414311a libgo/runtime/sema.goc
--- a/libgo/runtime/sema.goc Fri Jan 09 17:00:26 2015 -0800 --- a/libgo/runtime/sema.goc Fri Jan 16 07:57:02 2015 -0800
+++ b/libgo/runtime/sema.goc Fri Feb 13 12:12:23 2015 -0800 +++ b/libgo/runtime/sema.goc Fri Feb 13 16:36:07 2015 -0800
@@ -35,7 +35,7 @@ @@ -35,7 +35,7 @@
typedef struct SemaRoot SemaRoot; typedef struct SemaRoot SemaRoot;
struct SemaRoot struct SemaRoot
@ -1652,9 +1654,9 @@ diff -r 0fde0b6a7eb2 libgo/runtime/sema.goc
- runtime_unlock(s); - runtime_unlock(s);
+ runtime_unlock(&s->lock); + runtime_unlock(&s->lock);
} }
diff -r 0fde0b6a7eb2 libgo/runtime/sigqueue.goc diff -r a6e10414311a libgo/runtime/sigqueue.goc
--- a/libgo/runtime/sigqueue.goc Fri Jan 09 17:00:26 2015 -0800 --- a/libgo/runtime/sigqueue.goc Fri Jan 16 07:57:02 2015 -0800
+++ b/libgo/runtime/sigqueue.goc Fri Feb 13 12:12:23 2015 -0800 +++ b/libgo/runtime/sigqueue.goc Fri Feb 13 16:36:07 2015 -0800
@@ -32,7 +32,7 @@ @@ -32,7 +32,7 @@
#include "defs.h" #include "defs.h"
@ -1693,13 +1695,13 @@ diff -r 0fde0b6a7eb2 libgo/runtime/sigqueue.goc
return; return;
} }
diff -r 0fde0b6a7eb2 libgo/runtime/time.goc diff -r a6e10414311a libgo/runtime/time.goc
--- a/libgo/runtime/time.goc Fri Jan 09 17:00:26 2015 -0800 --- a/libgo/runtime/time.goc Fri Jan 16 07:57:02 2015 -0800
+++ b/libgo/runtime/time.goc Fri Feb 13 12:12:23 2015 -0800 +++ b/libgo/runtime/time.goc Fri Feb 13 16:36:07 2015 -0800
@@ -91,17 +91,17 @@ @@ -92,17 +92,17 @@
t.period = 0;
t.fv = &readyv; t.fv = &readyv;
t.arg.__object = g; t.arg.__object = g;
t.seq = 0;
- runtime_lock(&timers); - runtime_lock(&timers);
+ runtime_lock(&timers.lock); + runtime_lock(&timers.lock);
addtimer(&t); addtimer(&t);
@ -1718,7 +1720,7 @@ diff -r 0fde0b6a7eb2 libgo/runtime/time.goc
} }
// Add a timer to the heap and start or kick the timer proc // Add a timer to the heap and start or kick the timer proc
@@ -166,14 +166,14 @@ @@ -167,14 +167,14 @@
i = t->i; i = t->i;
gi = i; gi = i;
@ -1735,7 +1737,7 @@ diff -r 0fde0b6a7eb2 libgo/runtime/time.goc
return false; return false;
} }
@@ -189,7 +189,7 @@ @@ -190,7 +190,7 @@
} }
if(debug) if(debug)
dumptimers("deltimer"); dumptimers("deltimer");
@ -1744,8 +1746,8 @@ diff -r 0fde0b6a7eb2 libgo/runtime/time.goc
return true; return true;
} }
@@ -207,7 +207,7 @@ @@ -209,7 +209,7 @@
Eface arg; uintptr seq;
for(;;) { for(;;) {
- runtime_lock(&timers); - runtime_lock(&timers);
@ -1753,16 +1755,16 @@ diff -r 0fde0b6a7eb2 libgo/runtime/time.goc
timers.sleeping = false; timers.sleeping = false;
now = runtime_nanotime(); now = runtime_nanotime();
for(;;) { for(;;) {
@@ -233,7 +233,7 @@ @@ -236,7 +236,7 @@
fv = t->fv;
f = (void*)t->fv->fn; f = (void*)t->fv->fn;
arg = t->arg; arg = t->arg;
seq = t->seq;
- runtime_unlock(&timers); - runtime_unlock(&timers);
+ runtime_unlock(&timers.lock); + runtime_unlock(&timers.lock);
__go_set_closure(fv); __go_set_closure(fv);
f(now, arg); f(arg, seq);
@@ -244,20 +244,20 @@ @@ -247,20 +247,20 @@
arg.__object = nil; arg.__object = nil;
USED(&arg); USED(&arg);

View File

@ -1,4 +1,4 @@
f44017549ff9 14854533dcc7
The first line of this file holds the Mercurial revision number of the The first line of this file holds the Mercurial revision number of the
last merge done from the master library sources. last merge done from the master library sources.

View File

@ -495,6 +495,7 @@ runtime_files = \
runtime/go-unsafe-new.c \ runtime/go-unsafe-new.c \
runtime/go-unsafe-newarray.c \ runtime/go-unsafe-newarray.c \
runtime/go-unsafe-pointer.c \ runtime/go-unsafe-pointer.c \
runtime/go-unsetenv.c \
runtime/go-unwind.c \ runtime/go-unwind.c \
runtime/go-varargs.c \ runtime/go-varargs.c \
runtime/env_posix.c \ runtime/env_posix.c \
@ -695,7 +696,7 @@ go_net_sockoptip_file = go/net/sockoptip_linux.go go/net/sockoptip_posix.go
else else
if LIBGO_IS_SOLARIS if LIBGO_IS_SOLARIS
go_net_cgo_file = go/net/cgo_linux.go go_net_cgo_file = go/net/cgo_linux.go
go_net_sock_file = go/net/sock_solaris.go go_net_sock_file = go/net/sock_stub.go
go_net_sockopt_file = go/net/sockopt_solaris.go go_net_sockopt_file = go/net/sockopt_solaris.go
go_net_sockoptip_file = go/net/sockoptip_stub.go go_net_sockoptip_file = go/net/sockoptip_stub.go
else else
@ -997,7 +998,6 @@ go_runtime_files = \
go/runtime/extern.go \ go/runtime/extern.go \
go/runtime/mem.go \ go/runtime/mem.go \
go/runtime/softfloat64.go \ go/runtime/softfloat64.go \
go/runtime/type.go \
version.go version.go
version.go: s-version; @true version.go: s-version; @true
@ -1187,10 +1187,19 @@ go_crypto_md5_files = \
go/crypto/md5/md5.go \ go/crypto/md5/md5.go \
go/crypto/md5/md5block.go \ go/crypto/md5/md5block.go \
go/crypto/md5/md5block_generic.go go/crypto/md5/md5block_generic.go
if LIBGO_IS_LINUX
crypto_rand_file = go/crypto/rand/rand_linux.go
else
crypto_rand_file =
endif
go_crypto_rand_files = \ go_crypto_rand_files = \
go/crypto/rand/rand.go \ go/crypto/rand/rand.go \
go/crypto/rand/rand_unix.go \ go/crypto/rand/rand_unix.go \
$(crypto_rand_file) \
go/crypto/rand/util.go go/crypto/rand/util.go
go_crypto_rc4_files = \ go_crypto_rc4_files = \
go/crypto/rc4/rc4.go \ go/crypto/rc4/rc4.go \
go/crypto/rc4/rc4_ref.go go/crypto/rc4/rc4_ref.go
@ -1289,9 +1298,11 @@ go_encoding_csv_files = \
go_encoding_gob_files = \ go_encoding_gob_files = \
go/encoding/gob/decode.go \ go/encoding/gob/decode.go \
go/encoding/gob/decoder.go \ go/encoding/gob/decoder.go \
go/encoding/gob/dec_helpers.go \
go/encoding/gob/doc.go \ go/encoding/gob/doc.go \
go/encoding/gob/encode.go \ go/encoding/gob/encode.go \
go/encoding/gob/encoder.go \ go/encoding/gob/encoder.go \
go/encoding/gob/enc_helpers.go \
go/encoding/gob/error.go \ go/encoding/gob/error.go \
go/encoding/gob/type.go go/encoding/gob/type.go
go_encoding_hex_files = \ go_encoding_hex_files = \
@ -1452,7 +1463,6 @@ go_mime_multipart_files = \
go/mime/multipart/writer.go go/mime/multipart/writer.go
go_net_http_files = \ go_net_http_files = \
go/net/http/chunked.go \
go/net/http/client.go \ go/net/http/client.go \
go/net/http/cookie.go \ go/net/http/cookie.go \
go/net/http/filetransport.go \ go/net/http/filetransport.go \
@ -1496,12 +1506,12 @@ go_net_http_httptest_files = \
go_net_http_pprof_files = \ go_net_http_pprof_files = \
go/net/http/pprof/pprof.go go/net/http/pprof/pprof.go
go_net_http_httputil_files = \ go_net_http_httputil_files = \
go/net/http/httputil/chunked.go \
go/net/http/httputil/dump.go \ go/net/http/httputil/dump.go \
go/net/http/httputil/httputil.go \ go/net/http/httputil/httputil.go \
go/net/http/httputil/persist.go \ go/net/http/httputil/persist.go \
go/net/http/httputil/reverseproxy.go go/net/http/httputil/reverseproxy.go
go_net_http_internal_files = \
go/net/http/internal/chunked.go
go_old_regexp_files = \ go_old_regexp_files = \
go/old/regexp/regexp.go go/old/regexp/regexp.go
@ -1535,7 +1545,8 @@ go_path_filepath_files = \
go/path/filepath/match.go \ go/path/filepath/match.go \
go/path/filepath/path.go \ go/path/filepath/path.go \
go/path/filepath/path_unix.go \ go/path/filepath/path_unix.go \
go/path/filepath/symlink.go go/path/filepath/symlink.go \
go/path/filepath/symlink_unix.go
go_regexp_syntax_files = \ go_regexp_syntax_files = \
go/regexp/syntax/compile.go \ go/regexp/syntax/compile.go \
@ -1570,7 +1581,8 @@ go_text_template_parse_files = \
go/text/template/parse/parse.go go/text/template/parse/parse.go
go_sync_atomic_files = \ go_sync_atomic_files = \
go/sync/atomic/doc.go go/sync/atomic/doc.go \
go/sync/atomic/value.go
go_sync_atomic_c_files = \ go_sync_atomic_c_files = \
go/sync/atomic/atomic.c go/sync/atomic/atomic.c
@ -1784,10 +1796,21 @@ go_syscall_c_files = \
go_syscall_test_files = \ go_syscall_test_files = \
$(syscall_creds_test_file) \ $(syscall_creds_test_file) \
go/syscall/export_test.go \
go/syscall/mmap_unix_test.go \ go/syscall/mmap_unix_test.go \
go/syscall/syscall_test.go \ go/syscall/syscall_test.go \
go/syscall/syscall_unix_test.go go/syscall/syscall_unix_test.go
if LIBGO_IS_LINUX
internal_syscall_getrandom_file = go/internal/syscall/getrandom_linux.go
else
internal_syscall_getrandom_file =
endif
go_internal_syscall_files = \
go/internal/syscall/dummy.go \
$(internal_syscall_getrandom_file)
libcalls.go: s-libcalls; @true libcalls.go: s-libcalls; @true
s-libcalls: libcalls-list go/syscall/mksyscall.awk $(go_base_syscall_files) s-libcalls: libcalls-list go/syscall/mksyscall.awk $(go_base_syscall_files)
rm -f libcalls.go.tmp rm -f libcalls.go.tmp
@ -1957,6 +1980,7 @@ libgo_go_objs = \
net/http/fcgi.lo \ net/http/fcgi.lo \
net/http/httptest.lo \ net/http/httptest.lo \
net/http/httputil.lo \ net/http/httputil.lo \
net/http/internal.lo \
net/http/pprof.lo \ net/http/pprof.lo \
image/color.lo \ image/color.lo \
image/color/palette.lo \ image/color/palette.lo \
@ -1965,6 +1989,7 @@ libgo_go_objs = \
image/jpeg.lo \ image/jpeg.lo \
image/png.lo \ image/png.lo \
index/suffixarray.lo \ index/suffixarray.lo \
internal/syscall.lo \
io/ioutil.lo \ io/ioutil.lo \
log/syslog.lo \ log/syslog.lo \
log/syslog/syslog_c.lo \ log/syslog/syslog_c.lo \
@ -3160,6 +3185,15 @@ net/http/httputil/check: $(check_deps)
@$(CHECK) @$(CHECK)
.PHONY: net/http/httputil/check .PHONY: net/http/httputil/check
@go_include@ net/http/internal.lo.dep
net/http/internal.lo.dep: $(go_net_http_internal_files)
$(BUILDDEPS)
net/http/internal.lo: $(go_net_http_internal_files)
$(BUILDPACKAGE)
net/http/internal/check: $(CHECK_DEPS)
@$(CHECK)
.PHONY: net/http/internal/check
@go_include@ net/http/pprof.lo.dep @go_include@ net/http/pprof.lo.dep
net/http/pprof.lo.dep: $(go_net_http_pprof_files) net/http/pprof.lo.dep: $(go_net_http_pprof_files)
$(BUILDDEPS) $(BUILDDEPS)
@ -3260,7 +3294,8 @@ runtime/pprof/check: $(CHECK_DEPS)
.PHONY: runtime/pprof/check .PHONY: runtime/pprof/check
# At least for now, we need -static-libgo for this test, because # At least for now, we need -static-libgo for this test, because
# otherwise we can't get the line numbers. # otherwise we can't get the line numbers.
runtime_pprof_check_GOCFLAGS = -static-libgo # Also use -fno-inline to get better results from the memory profiler.
runtime_pprof_check_GOCFLAGS = -static-libgo -fno-inline
@go_include@ sync/atomic.lo.dep @go_include@ sync/atomic.lo.dep
sync/atomic.lo.dep: $(go_sync_atomic_files) sync/atomic.lo.dep: $(go_sync_atomic_files)
@ -3363,6 +3398,15 @@ syscall/check: $(CHECK_DEPS)
@$(CHECK) @$(CHECK)
.PHONY: syscall/check .PHONY: syscall/check
@go_include@ internal/syscall.lo.dep
internal/syscall.lo.dep: $(go_internal_syscall_files)
$(BUILDDEPS)
internal/syscall.lo: $(go_internal_syscall_files)
$(BUILDPACKAGE)
internal/syscall/check: $(CHECK_DEPS)
@$(CHECK)
.PHONY: internal/syscall/check
# How to build a .gox file from a .lo file. # How to build a .gox file from a .lo file.
BUILDGOX = \ BUILDGOX = \
f=`echo $< | sed -e 's/.lo$$/.o/'`; \ f=`echo $< | sed -e 's/.lo$$/.o/'`; \
@ -3623,6 +3667,9 @@ net/http/httputil.gox: net/http/httputil.lo
net/http/pprof.gox: net/http/pprof.lo net/http/pprof.gox: net/http/pprof.lo
$(BUILDGOX) $(BUILDGOX)
net/http/internal.gox: net/http/internal.lo
$(BUILDGOX)
net/rpc/jsonrpc.gox: net/rpc/jsonrpc.lo net/rpc/jsonrpc.gox: net/rpc/jsonrpc.lo
$(BUILDGOX) $(BUILDGOX)
@ -3652,6 +3699,9 @@ runtime/pprof.gox: runtime/pprof.lo
sync/atomic.gox: sync/atomic.lo sync/atomic.gox: sync/atomic.lo
$(BUILDGOX) $(BUILDGOX)
internal/syscall.gox: internal/syscall.lo
$(BUILDGOX)
text/scanner.gox: text/scanner.lo text/scanner.gox: text/scanner.lo
$(BUILDGOX) $(BUILDGOX)
text/tabwriter.gox: text/tabwriter.lo text/tabwriter.gox: text/tabwriter.lo
@ -3773,6 +3823,7 @@ TEST_PACKAGES = \
net/http/fcgi/check \ net/http/fcgi/check \
net/http/httptest/check \ net/http/httptest/check \
net/http/httputil/check \ net/http/httputil/check \
net/http/internal/check \
net/mail/check \ net/mail/check \
net/rpc/check \ net/rpc/check \
net/smtp/check \ net/smtp/check \

View File

@ -164,14 +164,15 @@ am__DEPENDENCIES_2 = bufio.lo bytes.lo bytes/index.lo crypto.lo \
go/printer.lo go/scanner.lo go/token.lo hash/adler32.lo \ go/printer.lo go/scanner.lo go/token.lo hash/adler32.lo \
hash/crc32.lo hash/crc64.lo hash/fnv.lo net/http/cgi.lo \ hash/crc32.lo hash/crc64.lo hash/fnv.lo net/http/cgi.lo \
net/http/cookiejar.lo net/http/fcgi.lo net/http/httptest.lo \ net/http/cookiejar.lo net/http/fcgi.lo net/http/httptest.lo \
net/http/httputil.lo net/http/pprof.lo image/color.lo \ net/http/httputil.lo net/http/internal.lo net/http/pprof.lo \
image/color/palette.lo image/draw.lo image/gif.lo \ image/color.lo image/color/palette.lo image/draw.lo \
image/jpeg.lo image/png.lo index/suffixarray.lo io/ioutil.lo \ image/gif.lo image/jpeg.lo image/png.lo index/suffixarray.lo \
log/syslog.lo log/syslog/syslog_c.lo math/big.lo math/cmplx.lo \ internal/syscall.lo io/ioutil.lo log/syslog.lo \
math/rand.lo mime/multipart.lo net/http.lo net/mail.lo \ log/syslog/syslog_c.lo math/big.lo math/cmplx.lo math/rand.lo \
net/rpc.lo net/smtp.lo net/textproto.lo net/url.lo \ mime/multipart.lo net/http.lo net/mail.lo net/rpc.lo \
old/regexp.lo old/template.lo os/exec.lo $(am__DEPENDENCIES_1) \ net/smtp.lo net/textproto.lo net/url.lo old/regexp.lo \
os/signal.lo os/user.lo path/filepath.lo regexp/syntax.lo \ old/template.lo os/exec.lo $(am__DEPENDENCIES_1) os/signal.lo \
os/user.lo path/filepath.lo regexp/syntax.lo \
net/rpc/jsonrpc.lo runtime/debug.lo runtime/pprof.lo \ net/rpc/jsonrpc.lo runtime/debug.lo runtime/pprof.lo \
sync/atomic.lo sync/atomic_c.lo text/scanner.lo \ sync/atomic.lo sync/atomic_c.lo text/scanner.lo \
text/tabwriter.lo text/template.lo text/template/parse.lo \ text/tabwriter.lo text/template.lo text/template/parse.lo \
@ -218,15 +219,15 @@ am__objects_6 = go-append.lo go-assert.lo go-assert-interface.lo \
go-type-complex.lo go-type-eface.lo go-type-error.lo \ go-type-complex.lo go-type-eface.lo go-type-error.lo \
go-type-float.lo go-type-identity.lo go-type-interface.lo \ go-type-float.lo go-type-identity.lo go-type-interface.lo \
go-type-string.lo go-typedesc-equal.lo go-unsafe-new.lo \ go-type-string.lo go-typedesc-equal.lo go-unsafe-new.lo \
go-unsafe-newarray.lo go-unsafe-pointer.lo go-unwind.lo \ go-unsafe-newarray.lo go-unsafe-pointer.lo go-unsetenv.lo \
go-varargs.lo env_posix.lo heapdump.lo $(am__objects_1) \ go-unwind.lo go-varargs.lo env_posix.lo heapdump.lo \
mcache.lo mcentral.lo $(am__objects_2) mfixalloc.lo mgc0.lo \ $(am__objects_1) mcache.lo mcentral.lo $(am__objects_2) \
mheap.lo msize.lo $(am__objects_3) panic.lo parfor.lo print.lo \ mfixalloc.lo mgc0.lo mheap.lo msize.lo $(am__objects_3) \
proc.lo runtime.lo signal_unix.lo thread.lo yield.lo \ panic.lo parfor.lo print.lo proc.lo runtime.lo signal_unix.lo \
$(am__objects_4) chan.lo cpuprof.lo go-iface.lo lfstack.lo \ thread.lo yield.lo $(am__objects_4) chan.lo cpuprof.lo \
malloc.lo map.lo mprof.lo netpoll.lo rdebug.lo reflect.lo \ go-iface.lo lfstack.lo malloc.lo map.lo mprof.lo netpoll.lo \
runtime1.lo sema.lo sigqueue.lo string.lo time.lo \ rdebug.lo reflect.lo runtime1.lo sema.lo sigqueue.lo string.lo \
$(am__objects_5) time.lo $(am__objects_5)
am_libgo_llgo_la_OBJECTS = $(am__objects_6) am_libgo_llgo_la_OBJECTS = $(am__objects_6)
libgo_llgo_la_OBJECTS = $(am_libgo_llgo_la_OBJECTS) libgo_llgo_la_OBJECTS = $(am_libgo_llgo_la_OBJECTS)
libgo_llgo_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ libgo_llgo_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
@ -838,6 +839,7 @@ runtime_files = \
runtime/go-unsafe-new.c \ runtime/go-unsafe-new.c \
runtime/go-unsafe-newarray.c \ runtime/go-unsafe-newarray.c \
runtime/go-unsafe-pointer.c \ runtime/go-unsafe-pointer.c \
runtime/go-unsetenv.c \
runtime/go-unwind.c \ runtime/go-unwind.c \
runtime/go-varargs.c \ runtime/go-varargs.c \
runtime/env_posix.c \ runtime/env_posix.c \
@ -992,7 +994,7 @@ go_mime_files = \
@LIBGO_IS_FREEBSD_FALSE@@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_net_sock_file = go/net/sock_bsd.go @LIBGO_IS_FREEBSD_FALSE@@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_net_sock_file = go/net/sock_bsd.go
@LIBGO_IS_FREEBSD_FALSE@@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_TRUE@@LIBGO_IS_SOLARIS_FALSE@go_net_sock_file = go/net/sock_bsd.go @LIBGO_IS_FREEBSD_FALSE@@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_TRUE@@LIBGO_IS_SOLARIS_FALSE@go_net_sock_file = go/net/sock_bsd.go
@LIBGO_IS_FREEBSD_TRUE@@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_net_sock_file = go/net/sock_bsd.go @LIBGO_IS_FREEBSD_TRUE@@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_net_sock_file = go/net/sock_bsd.go
@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_TRUE@go_net_sock_file = go/net/sock_solaris.go @LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_TRUE@go_net_sock_file = go/net/sock_stub.go
@LIBGO_IS_IRIX_TRUE@@LIBGO_IS_LINUX_FALSE@go_net_sock_file = go/net/sock_linux.go @LIBGO_IS_IRIX_TRUE@@LIBGO_IS_LINUX_FALSE@go_net_sock_file = go/net/sock_linux.go
@LIBGO_IS_LINUX_TRUE@go_net_sock_file = go/net/sock_linux.go @LIBGO_IS_LINUX_TRUE@go_net_sock_file = go/net/sock_linux.go
@LIBGO_IS_FREEBSD_FALSE@@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_net_sockopt_file = go/net/sockopt_bsd.go @LIBGO_IS_FREEBSD_FALSE@@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_net_sockopt_file = go/net/sockopt_bsd.go
@ -1180,7 +1182,6 @@ go_runtime_files = \
go/runtime/extern.go \ go/runtime/extern.go \
go/runtime/mem.go \ go/runtime/mem.go \
go/runtime/softfloat64.go \ go/runtime/softfloat64.go \
go/runtime/type.go \
version.go version.go
go_sort_files = \ go_sort_files = \
@ -1348,9 +1349,12 @@ go_crypto_md5_files = \
go/crypto/md5/md5block.go \ go/crypto/md5/md5block.go \
go/crypto/md5/md5block_generic.go go/crypto/md5/md5block_generic.go
@LIBGO_IS_LINUX_FALSE@crypto_rand_file =
@LIBGO_IS_LINUX_TRUE@crypto_rand_file = go/crypto/rand/rand_linux.go
go_crypto_rand_files = \ go_crypto_rand_files = \
go/crypto/rand/rand.go \ go/crypto/rand/rand.go \
go/crypto/rand/rand_unix.go \ go/crypto/rand/rand_unix.go \
$(crypto_rand_file) \
go/crypto/rand/util.go go/crypto/rand/util.go
go_crypto_rc4_files = \ go_crypto_rc4_files = \
@ -1469,9 +1473,11 @@ go_encoding_csv_files = \
go_encoding_gob_files = \ go_encoding_gob_files = \
go/encoding/gob/decode.go \ go/encoding/gob/decode.go \
go/encoding/gob/decoder.go \ go/encoding/gob/decoder.go \
go/encoding/gob/dec_helpers.go \
go/encoding/gob/doc.go \ go/encoding/gob/doc.go \
go/encoding/gob/encode.go \ go/encoding/gob/encode.go \
go/encoding/gob/encoder.go \ go/encoding/gob/encoder.go \
go/encoding/gob/enc_helpers.go \
go/encoding/gob/error.go \ go/encoding/gob/error.go \
go/encoding/gob/type.go go/encoding/gob/type.go
@ -1649,7 +1655,6 @@ go_mime_multipart_files = \
go/mime/multipart/writer.go go/mime/multipart/writer.go
go_net_http_files = \ go_net_http_files = \
go/net/http/chunked.go \
go/net/http/client.go \ go/net/http/client.go \
go/net/http/cookie.go \ go/net/http/cookie.go \
go/net/http/filetransport.go \ go/net/http/filetransport.go \
@ -1702,12 +1707,14 @@ go_net_http_pprof_files = \
go/net/http/pprof/pprof.go go/net/http/pprof/pprof.go
go_net_http_httputil_files = \ go_net_http_httputil_files = \
go/net/http/httputil/chunked.go \
go/net/http/httputil/dump.go \ go/net/http/httputil/dump.go \
go/net/http/httputil/httputil.go \ go/net/http/httputil/httputil.go \
go/net/http/httputil/persist.go \ go/net/http/httputil/persist.go \
go/net/http/httputil/reverseproxy.go go/net/http/httputil/reverseproxy.go
go_net_http_internal_files = \
go/net/http/internal/chunked.go
go_old_regexp_files = \ go_old_regexp_files = \
go/old/regexp/regexp.go go/old/regexp/regexp.go
@ -1737,7 +1744,8 @@ go_path_filepath_files = \
go/path/filepath/match.go \ go/path/filepath/match.go \
go/path/filepath/path.go \ go/path/filepath/path.go \
go/path/filepath/path_unix.go \ go/path/filepath/path_unix.go \
go/path/filepath/symlink.go go/path/filepath/symlink.go \
go/path/filepath/symlink_unix.go
go_regexp_syntax_files = \ go_regexp_syntax_files = \
go/regexp/syntax/compile.go \ go/regexp/syntax/compile.go \
@ -1775,7 +1783,8 @@ go_text_template_parse_files = \
go/text/template/parse/parse.go go/text/template/parse/parse.go
go_sync_atomic_files = \ go_sync_atomic_files = \
go/sync/atomic/doc.go go/sync/atomic/doc.go \
go/sync/atomic/value.go
go_sync_atomic_c_files = \ go_sync_atomic_c_files = \
go/sync/atomic/atomic.c go/sync/atomic/atomic.c
@ -1918,10 +1927,17 @@ go_syscall_c_files = \
go_syscall_test_files = \ go_syscall_test_files = \
$(syscall_creds_test_file) \ $(syscall_creds_test_file) \
go/syscall/export_test.go \
go/syscall/mmap_unix_test.go \ go/syscall/mmap_unix_test.go \
go/syscall/syscall_test.go \ go/syscall/syscall_test.go \
go/syscall/syscall_unix_test.go go/syscall/syscall_unix_test.go
@LIBGO_IS_LINUX_FALSE@internal_syscall_getrandom_file =
@LIBGO_IS_LINUX_TRUE@internal_syscall_getrandom_file = go/internal/syscall/getrandom_linux.go
go_internal_syscall_files = \
go/internal/syscall/dummy.go \
$(internal_syscall_getrandom_file)
@LIBGO_IS_LINUX_FALSE@os_lib_inotify_lo = @LIBGO_IS_LINUX_FALSE@os_lib_inotify_lo =
# os_lib_inotify_lo = os/inotify.lo # os_lib_inotify_lo = os/inotify.lo
@ -2030,6 +2046,7 @@ libgo_go_objs = \
net/http/fcgi.lo \ net/http/fcgi.lo \
net/http/httptest.lo \ net/http/httptest.lo \
net/http/httputil.lo \ net/http/httputil.lo \
net/http/internal.lo \
net/http/pprof.lo \ net/http/pprof.lo \
image/color.lo \ image/color.lo \
image/color/palette.lo \ image/color/palette.lo \
@ -2038,6 +2055,7 @@ libgo_go_objs = \
image/jpeg.lo \ image/jpeg.lo \
image/png.lo \ image/png.lo \
index/suffixarray.lo \ index/suffixarray.lo \
internal/syscall.lo \
io/ioutil.lo \ io/ioutil.lo \
log/syslog.lo \ log/syslog.lo \
log/syslog/syslog_c.lo \ log/syslog/syslog_c.lo \
@ -2169,7 +2187,8 @@ CHECK_DEPS = $(toolexeclibgo_DATA) $(toolexeclibgoarchive_DATA) \
$(toolexeclibgounicode_DATA) $(am__append_1) $(am__append_2) $(toolexeclibgounicode_DATA) $(am__append_1) $(am__append_2)
# At least for now, we need -static-libgo for this test, because # At least for now, we need -static-libgo for this test, because
# otherwise we can't get the line numbers. # otherwise we can't get the line numbers.
runtime_pprof_check_GOCFLAGS = -static-libgo # Also use -fno-inline to get better results from the memory profiler.
runtime_pprof_check_GOCFLAGS = -static-libgo -fno-inline
# How to build a .gox file from a .lo file. # How to build a .gox file from a .lo file.
BUILDGOX = \ BUILDGOX = \
@ -2278,6 +2297,7 @@ TEST_PACKAGES = \
net/http/fcgi/check \ net/http/fcgi/check \
net/http/httptest/check \ net/http/httptest/check \
net/http/httputil/check \ net/http/httputil/check \
net/http/internal/check \
net/mail/check \ net/mail/check \
net/rpc/check \ net/rpc/check \
net/smtp/check \ net/smtp/check \
@ -2513,6 +2533,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-unsafe-new.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-unsafe-new.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-unsafe-newarray.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-unsafe-newarray.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-unsafe-pointer.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-unsafe-pointer.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-unsetenv.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-unwind.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-unwind.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-varargs.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-varargs.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/heapdump.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/heapdump.Plo@am__quote@
@ -3029,6 +3050,13 @@ go-unsafe-pointer.lo: runtime/go-unsafe-pointer.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o go-unsafe-pointer.lo `test -f 'runtime/go-unsafe-pointer.c' || echo '$(srcdir)/'`runtime/go-unsafe-pointer.c @am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o go-unsafe-pointer.lo `test -f 'runtime/go-unsafe-pointer.c' || echo '$(srcdir)/'`runtime/go-unsafe-pointer.c
go-unsetenv.lo: runtime/go-unsetenv.c
@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT go-unsetenv.lo -MD -MP -MF $(DEPDIR)/go-unsetenv.Tpo -c -o go-unsetenv.lo `test -f 'runtime/go-unsetenv.c' || echo '$(srcdir)/'`runtime/go-unsetenv.c
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/go-unsetenv.Tpo $(DEPDIR)/go-unsetenv.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='runtime/go-unsetenv.c' object='go-unsetenv.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o go-unsetenv.lo `test -f 'runtime/go-unsetenv.c' || echo '$(srcdir)/'`runtime/go-unsetenv.c
go-unwind.lo: runtime/go-unwind.c go-unwind.lo: runtime/go-unwind.c
@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT go-unwind.lo -MD -MP -MF $(DEPDIR)/go-unwind.Tpo -c -o go-unwind.lo `test -f 'runtime/go-unwind.c' || echo '$(srcdir)/'`runtime/go-unwind.c @am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT go-unwind.lo -MD -MP -MF $(DEPDIR)/go-unwind.Tpo -c -o go-unwind.lo `test -f 'runtime/go-unwind.c' || echo '$(srcdir)/'`runtime/go-unwind.c
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/go-unwind.Tpo $(DEPDIR)/go-unwind.Plo @am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/go-unwind.Tpo $(DEPDIR)/go-unwind.Plo
@ -5496,6 +5524,15 @@ net/http/httputil/check: $(check_deps)
@$(CHECK) @$(CHECK)
.PHONY: net/http/httputil/check .PHONY: net/http/httputil/check
@go_include@ net/http/internal.lo.dep
net/http/internal.lo.dep: $(go_net_http_internal_files)
$(BUILDDEPS)
net/http/internal.lo: $(go_net_http_internal_files)
$(BUILDPACKAGE)
net/http/internal/check: $(CHECK_DEPS)
@$(CHECK)
.PHONY: net/http/internal/check
@go_include@ net/http/pprof.lo.dep @go_include@ net/http/pprof.lo.dep
net/http/pprof.lo.dep: $(go_net_http_pprof_files) net/http/pprof.lo.dep: $(go_net_http_pprof_files)
$(BUILDDEPS) $(BUILDDEPS)
@ -5696,6 +5733,15 @@ syscall/check: $(CHECK_DEPS)
@$(CHECK) @$(CHECK)
.PHONY: syscall/check .PHONY: syscall/check
@go_include@ internal/syscall.lo.dep
internal/syscall.lo.dep: $(go_internal_syscall_files)
$(BUILDDEPS)
internal/syscall.lo: $(go_internal_syscall_files)
$(BUILDPACKAGE)
internal/syscall/check: $(CHECK_DEPS)
@$(CHECK)
.PHONY: internal/syscall/check
bufio.gox: bufio.lo bufio.gox: bufio.lo
$(BUILDGOX) $(BUILDGOX)
bytes.gox: bytes.lo bytes.gox: bytes.lo
@ -5951,6 +5997,9 @@ net/http/httputil.gox: net/http/httputil.lo
net/http/pprof.gox: net/http/pprof.lo net/http/pprof.gox: net/http/pprof.lo
$(BUILDGOX) $(BUILDGOX)
net/http/internal.gox: net/http/internal.lo
$(BUILDGOX)
net/rpc/jsonrpc.gox: net/rpc/jsonrpc.lo net/rpc/jsonrpc.gox: net/rpc/jsonrpc.lo
$(BUILDGOX) $(BUILDGOX)
@ -5980,6 +6029,9 @@ runtime/pprof.gox: runtime/pprof.lo
sync/atomic.gox: sync/atomic.lo sync/atomic.gox: sync/atomic.lo
$(BUILDGOX) $(BUILDGOX)
internal/syscall.gox: internal/syscall.lo
$(BUILDGOX)
text/scanner.gox: text/scanner.lo text/scanner.gox: text/scanner.lo
$(BUILDGOX) $(BUILDGOX)
text/tabwriter.gox: text/tabwriter.lo text/tabwriter.gox: text/tabwriter.lo

View File

@ -319,6 +319,9 @@
/* Define to 1 if you have the `unlinkat' function. */ /* Define to 1 if you have the `unlinkat' function. */
#undef HAVE_UNLINKAT #undef HAVE_UNLINKAT
/* Define to 1 if you have the `unsetenv' function. */
#undef HAVE_UNSETENV
/* Define to 1 if you have the `unshare' function. */ /* Define to 1 if you have the `unshare' function. */
#undef HAVE_UNSHARE #undef HAVE_UNSHARE

View File

@ -2511,7 +2511,7 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
ac_config_headers="$ac_config_headers config.h" ac_config_headers="$ac_config_headers config.h"
libtool_VERSION=6:0:0 libtool_VERSION=7:0:0
# Default to --enable-multilib # Default to --enable-multilib
@ -14805,7 +14805,7 @@ else
fi fi
for ac_func in strerror_r strsignal wait4 mincore setenv dl_iterate_phdr for ac_func in strerror_r strsignal wait4 mincore setenv unsetenv dl_iterate_phdr
do : do :
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"

View File

@ -11,7 +11,7 @@ AC_INIT(package-unused, version-unused,, libgo)
AC_CONFIG_SRCDIR(Makefile.am) AC_CONFIG_SRCDIR(Makefile.am)
AC_CONFIG_HEADER(config.h) AC_CONFIG_HEADER(config.h)
libtool_VERSION=6:0:0 libtool_VERSION=7:0:0
AC_SUBST(libtool_VERSION) AC_SUBST(libtool_VERSION)
AM_ENABLE_MULTILIB(, ..) AM_ENABLE_MULTILIB(, ..)
@ -551,7 +551,7 @@ fi
AM_CONDITIONAL(HAVE_SYS_MMAN_H, test "$ac_cv_header_sys_mman_h" = yes) AM_CONDITIONAL(HAVE_SYS_MMAN_H, test "$ac_cv_header_sys_mman_h" = yes)
AC_CHECK_FUNCS(strerror_r strsignal wait4 mincore setenv dl_iterate_phdr) AC_CHECK_FUNCS(strerror_r strsignal wait4 mincore setenv unsetenv dl_iterate_phdr)
AM_CONDITIONAL(HAVE_STRERROR_R, test "$ac_cv_func_strerror_r" = yes) AM_CONDITIONAL(HAVE_STRERROR_R, test "$ac_cv_func_strerror_r" = yes)
AM_CONDITIONAL(HAVE_WAIT4, test "$ac_cv_func_wait4" = yes) AM_CONDITIONAL(HAVE_WAIT4, test "$ac_cv_func_wait4" = yes)

View File

@ -29,10 +29,11 @@ const maxNanoSecondIntSize = 9
// The Next method advances to the next file in the archive (including the first), // The Next method advances to the next file in the archive (including the first),
// and then it can be treated as an io.Reader to access the file's data. // and then it can be treated as an io.Reader to access the file's data.
type Reader struct { type Reader struct {
r io.Reader r io.Reader
err error err error
pad int64 // amount of padding (ignored) after current file entry pad int64 // amount of padding (ignored) after current file entry
curr numBytesReader // reader for current file entry curr numBytesReader // reader for current file entry
hdrBuff [blockSize]byte // buffer to use in readHeader
} }
// A numBytesReader is an io.Reader with a numBytes method, returning the number // A numBytesReader is an io.Reader with a numBytes method, returning the number
@ -426,7 +427,9 @@ func (tr *Reader) verifyChecksum(header []byte) bool {
} }
func (tr *Reader) readHeader() *Header { func (tr *Reader) readHeader() *Header {
header := make([]byte, blockSize) header := tr.hdrBuff[:]
copy(header, zeroBlock)
if _, tr.err = io.ReadFull(tr.r, header); tr.err != nil { if _, tr.err = io.ReadFull(tr.r, header); tr.err != nil {
return nil return nil
} }

View File

@ -37,8 +37,10 @@ type Writer struct {
nb int64 // number of unwritten bytes for current file entry nb int64 // number of unwritten bytes for current file entry
pad int64 // amount of padding to write after current file entry pad int64 // amount of padding to write after current file entry
closed bool closed bool
usedBinary bool // whether the binary numeric field extension was used usedBinary bool // whether the binary numeric field extension was used
preferPax bool // use pax header instead of binary numeric header preferPax bool // use pax header instead of binary numeric header
hdrBuff [blockSize]byte // buffer to use in writeHeader when writing a regular header
paxHdrBuff [blockSize]byte // buffer to use in writeHeader when writing a pax header
} }
// NewWriter creates a new Writer writing to w. // NewWriter creates a new Writer writing to w.
@ -160,7 +162,18 @@ func (tw *Writer) writeHeader(hdr *Header, allowPax bool) error {
// subsecond time resolution, but for now let's just capture // subsecond time resolution, but for now let's just capture
// too long fields or non ascii characters // too long fields or non ascii characters
header := make([]byte, blockSize) var header []byte
// We need to select which scratch buffer to use carefully,
// since this method is called recursively to write PAX headers.
// If allowPax is true, this is the non-recursive call, and we will use hdrBuff.
// If allowPax is false, we are being called by writePAXHeader, and hdrBuff is
// already being used by the non-recursive call, so we must use paxHdrBuff.
header = tw.hdrBuff[:]
if !allowPax {
header = tw.paxHdrBuff[:]
}
copy(header, zeroBlock)
s := slicer(header) s := slicer(header)
// keep a reference to the filename to allow to overwrite it later if we detect that we can use ustar longnames instead of pax // keep a reference to the filename to allow to overwrite it later if we detect that we can use ustar longnames instead of pax

View File

@ -454,3 +454,38 @@ func TestUSTARLongName(t *testing.T) {
t.Fatal("Couldn't recover long name") t.Fatal("Couldn't recover long name")
} }
} }
func TestValidTypeflagWithPAXHeader(t *testing.T) {
var buffer bytes.Buffer
tw := NewWriter(&buffer)
fileName := strings.Repeat("ab", 100)
hdr := &Header{
Name: fileName,
Size: 4,
Typeflag: 0,
}
if err := tw.WriteHeader(hdr); err != nil {
t.Fatalf("Failed to write header: %s", err)
}
if _, err := tw.Write([]byte("fooo")); err != nil {
t.Fatalf("Failed to write the file's data: %s", err)
}
tw.Close()
tr := NewReader(&buffer)
for {
header, err := tr.Next()
if err == io.EOF {
break
}
if err != nil {
t.Fatalf("Failed to read header: %s", err)
}
if header.Typeflag != 0 {
t.Fatalf("Typeflag should've been 0, found %d", header.Typeflag)
}
}
}

View File

@ -267,8 +267,13 @@ func readDirectoryHeader(f *File, r io.Reader) error {
b = b[size:] b = b[size:]
} }
// Should have consumed the whole header. // Should have consumed the whole header.
if len(b) != 0 { // But popular zip & JAR creation tools are broken and
return ErrFormat // may pad extra zeros at the end, so accept those
// too. See golang.org/issue/8186.
for _, v := range b {
if v != 0 {
return ErrFormat
}
} }
} }
return nil return nil

View File

@ -13,6 +13,7 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"regexp" "regexp"
"strings"
"testing" "testing"
"time" "time"
) )
@ -508,3 +509,25 @@ func returnRecursiveZip() (r io.ReaderAt, size int64) {
b := rZipBytes() b := rZipBytes()
return bytes.NewReader(b), int64(len(b)) return bytes.NewReader(b), int64(len(b))
} }
func TestIssue8186(t *testing.T) {
// Directory headers & data found in the TOC of a JAR file.
dirEnts := []string{
"PK\x01\x02\n\x00\n\x00\x00\b\x00\x004\x9d3?\xaa\x1b\x06\xf0\x81\x02\x00\x00\x81\x02\x00\x00-\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00res/drawable-xhdpi-v4/ic_actionbar_accept.png\xfe\xca\x00\x00\x00",
"PK\x01\x02\n\x00\n\x00\x00\b\x00\x004\x9d3?\x90K\x89\xc7t\n\x00\x00t\n\x00\x00\x0e\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xd1\x02\x00\x00resources.arsc\x00\x00\x00",
"PK\x01\x02\x14\x00\x14\x00\b\b\b\x004\x9d3?\xff$\x18\xed3\x03\x00\x00\xb4\b\x00\x00\x13\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00t\r\x00\x00AndroidManifest.xml",
"PK\x01\x02\x14\x00\x14\x00\b\b\b\x004\x9d3?\x14\xc5K\xab\x192\x02\x00\xc8\xcd\x04\x00\v\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe8\x10\x00\x00classes.dex",
"PK\x01\x02\x14\x00\x14\x00\b\b\b\x004\x9d3?E\x96\nD\xac\x01\x00\x00P\x03\x00\x00&\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00:C\x02\x00res/layout/actionbar_set_wallpaper.xml",
"PK\x01\x02\x14\x00\x14\x00\b\b\b\x004\x9d3?Ļ\x14\xe3\xd8\x01\x00\x00\xd8\x03\x00\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00:E\x02\x00res/layout/wallpaper_cropper.xml",
"PK\x01\x02\x14\x00\x14\x00\b\b\b\x004\x9d3?}\xc1\x15\x9eZ\x01\x00\x00!\x02\x00\x00\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00`G\x02\x00META-INF/MANIFEST.MF",
"PK\x01\x02\x14\x00\x14\x00\b\b\b\x004\x9d3?\xe6\x98Ьo\x01\x00\x00\x84\x02\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xfcH\x02\x00META-INF/CERT.SF",
"PK\x01\x02\x14\x00\x14\x00\b\b\b\x004\x9d3?\xbfP\x96b\x86\x04\x00\x00\xb2\x06\x00\x00\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa9J\x02\x00META-INF/CERT.RSA",
}
for i, s := range dirEnts {
var f File
err := readDirectoryHeader(&f, strings.NewReader(s))
if err != nil {
t.Errorf("error reading #%d: %v", i, err)
}
}
}

View File

@ -34,6 +34,12 @@ func NewWriter(w io.Writer) *Writer {
return &Writer{cw: &countWriter{w: bufio.NewWriter(w)}} return &Writer{cw: &countWriter{w: bufio.NewWriter(w)}}
} }
// Flush flushes any buffered data to the underlying writer.
// Calling Flush is not normally necessary; calling Close is sufficient.
func (w *Writer) Flush() error {
return w.cw.w.(*bufio.Writer).Flush()
}
// Close finishes writing the zip file by writing the central directory. // Close finishes writing the zip file by writing the central directory.
// It does not (and can not) close the underlying writer. // It does not (and can not) close the underlying writer.
func (w *Writer) Close() error { func (w *Writer) Close() error {

View File

@ -6,6 +6,7 @@ package zip
import ( import (
"bytes" "bytes"
"io"
"io/ioutil" "io/ioutil"
"math/rand" "math/rand"
"os" "os"
@ -86,6 +87,24 @@ func TestWriter(t *testing.T) {
} }
} }
func TestWriterFlush(t *testing.T) {
var buf bytes.Buffer
w := NewWriter(struct{ io.Writer }{&buf})
_, err := w.Create("foo")
if err != nil {
t.Fatal(err)
}
if buf.Len() > 0 {
t.Fatalf("Unexpected %d bytes already in buffer", buf.Len())
}
if err := w.Flush(); err != nil {
t.Fatal(err)
}
if buf.Len() == 0 {
t.Fatal("No bytes written after Flush")
}
}
func testCreate(t *testing.T, w *Writer, wt *WriteTest) { func testCreate(t *testing.T, w *Writer, wt *WriteTest) {
header := &FileHeader{ header := &FileHeader{
Name: wt.Name, Name: wt.Name,

View File

@ -30,8 +30,8 @@ var (
// Reader implements buffering for an io.Reader object. // Reader implements buffering for an io.Reader object.
type Reader struct { type Reader struct {
buf []byte buf []byte
rd io.Reader rd io.Reader // reader provided by the client
r, w int r, w int // buf read and write positions
err error err error
lastByte int lastByte int
lastRuneSize int lastRuneSize int
@ -131,18 +131,17 @@ func (b *Reader) Peek(n int) ([]byte, error) {
for b.w-b.r < n && b.err == nil { for b.w-b.r < n && b.err == nil {
b.fill() // b.w-b.r < len(b.buf) => buffer is not full b.fill() // b.w-b.r < len(b.buf) => buffer is not full
} }
m := b.w - b.r
if m > n {
m = n
}
var err error var err error
if m < n { if avail := b.w - b.r; avail < n {
// not enough data in buffer
n = avail
err = b.readErr() err = b.readErr()
if err == nil { if err == nil {
err = ErrBufferFull err = ErrBufferFull
} }
} }
return b.buf[b.r : b.r+m], err return b.buf[b.r : b.r+n], err
} }
// Read reads data into p. // Read reads data into p.
@ -173,15 +172,13 @@ func (b *Reader) Read(p []byte) (n int, err error) {
return n, b.readErr() return n, b.readErr()
} }
b.fill() // buffer is empty b.fill() // buffer is empty
if b.w == b.r { if b.r == b.w {
return 0, b.readErr() return 0, b.readErr()
} }
} }
if n > b.w-b.r { // copy as much as we can
n = b.w - b.r n = copy(p, b.buf[b.r:b.w])
}
copy(p[0:n], b.buf[b.r:])
b.r += n b.r += n
b.lastByte = int(b.buf[b.r-1]) b.lastByte = int(b.buf[b.r-1])
b.lastRuneSize = -1 b.lastRuneSize = -1
@ -288,7 +285,7 @@ func (b *Reader) ReadSlice(delim byte) (line []byte, err error) {
} }
// Buffer full? // Buffer full?
if n := b.Buffered(); n >= len(b.buf) { if b.Buffered() >= len(b.buf) {
b.r = b.w b.r = b.w
line = b.buf line = b.buf
err = ErrBufferFull err = ErrBufferFull
@ -301,6 +298,7 @@ func (b *Reader) ReadSlice(delim byte) (line []byte, err error) {
// Handle last byte, if any. // Handle last byte, if any.
if i := len(line) - 1; i >= 0 { if i := len(line) - 1; i >= 0 {
b.lastByte = int(line[i]) b.lastByte = int(line[i])
b.lastRuneSize = -1
} }
return return
@ -458,11 +456,13 @@ func (b *Reader) WriteTo(w io.Writer) (n int64, err error) {
return n, b.readErr() return n, b.readErr()
} }
var errNegativeWrite = errors.New("bufio: writer returned negative count from Write")
// writeBuf writes the Reader's buffer to the writer. // writeBuf writes the Reader's buffer to the writer.
func (b *Reader) writeBuf(w io.Writer) (int64, error) { func (b *Reader) writeBuf(w io.Writer) (int64, error) {
n, err := w.Write(b.buf[b.r:b.w]) n, err := w.Write(b.buf[b.r:b.w])
if n < b.r-b.w { if n < 0 {
panic(errors.New("bufio: writer did not write all data")) panic(errNegativeWrite)
} }
b.r += n b.r += n
return int64(n), err return int64(n), err

View File

@ -31,9 +31,6 @@ func newRot13Reader(r io.Reader) *rot13Reader {
func (r13 *rot13Reader) Read(p []byte) (int, error) { func (r13 *rot13Reader) Read(p []byte) (int, error) {
n, err := r13.r.Read(p) n, err := r13.r.Read(p)
if err != nil {
return n, err
}
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
c := p[i] | 0x20 // lowercase byte c := p[i] | 0x20 // lowercase byte
if 'a' <= c && c <= 'm' { if 'a' <= c && c <= 'm' {
@ -42,7 +39,7 @@ func (r13 *rot13Reader) Read(p []byte) (int, error) {
p[i] -= 13 p[i] -= 13
} }
} }
return n, nil return n, err
} }
// Call ReadByte to accumulate the text of a file // Call ReadByte to accumulate the text of a file
@ -438,7 +435,7 @@ func TestUnreadRuneError(t *testing.T) {
if err != nil { if err != nil {
t.Error("unexpected error on ReadRune (2):", err) t.Error("unexpected error on ReadRune (2):", err)
} }
for _ = range buf { for range buf {
_, err = r.ReadByte() _, err = r.ReadByte()
if err != nil { if err != nil {
t.Error("unexpected error on ReadByte (2):", err) t.Error("unexpected error on ReadByte (2):", err)
@ -463,6 +460,18 @@ func TestUnreadRuneError(t *testing.T) {
if r.UnreadRune() == nil { if r.UnreadRune() == nil {
t.Error("expected error after UnreadByte (3)") t.Error("expected error after UnreadByte (3)")
} }
// Test error after ReadSlice.
_, _, err = r.ReadRune() // reset state
if err != nil {
t.Error("unexpected error on ReadRune (4):", err)
}
_, err = r.ReadSlice(0)
if err != io.EOF {
t.Error("unexpected error on ReadSlice (4):", err)
}
if r.UnreadRune() == nil {
t.Error("expected error after ReadSlice (4)")
}
} }
func TestUnreadRuneAtEOF(t *testing.T) { func TestUnreadRuneAtEOF(t *testing.T) {

View File

@ -36,6 +36,7 @@ type Scanner struct {
start int // First non-processed byte in buf. start int // First non-processed byte in buf.
end int // End of data in buf. end int // End of data in buf.
err error // Sticky error. err error // Sticky error.
empties int // Count of successive empty tokens.
} }
// SplitFunc is the signature of the split function used to tokenize the // SplitFunc is the signature of the split function used to tokenize the
@ -64,8 +65,9 @@ var (
) )
const ( const (
// Maximum size used to buffer a token. The actual maximum token size // MaxScanTokenSize is the maximum size used to buffer a token.
// may be smaller as the buffer may need to include, for instance, a newline. // The actual maximum token size may be smaller as the buffer
// may need to include, for instance, a newline.
MaxScanTokenSize = 64 * 1024 MaxScanTokenSize = 64 * 1024
) )
@ -107,11 +109,15 @@ func (s *Scanner) Text() string {
// After Scan returns false, the Err method will return any error that // After Scan returns false, the Err method will return any error that
// occurred during scanning, except that if it was io.EOF, Err // occurred during scanning, except that if it was io.EOF, Err
// will return nil. // will return nil.
// Split panics if the split function returns 100 empty tokens without
// advancing the input. This is a common error mode for scanners.
func (s *Scanner) Scan() bool { func (s *Scanner) Scan() bool {
// Loop until we have a token. // Loop until we have a token.
for { for {
// See if we can get a token with what we already have. // See if we can get a token with what we already have.
if s.end > s.start { // If we've run out of data but have an error, give the split function
// a chance to recover any remaining, possibly empty token.
if s.end > s.start || s.err != nil {
advance, token, err := s.split(s.buf[s.start:s.end], s.err != nil) advance, token, err := s.split(s.buf[s.start:s.end], s.err != nil)
if err != nil { if err != nil {
s.setErr(err) s.setErr(err)
@ -122,6 +128,15 @@ func (s *Scanner) Scan() bool {
} }
s.token = token s.token = token
if token != nil { if token != nil {
if s.err == nil || advance > 0 {
s.empties = 0
} else {
// Returning tokens not advancing input at EOF.
s.empties++
if s.empties > 100 {
panic("bufio.Scan: 100 empty tokens without progressing")
}
}
return true return true
} }
} }
@ -169,6 +184,7 @@ func (s *Scanner) Scan() bool {
break break
} }
if n > 0 { if n > 0 {
s.empties = 0
break break
} }
loop++ loop++
@ -326,9 +342,6 @@ func ScanWords(data []byte, atEOF bool) (advance int, token []byte, err error) {
break break
} }
} }
if atEOF && len(data) == 0 {
return 0, nil, nil
}
// Scan until space, marking end of word. // Scan until space, marking end of word.
for width, i := 0, start; i < len(data); i += width { for width, i := 0, start; i < len(data); i += width {
var r rune var r rune
@ -342,5 +355,5 @@ func ScanWords(data []byte, atEOF bool) (advance int, token []byte, err error) {
return len(data), data[start:], nil return len(data), data[start:], nil
} }
// Request more data. // Request more data.
return 0, nil, nil return start, nil, nil
} }

View File

@ -15,6 +15,8 @@ import (
"unicode/utf8" "unicode/utf8"
) )
const smallMaxTokenSize = 256 // Much smaller for more efficient testing.
// Test white space table matches the Unicode definition. // Test white space table matches the Unicode definition.
func TestSpace(t *testing.T) { func TestSpace(t *testing.T) {
for r := rune(0); r <= utf8.MaxRune; r++ { for r := rune(0); r <= utf8.MaxRune; r++ {
@ -172,7 +174,6 @@ func genLine(buf *bytes.Buffer, lineNum, n int, addNewline bool) {
// Test the line splitter, including some carriage returns but no long lines. // Test the line splitter, including some carriage returns but no long lines.
func TestScanLongLines(t *testing.T) { func TestScanLongLines(t *testing.T) {
const smallMaxTokenSize = 256 // Much smaller for more efficient testing.
// Build a buffer of lots of line lengths up to but not exceeding smallMaxTokenSize. // Build a buffer of lots of line lengths up to but not exceeding smallMaxTokenSize.
tmp := new(bytes.Buffer) tmp := new(bytes.Buffer)
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
@ -404,3 +405,120 @@ func TestBadReader(t *testing.T) {
t.Errorf("unexpected error: %v", err) t.Errorf("unexpected error: %v", err)
} }
} }
func TestScanWordsExcessiveWhiteSpace(t *testing.T) {
const word = "ipsum"
s := strings.Repeat(" ", 4*smallMaxTokenSize) + word
scanner := NewScanner(strings.NewReader(s))
scanner.MaxTokenSize(smallMaxTokenSize)
scanner.Split(ScanWords)
if !scanner.Scan() {
t.Fatalf("scan failed: %v", scanner.Err())
}
if token := scanner.Text(); token != word {
t.Fatalf("unexpected token: %v", token)
}
}
// Test that empty tokens, including at end of line or end of file, are found by the scanner.
// Issue 8672: Could miss final empty token.
func commaSplit(data []byte, atEOF bool) (advance int, token []byte, err error) {
for i := 0; i < len(data); i++ {
if data[i] == ',' {
return i + 1, data[:i], nil
}
}
if !atEOF {
return 0, nil, nil
}
return 0, data, nil
}
func TestEmptyTokens(t *testing.T) {
s := NewScanner(strings.NewReader("1,2,3,"))
values := []string{"1", "2", "3", ""}
s.Split(commaSplit)
var i int
for i = 0; i < len(values); i++ {
if !s.Scan() {
break
}
if s.Text() != values[i] {
t.Errorf("%d: expected %q got %q", i, values[i], s.Text())
}
}
if i != len(values) {
t.Errorf("got %d fields, expected %d", i, len(values))
}
if err := s.Err(); err != nil {
t.Fatal(err)
}
}
func loopAtEOFSplit(data []byte, atEOF bool) (advance int, token []byte, err error) {
if len(data) > 0 {
return 1, data[:1], nil
}
return 0, data, nil
}
func TestDontLoopForever(t *testing.T) {
s := NewScanner(strings.NewReader("abc"))
s.Split(loopAtEOFSplit)
// Expect a panic
defer func() {
err := recover()
if err == nil {
t.Fatal("should have panicked")
}
if msg, ok := err.(string); !ok || !strings.Contains(msg, "empty tokens") {
panic(err)
}
}()
for count := 0; s.Scan(); count++ {
if count > 1000 {
t.Fatal("looping")
}
}
if s.Err() != nil {
t.Fatal("after scan:", s.Err())
}
}
func TestBlankLines(t *testing.T) {
s := NewScanner(strings.NewReader(strings.Repeat("\n", 1000)))
for count := 0; s.Scan(); count++ {
if count > 2000 {
t.Fatal("looping")
}
}
if s.Err() != nil {
t.Fatal("after scan:", s.Err())
}
}
type countdown int
func (c *countdown) split(data []byte, atEOF bool) (advance int, token []byte, err error) {
if *c > 0 {
*c--
return 1, data[:1], nil
}
return 0, nil, nil
}
// Check that the looping-at-EOF check doesn't trigger for merely empty tokens.
func TestEmptyLinesOK(t *testing.T) {
c := countdown(10000)
s := NewScanner(strings.NewReader(strings.Repeat("\n", 10000)))
s.Split(c.split)
for s.Scan() {
}
if s.Err() != nil {
t.Fatal("after scan:", s.Err())
}
if c != 0 {
t.Fatalf("stopped with %d left to process", c)
}
}

View File

@ -267,6 +267,8 @@ func Fields(s []byte) [][]byte {
// It splits the slice s at each run of code points c satisfying f(c) and // It splits the slice s at each run of code points c satisfying f(c) and
// returns a slice of subslices of s. If all code points in s satisfy f(c), or // returns a slice of subslices of s. If all code points in s satisfy f(c), or
// len(s) == 0, an empty slice is returned. // len(s) == 0, an empty slice is returned.
// FieldsFunc makes no guarantees about the order in which it calls f(c).
// If f does not return consistent results for a given c, FieldsFunc may crash.
func FieldsFunc(s []byte, f func(rune) bool) [][]byte { func FieldsFunc(s []byte, f func(rune) bool) [][]byte {
n := 0 n := 0
inField := false inField := false
@ -377,9 +379,10 @@ func Map(mapping func(r rune) rune, s []byte) []byte {
// Repeat returns a new byte slice consisting of count copies of b. // Repeat returns a new byte slice consisting of count copies of b.
func Repeat(b []byte, count int) []byte { func Repeat(b []byte, count int) []byte {
nb := make([]byte, len(b)*count) nb := make([]byte, len(b)*count)
bp := 0 bp := copy(nb, b)
for i := 0; i < count; i++ { for bp < len(nb) {
bp += copy(nb[bp:], b) copy(nb[bp:], nb[:bp])
bp *= 2
} }
return nb return nb
} }
@ -604,6 +607,9 @@ func Runes(s []byte) []rune {
// Replace returns a copy of the slice s with the first n // Replace returns a copy of the slice s with the first n
// non-overlapping instances of old replaced by new. // non-overlapping instances of old replaced by new.
// If old is empty, it matches at the beginning of the slice
// and after each UTF-8 sequence, yielding up to k+1 replacements
// for a k-rune slice.
// If n < 0, there is no limit on the number of replacements. // If n < 0, there is no limit on the number of replacements.
func Replace(s, old, new []byte, n int) []byte { func Replace(s, old, new []byte, n int) []byte {
m := 0 m := 0

View File

@ -1232,3 +1232,9 @@ func BenchmarkTrimSpace(b *testing.B) {
TrimSpace(s) TrimSpace(s)
} }
} }
func BenchmarkRepeat(b *testing.B) {
for i := 0; i < b.N; i++ {
Repeat([]byte("-"), 80)
}
}

View File

@ -272,7 +272,7 @@ func (f *File) walk(x interface{}, context string, visit func(*File, interface{}
case nil: case nil:
// These are ordered and grouped to match ../../pkg/go/ast/ast.go // These are ordered and grouped to match ../../go/ast/ast.go
case *ast.Field: case *ast.Field:
if len(n.Names) == 0 && context == "field" { if len(n.Names) == 0 && context == "field" {
f.walk(&n.Type, "embed-type", visit) f.walk(&n.Type, "embed-type", visit)
@ -308,6 +308,9 @@ func (f *File) walk(x interface{}, context string, visit func(*File, interface{}
if n.High != nil { if n.High != nil {
f.walk(&n.High, "expr", visit) f.walk(&n.High, "expr", visit)
} }
if n.Max != nil {
f.walk(&n.Max, "expr", visit)
}
case *ast.TypeAssertExpr: case *ast.TypeAssertExpr:
f.walk(&n.X, "expr", visit) f.walk(&n.X, "expr", visit)
f.walk(&n.Type, "type", visit) f.walk(&n.Type, "type", visit)

View File

@ -152,7 +152,7 @@ In C, a function argument written as a fixed size array
actually requires a pointer to the first element of the array. actually requires a pointer to the first element of the array.
C compilers are aware of this calling convention and adjust C compilers are aware of this calling convention and adjust
the call accordingly, but Go cannot. In Go, you must pass the call accordingly, but Go cannot. In Go, you must pass
the pointer to the first element explicitly: C.f(&x[0]). the pointer to the first element explicitly: C.f(&C.x[0]).
A few special functions convert between Go and C types A few special functions convert between Go and C types
by making copies of the data. In pseudo-Go definitions: by making copies of the data. In pseudo-Go definitions:

View File

@ -229,7 +229,8 @@ func (p *Package) guessKinds(f *File) []*Name {
// Determine kinds for names we already know about, // Determine kinds for names we already know about,
// like #defines or 'struct foo', before bothering with gcc. // like #defines or 'struct foo', before bothering with gcc.
var names, needType []*Name var names, needType []*Name
for _, n := range f.Name { for _, key := range nameKeys(f.Name) {
n := f.Name[key]
// If we've already found this name as a #define // If we've already found this name as a #define
// and we can translate it as a constant value, do so. // and we can translate it as a constant value, do so.
if n.Define != "" { if n.Define != "" {
@ -331,6 +332,7 @@ func (p *Package) guessKinds(f *File) []*Name {
const ( const (
notType = 1 << iota notType = 1 << iota
notConst notConst
notDeclared
) )
for _, line := range strings.Split(stderr, "\n") { for _, line := range strings.Split(stderr, "\n") {
if !strings.Contains(line, ": error:") { if !strings.Contains(line, ": error:") {
@ -365,7 +367,7 @@ func (p *Package) guessKinds(f *File) []*Name {
completed = true completed = true
case "not-declared": case "not-declared":
error_(token.NoPos, "%s", strings.TrimSpace(line[c2+1:])) sniff[i] |= notDeclared
case "not-type": case "not-type":
sniff[i] |= notType sniff[i] |= notType
case "not-const": case "not-const":
@ -374,12 +376,12 @@ func (p *Package) guessKinds(f *File) []*Name {
} }
if !completed { if !completed {
fatalf("%s did not produce error at completed:1\non input:\n%s", p.gccBaseCmd()[0], b.Bytes()) fatalf("%s did not produce error at completed:1\non input:\n%s\nfull error output:\n%s", p.gccBaseCmd()[0], b.Bytes(), stderr)
} }
for i, n := range names { for i, n := range names {
switch sniff[i] { switch sniff[i] {
case 0: default:
error_(token.NoPos, "could not determine kind of name for C.%s", fixGo(n.Go)) error_(token.NoPos, "could not determine kind of name for C.%s", fixGo(n.Go))
case notType: case notType:
n.Kind = "const" n.Kind = "const"
@ -390,6 +392,14 @@ func (p *Package) guessKinds(f *File) []*Name {
} }
} }
if nerrors > 0 { if nerrors > 0 {
// Check if compiling the preamble by itself causes any errors,
// because the messages we've printed out so far aren't helpful
// to users debugging preamble mistakes. See issue 8442.
preambleErrors := p.gccErrors([]byte(f.Preamble))
if len(preambleErrors) > 0 {
error_(token.NoPos, "\n%s errors for preamble:\n%s", p.gccBaseCmd()[0], preambleErrors)
}
fatalf("unresolved names") fatalf("unresolved names")
} }
@ -649,7 +659,13 @@ func (p *Package) rewriteRef(f *File) {
f.Name[fpName] = name f.Name[fpName] = name
} }
r.Name = name r.Name = name
expr = ast.NewIdent(name.Mangle) // Rewrite into call to _Cgo_ptr to prevent assignments. The _Cgo_ptr
// function is defined in out.go and simply returns its argument. See
// issue 7757.
expr = &ast.CallExpr{
Fun: &ast.Ident{NamePos: (*r.Expr).Pos(), Name: "_Cgo_ptr"},
Args: []ast.Expr{ast.NewIdent(name.Mangle)},
}
} else if r.Name.Kind == "type" { } else if r.Name.Kind == "type" {
// Okay - might be new(T) // Okay - might be new(T)
expr = r.Name.Type.Go expr = r.Name.Type.Go
@ -928,9 +944,8 @@ type typeConv struct {
// Map from types to incomplete pointers to those types. // Map from types to incomplete pointers to those types.
ptrs map[dwarf.Type][]*Type ptrs map[dwarf.Type][]*Type
// Keys of ptrs in insertion order (deterministic worklist)
// Fields to be processed by godefsField after completing pointers. ptrKeys []dwarf.Type
todoFlds [][]*ast.Field
// Predeclared types. // Predeclared types.
bool ast.Expr bool ast.Expr
@ -940,9 +955,9 @@ type typeConv struct {
float32, float64 ast.Expr float32, float64 ast.Expr
complex64, complex128 ast.Expr complex64, complex128 ast.Expr
void ast.Expr void ast.Expr
unsafePointer ast.Expr
string ast.Expr string ast.Expr
goVoid ast.Expr // _Ctype_void, denotes C's void goVoid ast.Expr // _Ctype_void, denotes C's void
goVoidPtr ast.Expr // unsafe.Pointer or *byte
ptrSize int64 ptrSize int64
intSize int64 intSize int64
@ -972,10 +987,17 @@ func (c *typeConv) Init(ptrSize, intSize int64) {
c.float64 = c.Ident("float64") c.float64 = c.Ident("float64")
c.complex64 = c.Ident("complex64") c.complex64 = c.Ident("complex64")
c.complex128 = c.Ident("complex128") c.complex128 = c.Ident("complex128")
c.unsafePointer = c.Ident("unsafe.Pointer")
c.void = c.Ident("void") c.void = c.Ident("void")
c.string = c.Ident("string") c.string = c.Ident("string")
c.goVoid = c.Ident("_Ctype_void") c.goVoid = c.Ident("_Ctype_void")
// Normally cgo translates void* to unsafe.Pointer,
// but for historical reasons -cdefs and -godefs use *byte instead.
if *cdefs || *godefs {
c.goVoidPtr = &ast.StarExpr{X: c.byte}
} else {
c.goVoidPtr = c.Ident("unsafe.Pointer")
}
} }
// base strips away qualifiers and typedefs to get the underlying type // base strips away qualifiers and typedefs to get the underlying type
@ -1037,29 +1059,22 @@ func (tr *TypeRepr) Set(repr string, fargs ...interface{}) {
} }
// FinishType completes any outstanding type mapping work. // FinishType completes any outstanding type mapping work.
// In particular, it resolves incomplete pointer types and also runs // In particular, it resolves incomplete pointer types.
// godefsFields on any new struct types.
func (c *typeConv) FinishType(pos token.Pos) { func (c *typeConv) FinishType(pos token.Pos) {
// Completing one pointer type might produce more to complete. // Completing one pointer type might produce more to complete.
// Keep looping until they're all done. // Keep looping until they're all done.
for len(c.ptrs) > 0 { for len(c.ptrKeys) > 0 {
for dtype := range c.ptrs { dtype := c.ptrKeys[0]
// Note Type might invalidate c.ptrs[dtype]. c.ptrKeys = c.ptrKeys[1:]
t := c.Type(dtype, pos)
for _, ptr := range c.ptrs[dtype] {
ptr.Go.(*ast.StarExpr).X = t.Go
ptr.C.Set("%s*", t.C)
}
delete(c.ptrs, dtype)
}
}
// Now that pointer types are completed, we can invoke godefsFields // Note Type might invalidate c.ptrs[dtype].
// to rewrite struct definitions. t := c.Type(dtype, pos)
for _, fld := range c.todoFlds { for _, ptr := range c.ptrs[dtype] {
godefsFields(fld) ptr.Go.(*ast.StarExpr).X = t.Go
ptr.C.Set("%s*", t.C)
}
c.ptrs[dtype] = nil // retain the map key
} }
c.todoFlds = nil
} }
// Type returns a *Type with the same memory layout as // Type returns a *Type with the same memory layout as
@ -1072,12 +1087,6 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
return t return t
} }
// clang won't generate DW_AT_byte_size for pointer types,
// so we have to fix it here.
if dt, ok := base(dtype).(*dwarf.PtrType); ok && dt.ByteSize == -1 {
dt.ByteSize = c.ptrSize
}
t := new(Type) t := new(Type)
t.Size = dtype.Size() // note: wrong for array of pointers, corrected below t.Size = dtype.Size() // note: wrong for array of pointers, corrected below
t.Align = -1 t.Align = -1
@ -1101,12 +1110,20 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
t.Go = c.Opaque(t.Size) t.Go = c.Opaque(t.Size)
break break
} }
count := dt.Count
if count == -1 {
// Indicates flexible array member, which Go doesn't support.
// Translate to zero-length array instead.
count = 0
}
sub := c.Type(dt.Type, pos) sub := c.Type(dt.Type, pos)
t.Align = sub.Align t.Align = sub.Align
t.Go = &ast.ArrayType{ t.Go = &ast.ArrayType{
Len: c.intExpr(dt.Count), Len: c.intExpr(count),
Elt: sub.Go, Elt: sub.Go,
} }
// Recalculate t.Size now that we know sub.Size.
t.Size = count * sub.Size
t.C.Set("__typeof__(%s[%d])", sub.C, dt.Count) t.C.Set("__typeof__(%s[%d])", sub.C, dt.Count)
case *dwarf.BoolType: case *dwarf.BoolType:
@ -1207,11 +1224,15 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
} }
case *dwarf.PtrType: case *dwarf.PtrType:
// Clang doesn't emit DW_AT_byte_size for pointer types.
if t.Size != c.ptrSize && t.Size != -1 {
fatalf("%s: unexpected: %d-byte pointer type - %s", lineno(pos), t.Size, dtype)
}
t.Size = c.ptrSize
t.Align = c.ptrSize t.Align = c.ptrSize
// Translate void* as unsafe.Pointer
if _, ok := base(dt.Type).(*dwarf.VoidType); ok { if _, ok := base(dt.Type).(*dwarf.VoidType); ok {
t.Go = c.unsafePointer t.Go = c.goVoidPtr
t.C.Set("void*") t.C.Set("void*")
break break
} }
@ -1219,6 +1240,9 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
// Placeholder initialization; completed in FinishType. // Placeholder initialization; completed in FinishType.
t.Go = &ast.StarExpr{} t.Go = &ast.StarExpr{}
t.C.Set("<incomplete>*") t.C.Set("<incomplete>*")
if _, ok := c.ptrs[dt.Type]; !ok {
c.ptrKeys = append(c.ptrKeys, dt.Type)
}
c.ptrs[dt.Type] = append(c.ptrs[dt.Type], t) c.ptrs[dt.Type] = append(c.ptrs[dt.Type], t)
case *dwarf.QualType: case *dwarf.QualType:
@ -1379,34 +1403,24 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
} }
} }
if t.Size <= 0 { if t.Size < 0 {
// Clang does not record the size of a pointer in its DWARF entry, // Unsized types are [0]byte, unless they're typedefs of other types
// so if dtype is an array, the call to dtype.Size at the top of the function // or structs with tags.
// computed the size as the array length * 0 = 0. // if so, use the name we've already defined.
// The type switch called Type (this function) recursively on the pointer t.Size = 0
// entry, and the code near the top of the function updated the size to switch dt := dtype.(type) {
// be correct, so calling dtype.Size again will produce the correct value. case *dwarf.TypedefType:
t.Size = dtype.Size() // ok
if t.Size < 0 { case *dwarf.StructType:
// Unsized types are [0]byte, unless they're typedefs of other types if dt.StructName != "" {
// or structs with tags. break
// if so, use the name we've already defined.
t.Size = 0
switch dt := dtype.(type) {
case *dwarf.TypedefType:
// ok
case *dwarf.StructType:
if dt.StructName != "" {
break
}
t.Go = c.Opaque(0)
default:
t.Go = c.Opaque(0)
} }
if t.C.Empty() { t.Go = c.Opaque(0)
t.C.Set("void") default:
} t.Go = c.Opaque(0)
return t }
if t.C.Empty() {
t.C.Set("void")
} }
} }
@ -1538,6 +1552,9 @@ func (c *typeConv) pad(fld []*ast.Field, size int64) []*ast.Field {
// Struct conversion: return Go and (6g) C syntax for type. // Struct conversion: return Go and (6g) C syntax for type.
func (c *typeConv) Struct(dt *dwarf.StructType, pos token.Pos) (expr *ast.StructType, csyntax string, align int64) { func (c *typeConv) Struct(dt *dwarf.StructType, pos token.Pos) (expr *ast.StructType, csyntax string, align int64) {
// Minimum alignment for a struct is 1 byte.
align = 1
var buf bytes.Buffer var buf bytes.Buffer
buf.WriteString("struct {") buf.WriteString("struct {")
fld := make([]*ast.Field, 0, 2*len(dt.Field)+1) // enough for padding around every field fld := make([]*ast.Field, 0, 2*len(dt.Field)+1) // enough for padding around every field
@ -1579,7 +1596,27 @@ func (c *typeConv) Struct(dt *dwarf.StructType, pos token.Pos) (expr *ast.Struct
fld = c.pad(fld, f.ByteOffset-off) fld = c.pad(fld, f.ByteOffset-off)
off = f.ByteOffset off = f.ByteOffset
} }
t := c.Type(f.Type, pos)
name := f.Name
ft := f.Type
// In godefs or cdefs mode, if this field is a C11
// anonymous union then treat the first field in the
// union as the field in the struct. This handles
// cases like the glibc <sys/resource.h> file; see
// issue 6677.
if *godefs || *cdefs {
if st, ok := f.Type.(*dwarf.StructType); ok && name == "" && st.Kind == "union" && len(st.Field) > 0 && !used[st.Field[0].Name] {
name = st.Field[0].Name
ident[name] = name
ft = st.Field[0].Type
}
}
// TODO: Handle fields that are anonymous structs by
// promoting the fields of the inner struct.
t := c.Type(ft, pos)
tgo := t.Go tgo := t.Go
size := t.Size size := t.Size
talign := t.Align talign := t.Align
@ -1598,17 +1635,18 @@ func (c *typeConv) Struct(dt *dwarf.StructType, pos token.Pos) (expr *ast.Struct
talign = size talign = size
} }
if talign > 0 && f.ByteOffset%talign != 0 { if talign > 0 && f.ByteOffset%talign != 0 && !*cdefs {
// Drop misaligned fields, the same way we drop integer bit fields. // Drop misaligned fields, the same way we drop integer bit fields.
// The goal is to make available what can be made available. // The goal is to make available what can be made available.
// Otherwise one bad and unneeded field in an otherwise okay struct // Otherwise one bad and unneeded field in an otherwise okay struct
// makes the whole program not compile. Much of the time these // makes the whole program not compile. Much of the time these
// structs are in system headers that cannot be corrected. // structs are in system headers that cannot be corrected.
// Exception: In -cdefs mode, we use #pragma pack, so misaligned
// fields should still work.
continue continue
} }
n := len(fld) n := len(fld)
fld = fld[0 : n+1] fld = fld[0 : n+1]
name := f.Name
if name == "" { if name == "" {
name = fmt.Sprintf("anon%d", anon) name = fmt.Sprintf("anon%d", anon)
anon++ anon++
@ -1635,7 +1673,7 @@ func (c *typeConv) Struct(dt *dwarf.StructType, pos token.Pos) (expr *ast.Struct
csyntax = buf.String() csyntax = buf.String()
if *godefs || *cdefs { if *godefs || *cdefs {
c.todoFlds = append(c.todoFlds, fld) godefsFields(fld)
} }
expr = &ast.StructType{Fields: &ast.FieldList{List: fld}} expr = &ast.StructType{Fields: &ast.FieldList{List: fld}}
return return
@ -1673,19 +1711,6 @@ func godefsFields(fld []*ast.Field) {
n.Name = upper(n.Name) n.Name = upper(n.Name)
} }
} }
p := &f.Type
t := *p
if star, ok := t.(*ast.StarExpr); ok {
star = &ast.StarExpr{X: star.X}
*p = star
p = &star.X
t = *p
}
if id, ok := t.(*ast.Ident); ok {
if id.Name == "unsafe.Pointer" {
*p = ast.NewIdent("*byte")
}
}
} }
} }

View File

@ -44,6 +44,7 @@ func (p *Package) writeDefs() {
fmt.Fprintf(fm, "int main() { return 0; }\n") fmt.Fprintf(fm, "int main() { return 0; }\n")
if *importRuntimeCgo { if *importRuntimeCgo {
fmt.Fprintf(fm, "void crosscall2(void(*fn)(void*, int), void *a, int c) { }\n") fmt.Fprintf(fm, "void crosscall2(void(*fn)(void*, int), void *a, int c) { }\n")
fmt.Fprintf(fm, "char* _cgo_topofstack(void) { return (char*)0; }\n")
} else { } else {
// If we're not importing runtime/cgo, we *are* runtime/cgo, // If we're not importing runtime/cgo, we *are* runtime/cgo,
// which provides crosscall2. We just need a prototype. // which provides crosscall2. We just need a prototype.
@ -58,16 +59,14 @@ func (p *Package) writeDefs() {
fmt.Fprintf(fgo2, "// Created by cgo - DO NOT EDIT\n\n") fmt.Fprintf(fgo2, "// Created by cgo - DO NOT EDIT\n\n")
fmt.Fprintf(fgo2, "package %s\n\n", p.PackageName) fmt.Fprintf(fgo2, "package %s\n\n", p.PackageName)
fmt.Fprintf(fgo2, "import \"unsafe\"\n\n") fmt.Fprintf(fgo2, "import \"unsafe\"\n\n")
if *importSyscall {
fmt.Fprintf(fgo2, "import \"syscall\"\n\n")
}
if !*gccgo && *importRuntimeCgo { if !*gccgo && *importRuntimeCgo {
fmt.Fprintf(fgo2, "import _ \"runtime/cgo\"\n\n") fmt.Fprintf(fgo2, "import _ \"runtime/cgo\"\n\n")
} }
fmt.Fprintf(fgo2, "type _ unsafe.Pointer\n\n")
if *importSyscall { if *importSyscall {
fmt.Fprintf(fgo2, "func _Cerrno(dst *error, x int32) { *dst = syscall.Errno(x) }\n") fmt.Fprintf(fgo2, "import \"syscall\"\n\n")
fmt.Fprintf(fgo2, "var _ syscall.Errno\n")
} }
fmt.Fprintf(fgo2, "func _Cgo_ptr(ptr unsafe.Pointer) unsafe.Pointer { return ptr }\n\n")
typedefNames := make([]string, 0, len(typedef)) typedefNames := make([]string, 0, len(typedef))
for name := range typedef { for name := range typedef {
@ -87,9 +86,10 @@ func (p *Package) writeDefs() {
} }
if *gccgo { if *gccgo {
fmt.Fprintf(fc, p.cPrologGccgo()) fmt.Fprint(fc, p.cPrologGccgo())
} else { } else {
fmt.Fprintf(fc, cProlog) fmt.Fprint(fc, cProlog)
fmt.Fprint(fgo2, goProlog)
} }
gccgoSymbolPrefix := p.gccgoSymbolPrefix() gccgoSymbolPrefix := p.gccgoSymbolPrefix()
@ -130,6 +130,7 @@ func (p *Package) writeDefs() {
fmt.Fprintf(fc, `extern void *%s __asm__("%s.%s");`, n.Mangle, gccgoSymbolPrefix, n.Mangle) fmt.Fprintf(fc, `extern void *%s __asm__("%s.%s");`, n.Mangle, gccgoSymbolPrefix, n.Mangle)
fmt.Fprintf(&gccgoInit, "\t%s = %s%s;\n", n.Mangle, amp, n.C) fmt.Fprintf(&gccgoInit, "\t%s = %s%s;\n", n.Mangle, amp, n.C)
} else { } else {
fmt.Fprintf(fc, "#pragma dataflag NOPTR /* C pointer, not heap pointer */ \n")
fmt.Fprintf(fc, "void *·%s = %s%s;\n", n.Mangle, amp, n.C) fmt.Fprintf(fc, "void *·%s = %s%s;\n", n.Mangle, amp, n.C)
} }
fmt.Fprintf(fc, "\n") fmt.Fprintf(fc, "\n")
@ -296,10 +297,6 @@ func (p *Package) structType(n *Name) (string, int64) {
fmt.Fprintf(&buf, "\t\tchar __pad%d[%d];\n", off, pad) fmt.Fprintf(&buf, "\t\tchar __pad%d[%d];\n", off, pad)
off += pad off += pad
} }
if n.AddError {
fmt.Fprint(&buf, "\t\tint e[2*sizeof(void *)/sizeof(int)]; /* error */\n")
off += 2 * p.PtrSize
}
if off == 0 { if off == 0 {
fmt.Fprintf(&buf, "\t\tchar unused;\n") // avoid empty struct fmt.Fprintf(&buf, "\t\tchar unused;\n") // avoid empty struct
} }
@ -334,19 +331,18 @@ func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) {
} }
// Builtins defined in the C prolog. // Builtins defined in the C prolog.
inProlog := name == "CString" || name == "GoString" || name == "GoStringN" || name == "GoBytes" || name == "_CMalloc" inProlog := builtinDefs[name] != ""
cname := fmt.Sprintf("_cgo%s%s", cPrefix, n.Mangle)
paramnames := []string(nil)
for i, param := range d.Type.Params.List {
paramName := fmt.Sprintf("p%d", i)
param.Names = []*ast.Ident{ast.NewIdent(paramName)}
paramnames = append(paramnames, paramName)
}
if *gccgo { if *gccgo {
// Gccgo style hooks. // Gccgo style hooks.
fmt.Fprint(fgo2, "\n") fmt.Fprint(fgo2, "\n")
cname := fmt.Sprintf("_cgo%s%s", cPrefix, n.Mangle)
paramnames := []string(nil)
for i, param := range d.Type.Params.List {
paramName := fmt.Sprintf("p%d", i)
param.Names = []*ast.Ident{ast.NewIdent(paramName)}
paramnames = append(paramnames, paramName)
}
conf.Fprint(fgo2, fset, d) conf.Fprint(fgo2, fset, d)
fmt.Fprint(fgo2, " {\n") fmt.Fprint(fgo2, " {\n")
if !inProlog { if !inProlog {
@ -383,7 +379,7 @@ func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) {
fmt.Fprint(fgo2, "}\n") fmt.Fprint(fgo2, "}\n")
// declare the C function. // declare the C function.
fmt.Fprintf(fgo2, "//extern _cgo%s%s\n", cPrefix, n.Mangle) fmt.Fprintf(fgo2, "//extern %s\n", cname)
d.Name = ast.NewIdent(cname) d.Name = ast.NewIdent(cname)
if n.AddError { if n.AddError {
l := d.Type.Results.List l := d.Type.Results.List
@ -394,61 +390,50 @@ func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) {
return return
} }
conf.Fprint(fgo2, fset, d)
fmt.Fprint(fgo2, "\n")
if inProlog { if inProlog {
fmt.Fprint(fgo2, builtinDefs[name])
return return
} }
var argSize int64
_, argSize = p.structType(n)
// C wrapper calls into gcc, passing a pointer to the argument frame. // C wrapper calls into gcc, passing a pointer to the argument frame.
fmt.Fprintf(fc, "#pragma cgo_import_static _cgo%s%s\n", cPrefix, n.Mangle) fmt.Fprintf(fc, "#pragma cgo_import_static %s\n", cname)
fmt.Fprintf(fc, "void _cgo%s%s(void*);\n", cPrefix, n.Mangle) fmt.Fprintf(fc, "void %s(void*);\n", cname)
fmt.Fprintf(fc, "\n") fmt.Fprintf(fc, "#pragma dataflag NOPTR\n")
fmt.Fprintf(fc, "void\n") fmt.Fprintf(fc, "void *·%s = %s;\n", cname, cname)
if argSize == 0 {
argSize++ nret := 0
if !void {
d.Type.Results.List[0].Names = []*ast.Ident{ast.NewIdent("r1")}
nret = 1
} }
// TODO(rsc): The struct here should declare pointers only where
// there are pointers in the actual argument frame.
// This is a workaround for golang.org/issue/6397.
fmt.Fprintf(fc, "·%s(struct{", n.Mangle)
if n := argSize / p.PtrSize; n > 0 {
fmt.Fprintf(fc, "void *y[%d];", n)
}
if n := argSize % p.PtrSize; n > 0 {
fmt.Fprintf(fc, "uint8 x[%d];", n)
}
fmt.Fprintf(fc, "}p)\n")
fmt.Fprintf(fc, "{\n")
fmt.Fprintf(fc, "\truntime·cgocall(_cgo%s%s, &p);\n", cPrefix, n.Mangle)
if n.AddError { if n.AddError {
// gcc leaves errno in first word of interface at end of p. d.Type.Results.List[nret].Names = []*ast.Ident{ast.NewIdent("r2")}
// check whether it is zero; if so, turn interface into nil.
// if not, turn interface into errno.
// Go init function initializes ·_Cerrno with an os.Errno
// for us to copy.
fmt.Fprintln(fc, ` {
int32 e;
void **v;
v = (void**)(&p+1) - 2; /* v = final two void* of p */
e = *(int32*)v;
v[0] = (void*)0xdeadbeef;
v[1] = (void*)0xdeadbeef;
if(e == 0) {
/* nil interface */
v[0] = 0;
v[1] = 0;
} else {
·_Cerrno(v, e); /* fill in v as error for errno e */
}
}`)
} }
fmt.Fprintf(fc, "}\n")
fmt.Fprintf(fc, "\n") fmt.Fprint(fgo2, "\n")
fmt.Fprintf(fgo2, "var %s unsafe.Pointer\n", cname)
conf.Fprint(fgo2, fset, d)
fmt.Fprint(fgo2, " {\n")
// NOTE: Using uintptr to hide from escape analysis.
arg := "0"
if len(paramnames) > 0 {
arg = "uintptr(unsafe.Pointer(&p0))"
} else if !void {
arg = "uintptr(unsafe.Pointer(&r1))"
}
prefix := ""
if n.AddError {
prefix = "errno := "
}
fmt.Fprintf(fgo2, "\t%s_cgo_runtime_cgocall_errno(%s, %s)\n", prefix, cname, arg)
if n.AddError {
fmt.Fprintf(fgo2, "\tif errno != 0 { r2 = syscall.Errno(errno) }\n")
}
fmt.Fprintf(fgo2, "\treturn\n")
fmt.Fprintf(fgo2, "}\n")
} }
// writeOutput creates stubs for a specific source file to be compiled by 6g // writeOutput creates stubs for a specific source file to be compiled by 6g
@ -521,7 +506,11 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) {
// Gcc wrapper unpacks the C argument struct // Gcc wrapper unpacks the C argument struct
// and calls the actual C function. // and calls the actual C function.
fmt.Fprintf(fgcc, "void\n") if n.AddError {
fmt.Fprintf(fgcc, "int\n")
} else {
fmt.Fprintf(fgcc, "void\n")
}
fmt.Fprintf(fgcc, "_cgo%s%s(void *v)\n", cPrefix, n.Mangle) fmt.Fprintf(fgcc, "_cgo%s%s(void *v)\n", cPrefix, n.Mangle)
fmt.Fprintf(fgcc, "{\n") fmt.Fprintf(fgcc, "{\n")
if n.AddError { if n.AddError {
@ -531,9 +520,13 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) {
// Use packed attribute to force no padding in this struct in case // Use packed attribute to force no padding in this struct in case
// gcc has different packing requirements. // gcc has different packing requirements.
fmt.Fprintf(fgcc, "\t%s %v *a = v;\n", ctype, p.packedAttribute()) fmt.Fprintf(fgcc, "\t%s %v *a = v;\n", ctype, p.packedAttribute())
if n.FuncType.Result != nil {
// Save the stack top for use below.
fmt.Fprintf(fgcc, "\tchar *stktop = _cgo_topofstack();\n")
}
fmt.Fprintf(fgcc, "\t") fmt.Fprintf(fgcc, "\t")
if t := n.FuncType.Result; t != nil { if t := n.FuncType.Result; t != nil {
fmt.Fprintf(fgcc, "a->r = ") fmt.Fprintf(fgcc, "__typeof__(a->r) r = ")
if c := t.C.String(); c[len(c)-1] == '*' { if c := t.C.String(); c[len(c)-1] == '*' {
fmt.Fprint(fgcc, "(__typeof__(a->r)) ") fmt.Fprint(fgcc, "(__typeof__(a->r)) ")
} }
@ -556,8 +549,15 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) {
fmt.Fprintf(fgcc, "a->p%d", i) fmt.Fprintf(fgcc, "a->p%d", i)
} }
fmt.Fprintf(fgcc, ");\n") fmt.Fprintf(fgcc, ");\n")
if n.FuncType.Result != nil {
// The cgo call may have caused a stack copy (via a callback).
// Adjust the return value pointer appropriately.
fmt.Fprintf(fgcc, "\ta = (void*)((char*)a + (_cgo_topofstack() - stktop));\n")
// Save the return value.
fmt.Fprintf(fgcc, "\ta->r = r;\n")
}
if n.AddError { if n.AddError {
fmt.Fprintf(fgcc, "\t*(int*)(a->e) = errno;\n") fmt.Fprintf(fgcc, "\treturn errno;\n")
} }
fmt.Fprintf(fgcc, "}\n") fmt.Fprintf(fgcc, "}\n")
fmt.Fprintf(fgcc, "\n") fmt.Fprintf(fgcc, "\n")
@ -1016,7 +1016,7 @@ func forFieldList(fl *ast.FieldList, fn func(int, ast.Expr)) {
fn(i, r.Type) fn(i, r.Type)
i++ i++
} else { } else {
for _ = range r.Names { for range r.Names {
fn(i, r.Type) fn(i, r.Type)
i++ i++
} }
@ -1143,21 +1143,17 @@ __cgo_size_assert(__cgo_long_long, 8)
__cgo_size_assert(float, 4) __cgo_size_assert(float, 4)
__cgo_size_assert(double, 8) __cgo_size_assert(double, 8)
extern char* _cgo_topofstack(void);
#include <errno.h> #include <errno.h>
#include <string.h> #include <string.h>
` `
const builtinProlog = ` const builtinProlog = `
#include <sys/types.h> /* for size_t below */ #include <stddef.h> /* for ptrdiff_t and size_t below */
/* Define intgo when compiling with GCC. */ /* Define intgo when compiling with GCC. */
#ifdef __PTRDIFF_TYPE__ typedef ptrdiff_t intgo;
typedef __PTRDIFF_TYPE__ intgo;
#elif defined(_LP64)
typedef long long intgo;
#else
typedef int intgo;
#endif
typedef struct { char *p; intgo n; } _GoString_; typedef struct { char *p; intgo n; } _GoString_;
typedef struct { char *p; intgo n; intgo c; } _GoBytes_; typedef struct { char *p; intgo n; intgo c; } _GoBytes_;
@ -1171,47 +1167,86 @@ void *_CMalloc(size_t);
const cProlog = ` const cProlog = `
#include "runtime.h" #include "runtime.h"
#include "cgocall.h" #include "cgocall.h"
#include "textflag.h"
#pragma dataflag NOPTR
static void *cgocall_errno = runtime·cgocall_errno;
#pragma dataflag NOPTR
void *·_cgo_runtime_cgocall_errno = &cgocall_errno;
#pragma dataflag NOPTR
static void *runtime_gostring = runtime·gostring;
#pragma dataflag NOPTR
void *·_cgo_runtime_gostring = &runtime_gostring;
#pragma dataflag NOPTR
static void *runtime_gostringn = runtime·gostringn;
#pragma dataflag NOPTR
void *·_cgo_runtime_gostringn = &runtime_gostringn;
#pragma dataflag NOPTR
static void *runtime_gobytes = runtime·gobytes;
#pragma dataflag NOPTR
void *·_cgo_runtime_gobytes = &runtime_gobytes;
#pragma dataflag NOPTR
static void *runtime_cmalloc = runtime·cmalloc;
#pragma dataflag NOPTR
void *·_cgo_runtime_cmalloc = &runtime_cmalloc;
void ·_Cerrno(void*, int32); void ·_Cerrno(void*, int32);
`
void const goProlog = `
·_Cfunc_GoString(int8 *p, String s) var _cgo_runtime_cgocall_errno func(unsafe.Pointer, uintptr) int32
{ var _cgo_runtime_cmalloc func(uintptr) unsafe.Pointer
s = runtime·gostring((byte*)p); `
FLUSH(&s);
}
void const goStringDef = `
·_Cfunc_GoStringN(int8 *p, int32 l, String s) var _cgo_runtime_gostring func(*_Ctype_char) string
{ func _Cfunc_GoString(p *_Ctype_char) string {
s = runtime·gostringn((byte*)p, l); return _cgo_runtime_gostring(p)
FLUSH(&s);
}
void
·_Cfunc_GoBytes(int8 *p, int32 l, Slice s)
{
s = runtime·gobytes((byte*)p, l);
FLUSH(&s);
}
void
·_Cfunc_CString(String s, int8 *p)
{
p = runtime·cmalloc(s.len+1);
runtime·memmove((byte*)p, s.str, s.len);
p[s.len] = 0;
FLUSH(&p);
}
void
·_Cfunc__CMalloc(uintptr n, int8 *p)
{
p = runtime·cmalloc(n);
FLUSH(&p);
} }
` `
const goStringNDef = `
var _cgo_runtime_gostringn func(*_Ctype_char, int) string
func _Cfunc_GoStringN(p *_Ctype_char, l _Ctype_int) string {
return _cgo_runtime_gostringn(p, int(l))
}
`
const goBytesDef = `
var _cgo_runtime_gobytes func(unsafe.Pointer, int) []byte
func _Cfunc_GoBytes(p unsafe.Pointer, l _Ctype_int) []byte {
return _cgo_runtime_gobytes(p, int(l))
}
`
const cStringDef = `
func _Cfunc_CString(s string) *_Ctype_char {
p := _cgo_runtime_cmalloc(uintptr(len(s)+1))
pp := (*[1<<30]byte)(p)
copy(pp[:], s)
pp[len(s)] = 0
return (*_Ctype_char)(p)
}
`
const cMallocDef = `
func _Cfunc__CMalloc(n _Ctype_size_t) unsafe.Pointer {
return _cgo_runtime_cmalloc(uintptr(n))
}
`
var builtinDefs = map[string]string{
"GoString": goStringDef,
"GoStringN": goStringNDef,
"GoBytes": goBytesDef,
"CString": cStringDef,
"_CMalloc": cMallocDef,
}
func (p *Package) cPrologGccgo() string { func (p *Package) cPrologGccgo() string {
return strings.Replace(cPrologGccgo, "PREFIX", cPrefix, -1) return strings.Replace(cPrologGccgo, "PREFIX", cPrefix, -1)
} }

View File

@ -57,6 +57,7 @@ and test commands:
-a -a
force rebuilding of packages that are already up-to-date. force rebuilding of packages that are already up-to-date.
In Go releases, does not apply to the standard library.
-n -n
print the commands but do not run them. print the commands but do not run them.
-p n -p n
@ -64,7 +65,7 @@ and test commands:
The default is the number of CPUs available. The default is the number of CPUs available.
-race -race
enable data race detection. enable data race detection.
Supported only on linux/amd64, darwin/amd64 and windows/amd64. Supported only on linux/amd64, freebsd/amd64, darwin/amd64 and windows/amd64.
-v -v
print the names of packages as they are compiled. print the names of packages as they are compiled.
-work -work
@ -291,23 +292,26 @@ func runBuild(cmd *Command, args []string) {
} }
} }
depMode := modeBuild
if buildI {
depMode = modeInstall
}
if *buildO != "" { if *buildO != "" {
if len(pkgs) > 1 { if len(pkgs) > 1 {
fatalf("go build: cannot use -o with multiple packages") fatalf("go build: cannot use -o with multiple packages")
} else if len(pkgs) == 0 {
fatalf("no packages to build")
} }
p := pkgs[0] p := pkgs[0]
p.target = "" // must build - not up to date p.target = "" // must build - not up to date
a := b.action(modeInstall, modeBuild, p) a := b.action(modeInstall, depMode, p)
a.target = *buildO a.target = *buildO
b.do(a) b.do(a)
return return
} }
a := &action{} a := &action{}
depMode := modeBuild
if buildI {
depMode = modeInstall
}
for _, p := range packages(args) { for _, p := range packages(args) {
a.deps = append(a.deps, b.action(modeBuild, depMode, p)) a.deps = append(a.deps, b.action(modeBuild, depMode, p))
} }
@ -438,12 +442,11 @@ const (
) )
var ( var (
goroot = filepath.Clean(runtime.GOROOT()) goroot = filepath.Clean(runtime.GOROOT())
gobin = os.Getenv("GOBIN") gobin = os.Getenv("GOBIN")
gorootBin = filepath.Join(goroot, "bin") gorootBin = filepath.Join(goroot, "bin")
gorootSrcPkg = filepath.Join(goroot, "src/pkg") gorootPkg = filepath.Join(goroot, "pkg")
gorootPkg = filepath.Join(goroot, "pkg") gorootSrc = filepath.Join(goroot, "src")
gorootSrc = filepath.Join(goroot, "src")
) )
func (b *builder) init() { func (b *builder) init() {
@ -510,8 +513,13 @@ func goFilesPackage(gofiles []string) *Package {
} }
ctxt.ReadDir = func(string) ([]os.FileInfo, error) { return dirent, nil } ctxt.ReadDir = func(string) ([]os.FileInfo, error) { return dirent, nil }
if !filepath.IsAbs(dir) { var err error
dir = filepath.Join(cwd, dir) if dir == "" {
dir = cwd
}
dir, err = filepath.Abs(dir)
if err != nil {
fatalf("%s", err)
} }
bp, err := ctxt.ImportDir(dir, 0) bp, err := ctxt.ImportDir(dir, 0)
@ -833,12 +841,17 @@ func (b *builder) build(a *action) (err error) {
} }
} }
var gofiles, cfiles, sfiles, objects, cgoObjects []string var gofiles, cfiles, sfiles, objects, cgoObjects, pcCFLAGS, pcLDFLAGS []string
gofiles = append(gofiles, a.p.GoFiles...) gofiles = append(gofiles, a.p.GoFiles...)
cfiles = append(cfiles, a.p.CFiles...) cfiles = append(cfiles, a.p.CFiles...)
sfiles = append(sfiles, a.p.SFiles...) sfiles = append(sfiles, a.p.SFiles...)
if a.p.usesCgo() || a.p.usesSwig() {
if pcCFLAGS, pcLDFLAGS, err = b.getPkgConfigFlags(a.p); err != nil {
return
}
}
// Run cgo. // Run cgo.
if a.p.usesCgo() { if a.p.usesCgo() {
// In a package using cgo, cgo compiles the C, C++ and assembly files with gcc. // In a package using cgo, cgo compiles the C, C++ and assembly files with gcc.
@ -869,7 +882,7 @@ func (b *builder) build(a *action) (err error) {
if a.cgo != nil && a.cgo.target != "" { if a.cgo != nil && a.cgo.target != "" {
cgoExe = a.cgo.target cgoExe = a.cgo.target
} }
outGo, outObj, err := b.cgo(a.p, cgoExe, obj, gccfiles, a.p.CXXFiles, a.p.MFiles) outGo, outObj, err := b.cgo(a.p, cgoExe, obj, pcCFLAGS, pcLDFLAGS, gccfiles, a.p.CXXFiles, a.p.MFiles)
if err != nil { if err != nil {
return err return err
} }
@ -882,9 +895,18 @@ func (b *builder) build(a *action) (err error) {
// In a package using SWIG, any .c or .s files are // In a package using SWIG, any .c or .s files are
// compiled with gcc. // compiled with gcc.
gccfiles := append(cfiles, sfiles...) gccfiles := append(cfiles, sfiles...)
cxxfiles, mfiles := a.p.CXXFiles, a.p.MFiles
cfiles = nil cfiles = nil
sfiles = nil sfiles = nil
outGo, outObj, err := b.swig(a.p, obj, gccfiles, a.p.CXXFiles, a.p.MFiles)
// Don't build c/c++ files twice if cgo is enabled (mainly for pkg-config).
if a.p.usesCgo() {
cxxfiles = nil
gccfiles = nil
mfiles = nil
}
outGo, outObj, err := b.swig(a.p, obj, pcCFLAGS, gccfiles, cxxfiles, mfiles)
if err != nil { if err != nil {
return err return err
} }
@ -893,7 +915,7 @@ func (b *builder) build(a *action) (err error) {
} }
if len(gofiles) == 0 { if len(gofiles) == 0 {
return &build.NoGoError{a.p.Dir} return &build.NoGoError{Dir: a.p.Dir}
} }
// If we're doing coverage, preprocess the .go files and put them in the work directory // If we're doing coverage, preprocess the .go files and put them in the work directory
@ -1028,6 +1050,34 @@ func (b *builder) build(a *action) (err error) {
return nil return nil
} }
// Calls pkg-config if needed and returns the cflags/ldflags needed to build the package.
func (b *builder) getPkgConfigFlags(p *Package) (cflags, ldflags []string, err error) {
if pkgs := p.CgoPkgConfig; len(pkgs) > 0 {
var out []byte
out, err = b.runOut(p.Dir, p.ImportPath, nil, "pkg-config", "--cflags", pkgs)
if err != nil {
b.showOutput(p.Dir, "pkg-config --cflags "+strings.Join(pkgs, " "), string(out))
b.print(err.Error() + "\n")
err = errPrintedOutput
return
}
if len(out) > 0 {
cflags = strings.Fields(string(out))
}
out, err = b.runOut(p.Dir, p.ImportPath, nil, "pkg-config", "--libs", pkgs)
if err != nil {
b.showOutput(p.Dir, "pkg-config --libs "+strings.Join(pkgs, " "), string(out))
b.print(err.Error() + "\n")
err = errPrintedOutput
return
}
if len(out) > 0 {
ldflags = strings.Fields(string(out))
}
}
return
}
// install is the action for installing a single package or executable. // install is the action for installing a single package or executable.
func (b *builder) install(a *action) (err error) { func (b *builder) install(a *action) (err error) {
defer func() { defer func() {
@ -1263,7 +1313,7 @@ func (b *builder) showcmd(dir string, format string, args ...interface{}) {
// the source directory for the package that has failed to build. // the source directory for the package that has failed to build.
// showOutput rewrites mentions of dir with a relative path to dir // showOutput rewrites mentions of dir with a relative path to dir
// when the relative path is shorter. This is usually more pleasant. // when the relative path is shorter. This is usually more pleasant.
// For example, if fmt doesn't compile and we are in src/pkg/html, // For example, if fmt doesn't compile and we are in src/html,
// the output is // the output is
// //
// $ go build // $ go build
@ -1275,7 +1325,7 @@ func (b *builder) showcmd(dir string, format string, args ...interface{}) {
// //
// $ go build // $ go build
// # fmt // # fmt
// /usr/gopher/go/src/pkg/fmt/print.go:1090: undefined: asdf // /usr/gopher/go/src/fmt/print.go:1090: undefined: asdf
// $ // $
// //
// showOutput also replaces references to the work directory with $WORK. // showOutput also replaces references to the work directory with $WORK.
@ -1435,6 +1485,14 @@ func (b *builder) runOut(dir string, desc string, env []string, cmdargs ...inter
continue continue
} }
// err can be something like 'exit status 1'.
// Add information about what program was running.
// Note that if buf.Bytes() is non-empty, the caller usually
// shows buf.Bytes() and does not print err at all, so the
// prefix here does not make most output any more verbose.
if err != nil {
err = errors.New(cmdline[0] + ": " + err.Error())
}
return buf.Bytes(), err return buf.Bytes(), err
} }
} }
@ -1597,7 +1655,7 @@ func (gcToolchain) gc(b *builder, p *Package, archive, obj string, importArgs []
extFiles := len(p.CgoFiles) + len(p.CFiles) + len(p.CXXFiles) + len(p.MFiles) + len(p.SFiles) + len(p.SysoFiles) + len(p.SwigFiles) + len(p.SwigCXXFiles) extFiles := len(p.CgoFiles) + len(p.CFiles) + len(p.CXXFiles) + len(p.MFiles) + len(p.SFiles) + len(p.SysoFiles) + len(p.SwigFiles) + len(p.SwigCXXFiles)
if p.Standard { if p.Standard {
switch p.ImportPath { switch p.ImportPath {
case "os", "runtime/pprof", "sync", "time": case "bytes", "net", "os", "runtime/pprof", "sync", "time":
extFiles++ extFiles++
} }
} }
@ -1621,8 +1679,10 @@ func (gcToolchain) gc(b *builder, p *Package, archive, obj string, importArgs []
} }
func (gcToolchain) asm(b *builder, p *Package, obj, ofile, sfile string) error { func (gcToolchain) asm(b *builder, p *Package, obj, ofile, sfile string) error {
// Add -I pkg/GOOS_GOARCH so #include "textflag.h" works in .s files.
inc := filepath.Join(goroot, "pkg", fmt.Sprintf("%s_%s", goos, goarch))
sfile = mkAbs(p.Dir, sfile) sfile = mkAbs(p.Dir, sfile)
return b.run(p.Dir, p.ImportPath, nil, tool(archChar+"a"), "-trimpath", b.work, "-I", obj, "-o", ofile, "-D", "GOOS_"+goos, "-D", "GOARCH_"+goarch, sfile) return b.run(p.Dir, p.ImportPath, nil, tool(archChar+"a"), "-trimpath", b.work, "-I", obj, "-I", inc, "-o", ofile, "-D", "GOOS_"+goos, "-D", "GOARCH_"+goarch, sfile)
} }
func (gcToolchain) pkgpath(basedir string, p *Package) string { func (gcToolchain) pkgpath(basedir string, p *Package) string {
@ -1716,7 +1776,7 @@ func packInternal(b *builder, afile string, ofiles []string) error {
func (gcToolchain) ld(b *builder, p *Package, out string, allactions []*action, mainpkg string, ofiles []string) error { func (gcToolchain) ld(b *builder, p *Package, out string, allactions []*action, mainpkg string, ofiles []string) error {
importArgs := b.includeArgs("-L", allactions) importArgs := b.includeArgs("-L", allactions)
cxx := false cxx := len(p.CXXFiles) > 0
for _, a := range allactions { for _, a := range allactions {
if a.p != nil && len(a.p.CXXFiles) > 0 { if a.p != nil && len(a.p.CXXFiles) > 0 {
cxx = true cxx = true
@ -1776,7 +1836,15 @@ func (gcToolchain) ld(b *builder, p *Package, out string, allactions []*action,
func (gcToolchain) cc(b *builder, p *Package, objdir, ofile, cfile string) error { func (gcToolchain) cc(b *builder, p *Package, objdir, ofile, cfile string) error {
inc := filepath.Join(goroot, "pkg", fmt.Sprintf("%s_%s", goos, goarch)) inc := filepath.Join(goroot, "pkg", fmt.Sprintf("%s_%s", goos, goarch))
cfile = mkAbs(p.Dir, cfile) cfile = mkAbs(p.Dir, cfile)
args := stringList(tool(archChar+"c"), "-F", "-V", "-w", "-trimpath", b.work, "-I", objdir, "-I", inc, "-o", ofile, buildCcflags, "-D", "GOOS_"+goos, "-D", "GOARCH_"+goarch, cfile) warn := []string{"-w"}
if p.usesSwig() {
// When using SWIG, this compiler is only used to
// compile the C files generated by SWIG.
// We don't want warnings.
// See issue 9065 for details.
warn = nil
}
args := stringList(tool(archChar+"c"), "-F", "-V", warn, "-trimpath", b.work, "-I", objdir, "-I", inc, "-o", ofile, buildCcflags, "-D", "GOOS_"+goos, "-D", "GOARCH_"+goarch, cfile)
return b.run(p.Dir, p.ImportPath, nil, args) return b.run(p.Dir, p.ImportPath, nil, args)
} }
@ -1802,7 +1870,7 @@ func (gccgoToolchain) linker() string {
} }
func (tools gccgoToolchain) gc(b *builder, p *Package, archive, obj string, importArgs []string, gofiles []string) (ofile string, output []byte, err error) { func (tools gccgoToolchain) gc(b *builder, p *Package, archive, obj string, importArgs []string, gofiles []string) (ofile string, output []byte, err error) {
out := p.Name + ".o" out := "_go_.o"
ofile = obj + out ofile = obj + out
gcargs := []string{"-g"} gcargs := []string{"-g"}
gcargs = append(gcargs, b.gccArchArgs()...) gcargs = append(gcargs, b.gccArchArgs()...)
@ -1828,6 +1896,7 @@ func (tools gccgoToolchain) asm(b *builder, p *Package, obj, ofile, sfile string
defs = append(defs, `-D`, `GOPKGPATH="`+pkgpath+`"`) defs = append(defs, `-D`, `GOPKGPATH="`+pkgpath+`"`)
} }
defs = append(defs, b.gccArchArgs()...) defs = append(defs, b.gccArchArgs()...)
return b.run(p.Dir, p.ImportPath, nil, tools.compiler(), "-c", "-I", obj, "-o", ofile, defs, sfile) return b.run(p.Dir, p.ImportPath, nil, tools.compiler(), "-c", "-I", obj, "-o", ofile, defs, sfile)
} }
@ -1854,8 +1923,8 @@ func (tools gccgoToolchain) ld(b *builder, p *Package, out string, allactions []
ldflags := b.gccArchArgs() ldflags := b.gccArchArgs()
cgoldflags := []string{} cgoldflags := []string{}
usesCgo := false usesCgo := false
cxx := false cxx := len(p.CXXFiles) > 0
objc := false objc := len(p.MFiles) > 0
// Prefer the output of an install action to the output of a build action, // Prefer the output of an install action to the output of a build action,
// because the install action will delete the output of the build action. // because the install action will delete the output of the build action.
@ -1917,8 +1986,7 @@ func (gccgoToolchain) cc(b *builder, p *Package, objdir, ofile, cfile string) er
if pkgpath := gccgoCleanPkgpath(p); pkgpath != "" { if pkgpath := gccgoCleanPkgpath(p); pkgpath != "" {
defs = append(defs, `-D`, `GOPKGPATH="`+pkgpath+`"`) defs = append(defs, `-D`, `GOPKGPATH="`+pkgpath+`"`)
} }
// TODO: Support using clang here (during gccgo build)? return b.run(p.Dir, p.ImportPath, nil, envList("CC", defaultCC), "-Wall", "-g",
return b.run(p.Dir, p.ImportPath, nil, "gcc", "-Wall", "-g",
"-I", objdir, "-I", inc, "-o", ofile, defs, "-c", cfile) "-I", objdir, "-I", inc, "-o", ofile, defs, "-c", cfile)
} }
@ -1969,9 +2037,9 @@ func (b *builder) libgcc(p *Package) (string, error) {
return "$LIBGCC", nil return "$LIBGCC", nil
} }
// clang might not be able to find libgcc, and in that case, // The compiler might not be able to find libgcc, and in that case,
// it will simply return "libgcc.a", which is of no use to us. // it will simply return "libgcc.a", which is of no use to us.
if strings.Contains(gccCmd[0], "clang") && !filepath.IsAbs(string(f)) { if !filepath.IsAbs(string(f)) {
return "", nil return "", nil
} }
@ -2109,36 +2177,16 @@ var (
cgoLibGccFileOnce sync.Once cgoLibGccFileOnce sync.Once
) )
func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles, gxxfiles, mfiles []string) (outGo, outObj []string, err error) { func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, gccfiles, gxxfiles, mfiles []string) (outGo, outObj []string, err error) {
cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, cgoLDFLAGS := b.cflags(p, true) cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, cgoLDFLAGS := b.cflags(p, true)
_, cgoexeCFLAGS, _, _ := b.cflags(p, false) _, cgoexeCFLAGS, _, _ := b.cflags(p, false)
cgoCPPFLAGS = append(cgoCPPFLAGS, pcCFLAGS...)
cgoLDFLAGS = append(cgoLDFLAGS, pcLDFLAGS...)
// If we are compiling Objective-C code, then we need to link against libobjc // If we are compiling Objective-C code, then we need to link against libobjc
if len(mfiles) > 0 { if len(mfiles) > 0 {
cgoLDFLAGS = append(cgoLDFLAGS, "-lobjc") cgoLDFLAGS = append(cgoLDFLAGS, "-lobjc")
} }
if pkgs := p.CgoPkgConfig; len(pkgs) > 0 {
out, err := b.runOut(p.Dir, p.ImportPath, nil, "pkg-config", "--cflags", pkgs)
if err != nil {
b.showOutput(p.Dir, "pkg-config --cflags "+strings.Join(pkgs, " "), string(out))
b.print(err.Error() + "\n")
return nil, nil, errPrintedOutput
}
if len(out) > 0 {
cgoCPPFLAGS = append(cgoCPPFLAGS, strings.Fields(string(out))...)
}
out, err = b.runOut(p.Dir, p.ImportPath, nil, "pkg-config", "--libs", pkgs)
if err != nil {
b.showOutput(p.Dir, "pkg-config --libs "+strings.Join(pkgs, " "), string(out))
b.print(err.Error() + "\n")
return nil, nil, errPrintedOutput
}
if len(out) > 0 {
cgoLDFLAGS = append(cgoLDFLAGS, strings.Fields(string(out))...)
}
}
// Allows including _cgo_export.h from .[ch] files in the package. // Allows including _cgo_export.h from .[ch] files in the package.
cgoCPPFLAGS = append(cgoCPPFLAGS, "-I", obj) cgoCPPFLAGS = append(cgoCPPFLAGS, "-I", obj)
@ -2215,6 +2263,14 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles, gxxfiles, mfiles
strings.HasSuffix(f, ".so"), strings.HasSuffix(f, ".so"),
strings.HasSuffix(f, ".dll"): strings.HasSuffix(f, ".dll"):
continue continue
// Remove any -fsanitize=foo flags.
// Otherwise the compiler driver thinks that we are doing final link
// and links sanitizer runtime into the object file. But we are not doing
// the final link, we will link the resulting object file again. And
// so the program ends up with two copies of sanitizer runtime.
// See issue 8788 for details.
case strings.HasPrefix(f, "-fsanitize="):
continue
default: default:
bareLDFLAGS = append(bareLDFLAGS, f) bareLDFLAGS = append(bareLDFLAGS, f)
} }
@ -2281,13 +2337,14 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles, gxxfiles, mfiles
linkobj = append(linkobj, p.SysoFiles...) linkobj = append(linkobj, p.SysoFiles...)
dynobj := obj + "_cgo_.o" dynobj := obj + "_cgo_.o"
if goarch == "arm" && goos == "linux" { // we need to use -pie for Linux/ARM to get accurate imported sym pie := goarch == "arm" && (goos == "linux" || goos == "android")
if pie { // we need to use -pie for Linux/ARM to get accurate imported sym
cgoLDFLAGS = append(cgoLDFLAGS, "-pie") cgoLDFLAGS = append(cgoLDFLAGS, "-pie")
} }
if err := b.gccld(p, dynobj, cgoLDFLAGS, linkobj); err != nil { if err := b.gccld(p, dynobj, cgoLDFLAGS, linkobj); err != nil {
return nil, nil, err return nil, nil, err
} }
if goarch == "arm" && goos == "linux" { // but we don't need -pie for normal cgo programs if pie { // but we don't need -pie for normal cgo programs
cgoLDFLAGS = cgoLDFLAGS[0 : len(cgoLDFLAGS)-1] cgoLDFLAGS = cgoLDFLAGS[0 : len(cgoLDFLAGS)-1]
} }
@ -2321,7 +2378,23 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles, gxxfiles, mfiles
nonGccObjs = append(nonGccObjs, f) nonGccObjs = append(nonGccObjs, f)
} }
} }
if err := b.gccld(p, ofile, stringList(bareLDFLAGS, "-Wl,-r", "-nostdlib", staticLibs), gccObjs); err != nil { ldflags := stringList(bareLDFLAGS, "-Wl,-r", "-nostdlib", staticLibs)
// Some systems, such as Ubuntu, always add --build-id to
// every link, but we don't want a build ID since we are
// producing an object file. On some of those system a plain
// -r (not -Wl,-r) will turn off --build-id, but clang 3.0
// doesn't support a plain -r. I don't know how to turn off
// --build-id when using clang other than passing a trailing
// --build-id=none. So that is what we do, but only on
// systems likely to support it, which is to say, systems that
// normally use gold or the GNU linker.
switch goos {
case "android", "dragonfly", "linux", "netbsd":
ldflags = append(ldflags, "-Wl,--build-id=none")
}
if err := b.gccld(p, ofile, ldflags, gccObjs); err != nil {
return nil, nil, err return nil, nil, err
} }
@ -2336,7 +2409,7 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles, gxxfiles, mfiles
// Run SWIG on all SWIG input files. // Run SWIG on all SWIG input files.
// TODO: Don't build a shared library, once SWIG emits the necessary // TODO: Don't build a shared library, once SWIG emits the necessary
// pragmas for external linking. // pragmas for external linking.
func (b *builder) swig(p *Package, obj string, gccfiles, gxxfiles, mfiles []string) (outGo, outObj []string, err error) { func (b *builder) swig(p *Package, obj string, pcCFLAGS, gccfiles, gxxfiles, mfiles []string) (outGo, outObj []string, err error) {
cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, _ := b.cflags(p, true) cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, _ := b.cflags(p, true)
cflags := stringList(cgoCPPFLAGS, cgoCFLAGS) cflags := stringList(cgoCPPFLAGS, cgoCFLAGS)
cxxflags := stringList(cgoCPPFLAGS, cgoCXXFLAGS) cxxflags := stringList(cgoCPPFLAGS, cgoCXXFLAGS)
@ -2377,7 +2450,7 @@ func (b *builder) swig(p *Package, obj string, gccfiles, gxxfiles, mfiles []stri
} }
for _, f := range p.SwigFiles { for _, f := range p.SwigFiles {
goFile, objFile, gccObjFile, err := b.swigOne(p, f, obj, false, intgosize) goFile, objFile, gccObjFile, err := b.swigOne(p, f, obj, pcCFLAGS, false, intgosize)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@ -2392,7 +2465,7 @@ func (b *builder) swig(p *Package, obj string, gccfiles, gxxfiles, mfiles []stri
} }
} }
for _, f := range p.SwigCXXFiles { for _, f := range p.SwigCXXFiles {
goFile, objFile, gccObjFile, err := b.swigOne(p, f, obj, true, intgosize) goFile, objFile, gccObjFile, err := b.swigOne(p, f, obj, pcCFLAGS, true, intgosize)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@ -2471,13 +2544,13 @@ func (b *builder) swigIntSize(obj string) (intsize string, err error) {
} }
// Run SWIG on one SWIG input file. // Run SWIG on one SWIG input file.
func (b *builder) swigOne(p *Package, file, obj string, cxx bool, intgosize string) (outGo, outObj, objGccObj string, err error) { func (b *builder) swigOne(p *Package, file, obj string, pcCFLAGS []string, cxx bool, intgosize string) (outGo, outObj, objGccObj string, err error) {
cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, _ := b.cflags(p, true) cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, _ := b.cflags(p, true)
var cflags []string var cflags []string
if cxx { if cxx {
cflags = stringList(cgoCPPFLAGS, cgoCXXFLAGS) cflags = stringList(cgoCPPFLAGS, pcCFLAGS, cgoCXXFLAGS)
} else { } else {
cflags = stringList(cgoCPPFLAGS, cgoCFLAGS) cflags = stringList(cgoCPPFLAGS, pcCFLAGS, cgoCFLAGS)
} }
n := 5 // length of ".swig" n := 5 // length of ".swig"
@ -2503,6 +2576,13 @@ func (b *builder) swigOne(p *Package, file, obj string, cxx bool, intgosize stri
"-o", obj + gccBase + gccExt, "-o", obj + gccBase + gccExt,
"-outdir", obj, "-outdir", obj,
} }
for _, f := range cflags {
if len(f) > 3 && f[:2] == "-I" {
args = append(args, f)
}
}
if gccgo { if gccgo {
args = append(args, "-gccgo") args = append(args, "-gccgo")
if pkgpath := gccgoPkgpath(p); pkgpath != "" { if pkgpath := gccgoPkgpath(p); pkgpath != "" {
@ -2575,8 +2655,8 @@ func raceInit() {
if !buildRace { if !buildRace {
return return
} }
if goarch != "amd64" || goos != "linux" && goos != "darwin" && goos != "windows" { if goarch != "amd64" || goos != "linux" && goos != "freebsd" && goos != "darwin" && goos != "windows" {
fmt.Fprintf(os.Stderr, "go %s: -race is only supported on linux/amd64, darwin/amd64 and windows/amd64\n", flag.Args()[0]) fmt.Fprintf(os.Stderr, "go %s: -race is only supported on linux/amd64, freebsd/amd64, darwin/amd64 and windows/amd64\n", flag.Args()[0])
os.Exit(2) os.Exit(2)
} }
buildGcflags = append(buildGcflags, "-race") buildGcflags = append(buildGcflags, "-race")

View File

@ -19,6 +19,7 @@ The commands are:
env print Go environment information env print Go environment information
fix run go tool fix on packages fix run go tool fix on packages
fmt run gofmt on package sources fmt run gofmt on package sources
generate generate Go files by processing source
get download and install packages and dependencies get download and install packages and dependencies
install compile and install packages and dependencies install compile and install packages and dependencies
list list packages list list packages
@ -75,6 +76,7 @@ and test commands:
-a -a
force rebuilding of packages that are already up-to-date. force rebuilding of packages that are already up-to-date.
In Go releases, does not apply to the standard library.
-n -n
print the commands but do not run them. print the commands but do not run them.
-p n -p n
@ -82,7 +84,7 @@ and test commands:
The default is the number of CPUs available. The default is the number of CPUs available.
-race -race
enable data race detection. enable data race detection.
Supported only on linux/amd64, darwin/amd64 and windows/amd64. Supported only on linux/amd64, freebsd/amd64, darwin/amd64 and windows/amd64.
-v -v
print the names of packages as they are compiled. print the names of packages as they are compiled.
-work -work
@ -219,11 +221,110 @@ To run gofmt with specific options, run gofmt itself.
See also: go fix, go vet. See also: go fix, go vet.
Generate Go files by processing source
Usage:
go generate [-run regexp] [file.go... | packages]
Generate runs commands described by directives within existing
files. Those commands can run any process but the intent is to
create or update Go source files, for instance by running yacc.
Go generate is never run automatically by go build, go get, go test,
and so on. It must be run explicitly.
Go generate scans the file for directives, which are lines of
the form,
//go:generate command argument...
(note: no leading spaces and no space in "//go") where command
is the generator to be run, corresponding to an executable file
that can be run locally. It must either be in the shell path
(gofmt), a fully qualified path (/usr/you/bin/mytool), or a
command alias, described below.
Note that go generate does not parse the file, so lines that look
like directives in comments or multiline strings will be treated
as directives.
The arguments to the directive are space-separated tokens or
double-quoted strings passed to the generator as individual
arguments when it is run.
Quoted strings use Go syntax and are evaluated before execution; a
quoted string appears as a single argument to the generator.
Go generate sets several variables when it runs the generator:
$GOARCH
The execution architecture (arm, amd64, etc.)
$GOOS
The execution operating system (linux, windows, etc.)
$GOFILE
The base name of the file.
$GOPACKAGE
The name of the package of the file containing the directive.
Other than variable substitution and quoted-string evaluation, no
special processing such as "globbing" is performed on the command
line.
As a last step before running the command, any invocations of any
environment variables with alphanumeric names, such as $GOFILE or
$HOME, are expanded throughout the command line. The syntax for
variable expansion is $NAME on all operating systems. Due to the
order of evaluation, variables are expanded even inside quoted
strings. If the variable NAME is not set, $NAME expands to the
empty string.
A directive of the form,
//go:generate -command xxx args...
specifies, for the remainder of this source file only, that the
string xxx represents the command identified by the arguments. This
can be used to create aliases or to handle multiword generators.
For example,
//go:generate -command yacc go tool yacc
specifies that the command "yacc" represents the generator
"go tool yacc".
Generate processes packages in the order given on the command line,
one at a time. If the command line lists .go files, they are treated
as a single package. Within a package, generate processes the
source files in a package in file name order, one at a time. Within
a source file, generate runs generators in the order they appear
in the file, one at a time.
If any generator returns an error exit status, "go generate" skips
all further processing for that package.
The generator is run in the package's source directory.
Go generate accepts one specific flag:
-run=""
if non-empty, specifies a regular expression to
select directives whose command matches the expression.
It also accepts the standard build flags -v, -n, and -x.
The -v flag prints the names of packages and files as they are
processed.
The -n flag prints commands that would be executed.
The -x flag prints commands as they are executed.
For more about specifying packages, see 'go help packages'.
Download and install packages and dependencies Download and install packages and dependencies
Usage: Usage:
go get [-d] [-fix] [-t] [-u] [build flags] [packages] go get [-d] [-f] [-fix] [-t] [-u] [build flags] [packages]
Get downloads and installs the packages named by the import paths, Get downloads and installs the packages named by the import paths,
along with their dependencies. along with their dependencies.
@ -231,6 +332,11 @@ along with their dependencies.
The -d flag instructs get to stop after downloading the packages; that is, The -d flag instructs get to stop after downloading the packages; that is,
it instructs get not to install the packages. it instructs get not to install the packages.
The -f flag, valid only when -u is set, forces get -u not to verify that
each package has been checked out from the source control repository
implied by its import path. This can be useful if the source is a local fork
of the original.
The -fix flag instructs get to run the fix tool on the downloaded packages The -fix flag instructs get to run the fix tool on the downloaded packages
before resolving dependencies or building the code. before resolving dependencies or building the code.
@ -291,28 +397,29 @@ syntax of package template. The default output is equivalent to -f
'{{.ImportPath}}'. The struct being passed to the template is: '{{.ImportPath}}'. The struct being passed to the template is:
type Package struct { type Package struct {
Dir string // directory containing package sources Dir string // directory containing package sources
ImportPath string // import path of package in dir ImportPath string // import path of package in dir
Name string // package name ImportComment string // path in import comment on package statement
Doc string // package documentation string Name string // package name
Target string // install path Doc string // package documentation string
Goroot bool // is this package in the Go root? Target string // install path
Standard bool // is this package part of the standard Go library? Goroot bool // is this package in the Go root?
Stale bool // would 'go install' do anything for this package? Standard bool // is this package part of the standard Go library?
Root string // Go root or Go path dir containing this package Stale bool // would 'go install' do anything for this package?
Root string // Go root or Go path dir containing this package
// Source files // Source files
GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles) GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
CgoFiles []string // .go sources files that import "C" CgoFiles []string // .go sources files that import "C"
IgnoredGoFiles []string // .go sources ignored due to build constraints IgnoredGoFiles []string // .go sources ignored due to build constraints
CFiles []string // .c source files CFiles []string // .c source files
CXXFiles []string // .cc, .cxx and .cpp source files CXXFiles []string // .cc, .cxx and .cpp source files
MFiles []string // .m source files MFiles []string // .m source files
HFiles []string // .h, .hh, .hpp and .hxx source files HFiles []string // .h, .hh, .hpp and .hxx source files
SFiles []string // .s source files SFiles []string // .s source files
SwigFiles []string // .swig files SwigFiles []string // .swig files
SwigCXXFiles []string // .swigcxx files SwigCXXFiles []string // .swigcxx files
SysoFiles []string // .syso object files to add to archive SysoFiles []string // .syso object files to add to archive
// Cgo directives // Cgo directives
CgoCFLAGS []string // cgo: flags for C compiler CgoCFLAGS []string // cgo: flags for C compiler
@ -431,16 +538,23 @@ non-test installation.
In addition to the build flags, the flags handled by 'go test' itself are: In addition to the build flags, the flags handled by 'go test' itself are:
-c Compile the test binary to pkg.test but do not run it. -c
(Where pkg is the last element of the package's import path.) Compile the test binary to pkg.test but do not run it
(where pkg is the last element of the package's import path).
The file name can be changed with the -o flag.
-exec xprog
Run the test binary using xprog. The behavior is the same as
in 'go run'. See 'go help run' for details.
-i -i
Install packages that are dependencies of the test. Install packages that are dependencies of the test.
Do not run the test. Do not run the test.
-exec xprog -o file
Run the test binary using xprog. The behavior is the same as Compile the test binary to the named file.
in 'go run'. See 'go help run' for details. The test still runs (unless -c or -i is specified).
The test binary also accepts flags that control execution of the test; these The test binary also accepts flags that control execution of the test; these
flags are also accessible by 'go test'. See 'go help testflag' for details. flags are also accessible by 'go test'. See 'go help testflag' for details.
@ -488,7 +602,7 @@ Usage:
Vet runs the Go vet command on the packages named by the import paths. Vet runs the Go vet command on the packages named by the import paths.
For more about vet, see 'godoc code.google.com/p/go.tools/cmd/vet'. For more about vet, see 'godoc golang.org/x/tools/cmd/vet'.
For more about specifying packages, see 'go help packages'. For more about specifying packages, see 'go help packages'.
To run the vet tool with specific options, run 'go tool vet'. To run the vet tool with specific options, run 'go tool vet'.
@ -681,6 +795,11 @@ A few common code hosting sites have special syntax:
import "launchpad.net/~user/project/branch" import "launchpad.net/~user/project/branch"
import "launchpad.net/~user/project/branch/sub/directory" import "launchpad.net/~user/project/branch/sub/directory"
IBM DevOps Services (Git)
import "hub.jazz.net/git/user/project"
import "hub.jazz.net/git/user/project/sub/directory"
For code hosted on other servers, import paths may either be qualified For code hosted on other servers, import paths may either be qualified
with the version control type, or the go tool can dynamically fetch with the version control type, or the go tool can dynamically fetch
the import path over https/http and discover where the code resides the import path over https/http and discover where the code resides
@ -756,7 +875,26 @@ listed in the GOPATH environment variable (see 'go help gopath').
The go command attempts to download the version of the The go command attempts to download the version of the
package appropriate for the Go release being used. package appropriate for the Go release being used.
Run 'go help install' for more. Run 'go help get' for more.
Import path checking
When the custom import path feature described above redirects to a
known code hosting site, each of the resulting packages has two possible
import paths, using the custom domain or the known hosting site.
A package statement is said to have an "import comment" if it is immediately
followed (before the next newline) by a comment of one of these two forms:
package math // import "path"
package math /* import "path" * /
The go command will refuse to install a package with an import comment
unless it is being referred to by that import path. In this way, import comments
let package authors make sure the custom import path is used and not a
direct path to the underlying code hosting site.
See https://golang.org/s/go14customimport for details.
Description of package lists Description of package lists
@ -812,7 +950,8 @@ single directory, the command is applied to a single synthesized
package made up of exactly those files, ignoring any build constraints package made up of exactly those files, ignoring any build constraints
in those files and ignoring any other files in the directory. in those files and ignoring any other files in the directory.
File names that begin with "." or "_" are ignored by the go tool. Directory and file names that begin with "." or "_" are ignored
by the go tool, as are directories named "testdata".
Description of testing flags Description of testing flags
@ -844,6 +983,7 @@ control the execution of any test:
-blockprofile block.out -blockprofile block.out
Write a goroutine blocking profile to the specified file Write a goroutine blocking profile to the specified file
when all tests are complete. when all tests are complete.
Writes test binary as -c would.
-blockprofilerate n -blockprofilerate n
Control the detail provided in goroutine blocking profiles by Control the detail provided in goroutine blocking profiles by
@ -875,8 +1015,7 @@ control the execution of any test:
Sets -cover. Sets -cover.
-coverprofile cover.out -coverprofile cover.out
Write a coverage profile to the specified file after all tests Write a coverage profile to the file after all tests have passed.
have passed.
Sets -cover. Sets -cover.
-cpu 1,2,4 -cpu 1,2,4
@ -886,10 +1025,11 @@ control the execution of any test:
-cpuprofile cpu.out -cpuprofile cpu.out
Write a CPU profile to the specified file before exiting. Write a CPU profile to the specified file before exiting.
Writes test binary as -c would.
-memprofile mem.out -memprofile mem.out
Write a memory profile to the specified file after all tests Write a memory profile to the file after all tests have passed.
have passed. Writes test binary as -c would.
-memprofilerate n -memprofilerate n
Enable more precise (and expensive) memory profiles by setting Enable more precise (and expensive) memory profiles by setting

View File

@ -0,0 +1,398 @@
// Copyright 2011 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 (
"bufio"
"bytes"
"fmt"
"io"
"log"
"os"
"os/exec"
"path/filepath"
"runtime"
"strconv"
"strings"
"unicode"
"unicode/utf8"
)
var cmdGenerate = &Command{
Run: runGenerate,
UsageLine: "generate [-run regexp] [file.go... | packages]",
Short: "generate Go files by processing source",
Long: `
Generate runs commands described by directives within existing
files. Those commands can run any process but the intent is to
create or update Go source files, for instance by running yacc.
Go generate is never run automatically by go build, go get, go test,
and so on. It must be run explicitly.
Go generate scans the file for directives, which are lines of
the form,
//go:generate command argument...
(note: no leading spaces and no space in "//go") where command
is the generator to be run, corresponding to an executable file
that can be run locally. It must either be in the shell path
(gofmt), a fully qualified path (/usr/you/bin/mytool), or a
command alias, described below.
Note that go generate does not parse the file, so lines that look
like directives in comments or multiline strings will be treated
as directives.
The arguments to the directive are space-separated tokens or
double-quoted strings passed to the generator as individual
arguments when it is run.
Quoted strings use Go syntax and are evaluated before execution; a
quoted string appears as a single argument to the generator.
Go generate sets several variables when it runs the generator:
$GOARCH
The execution architecture (arm, amd64, etc.)
$GOOS
The execution operating system (linux, windows, etc.)
$GOFILE
The base name of the file.
$GOPACKAGE
The name of the package of the file containing the directive.
Other than variable substitution and quoted-string evaluation, no
special processing such as "globbing" is performed on the command
line.
As a last step before running the command, any invocations of any
environment variables with alphanumeric names, such as $GOFILE or
$HOME, are expanded throughout the command line. The syntax for
variable expansion is $NAME on all operating systems. Due to the
order of evaluation, variables are expanded even inside quoted
strings. If the variable NAME is not set, $NAME expands to the
empty string.
A directive of the form,
//go:generate -command xxx args...
specifies, for the remainder of this source file only, that the
string xxx represents the command identified by the arguments. This
can be used to create aliases or to handle multiword generators.
For example,
//go:generate -command yacc go tool yacc
specifies that the command "yacc" represents the generator
"go tool yacc".
Generate processes packages in the order given on the command line,
one at a time. If the command line lists .go files, they are treated
as a single package. Within a package, generate processes the
source files in a package in file name order, one at a time. Within
a source file, generate runs generators in the order they appear
in the file, one at a time.
If any generator returns an error exit status, "go generate" skips
all further processing for that package.
The generator is run in the package's source directory.
Go generate accepts one specific flag:
-run=""
if non-empty, specifies a regular expression to
select directives whose command matches the expression.
It also accepts the standard build flags -v, -n, and -x.
The -v flag prints the names of packages and files as they are
processed.
The -n flag prints commands that would be executed.
The -x flag prints commands as they are executed.
For more about specifying packages, see 'go help packages'.
`,
}
var generateRunFlag string // generate -run flag
func init() {
addBuildFlags(cmdGenerate)
cmdGenerate.Flag.StringVar(&generateRunFlag, "run", "", "")
}
func runGenerate(cmd *Command, args []string) {
// Even if the arguments are .go files, this loop suffices.
for _, pkg := range packages(args) {
for _, file := range pkg.gofiles {
if !generate(pkg.Name, file) {
break
}
}
}
}
// generate runs the generation directives for a single file.
func generate(pkg, absFile string) bool {
fd, err := os.Open(absFile)
if err != nil {
log.Fatalf("generate: %s", err)
}
defer fd.Close()
g := &Generator{
r: fd,
path: absFile,
pkg: pkg,
commands: make(map[string][]string),
}
return g.run()
}
// A Generator represents the state of a single Go source file
// being scanned for generator commands.
type Generator struct {
r io.Reader
path string // full rooted path name.
dir string // full rooted directory of file.
file string // base name of file.
pkg string
commands map[string][]string
lineNum int
}
// run runs the generators in the current file.
func (g *Generator) run() (ok bool) {
// Processing below here calls g.errorf on failure, which does panic(stop).
// If we encounter an error, we abort the package.
defer func() {
e := recover()
if e != nil {
ok = false
if e != stop {
panic(e)
}
setExitStatus(1)
}
}()
g.dir, g.file = filepath.Split(g.path)
g.dir = filepath.Clean(g.dir) // No final separator please.
if buildV {
fmt.Fprintf(os.Stderr, "%s\n", shortPath(g.path))
}
// Scan for lines that start "//go:generate".
// Can't use bufio.Scanner because it can't handle long lines,
// which are likely to appear when using generate.
input := bufio.NewReader(g.r)
var err error
// One line per loop.
for {
g.lineNum++ // 1-indexed.
var buf []byte
buf, err = input.ReadSlice('\n')
if err == bufio.ErrBufferFull {
// Line too long - consume and ignore.
if isGoGenerate(buf) {
g.errorf("directive too long")
}
for err == bufio.ErrBufferFull {
_, err = input.ReadSlice('\n')
}
if err != nil {
break
}
continue
}
if err != nil {
// Check for marker at EOF without final \n.
if err == io.EOF && isGoGenerate(buf) {
err = io.ErrUnexpectedEOF
}
break
}
if !isGoGenerate(buf) {
continue
}
words := g.split(string(buf))
if len(words) == 0 {
g.errorf("no arguments to directive")
}
if words[0] == "-command" {
g.setShorthand(words)
continue
}
// Run the command line.
if buildN || buildX {
fmt.Fprintf(os.Stderr, "%s\n", strings.Join(words, " "))
}
if buildN {
continue
}
g.exec(words)
}
if err != nil && err != io.EOF {
g.errorf("error reading %s: %s", shortPath(g.path), err)
}
return true
}
func isGoGenerate(buf []byte) bool {
return bytes.HasPrefix(buf, []byte("//go:generate ")) || bytes.HasPrefix(buf, []byte("//go:generate\t"))
}
// split breaks the line into words, evaluating quoted
// strings and evaluating environment variables.
// The initial //go:generate element is present in line.
func (g *Generator) split(line string) []string {
// Parse line, obeying quoted strings.
var words []string
line = line[len("//go:generate ") : len(line)-1] // Drop preamble and final newline.
// One (possibly quoted) word per iteration.
Words:
for {
line = strings.TrimLeft(line, " \t")
if len(line) == 0 {
break
}
if line[0] == '"' {
for i := 1; i < len(line); i++ {
c := line[i] // Only looking for ASCII so this is OK.
switch c {
case '\\':
if i+1 == len(line) {
g.errorf("bad backslash")
}
i++ // Absorb next byte (If it's a multibyte we'll get an error in Unquote).
case '"':
word, err := strconv.Unquote(line[0 : i+1])
if err != nil {
g.errorf("bad quoted string")
}
words = append(words, word)
line = line[i+1:]
// Check the next character is space or end of line.
if len(line) > 0 && line[0] != ' ' && line[0] != '\t' {
g.errorf("expect space after quoted argument")
}
continue Words
}
}
g.errorf("mismatched quoted string")
}
i := strings.IndexAny(line, " \t")
if i < 0 {
i = len(line)
}
words = append(words, line[0:i])
line = line[i:]
}
// Substitute command if required.
if len(words) > 0 && g.commands[words[0]] != nil {
// Replace 0th word by command substitution.
words = append(g.commands[words[0]], words[1:]...)
}
// Substitute environment variables.
for i, word := range words {
words[i] = g.expandEnv(word)
}
return words
}
var stop = fmt.Errorf("error in generation")
// errorf logs an error message prefixed with the file and line number.
// It then exits the program (with exit status 1) because generation stops
// at the first error.
func (g *Generator) errorf(format string, args ...interface{}) {
fmt.Fprintf(os.Stderr, "%s:%d: %s\n", shortPath(g.path), g.lineNum,
fmt.Sprintf(format, args...))
panic(stop)
}
// expandEnv expands any $XXX invocations in word.
func (g *Generator) expandEnv(word string) string {
if !strings.ContainsRune(word, '$') {
return word
}
var buf bytes.Buffer
var w int
var r rune
for i := 0; i < len(word); i += w {
r, w = utf8.DecodeRuneInString(word[i:])
if r != '$' {
buf.WriteRune(r)
continue
}
w += g.identLength(word[i+w:])
envVar := word[i+1 : i+w]
var sub string
switch envVar {
case "GOARCH":
sub = runtime.GOARCH
case "GOOS":
sub = runtime.GOOS
case "GOFILE":
sub = g.file
case "GOPACKAGE":
sub = g.pkg
default:
sub = os.Getenv(envVar)
}
buf.WriteString(sub)
}
return buf.String()
}
// identLength returns the length of the identifier beginning the string.
func (g *Generator) identLength(word string) int {
for i, r := range word {
if r == '_' || unicode.IsLetter(r) || unicode.IsDigit(r) {
continue
}
return i
}
return len(word)
}
// setShorthand installs a new shorthand as defined by a -command directive.
func (g *Generator) setShorthand(words []string) {
// Create command shorthand.
if len(words) == 1 {
g.errorf("no command specified for -command")
}
command := words[1]
if g.commands[command] != nil {
g.errorf("command %q defined multiply defined", command)
}
g.commands[command] = words[2:len(words):len(words)] // force later append to make copy
}
// exec runs the command specified by the argument. The first word is
// the command name itself.
func (g *Generator) exec(words []string) {
cmd := exec.Command(words[0], words[1:]...)
// Standard in and out of generator should be the usual.
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
// Run the command in the package directory.
cmd.Dir = g.dir
env := []string{
"GOARCH=" + runtime.GOARCH,
"GOOS=" + runtime.GOOS,
"GOFILE=" + g.file,
"GOPACKAGE=" + g.pkg,
}
cmd.Env = mergeEnvLists(env, os.Environ())
err := cmd.Run()
if err != nil {
g.errorf("running %q: %s", words[0], err)
}
}

View File

@ -0,0 +1,48 @@
// Copyright 2011 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 (
"reflect"
"runtime"
"testing"
)
type splitTest struct {
in string
out []string
}
var splitTests = []splitTest{
{"", nil},
{"x", []string{"x"}},
{" a b\tc ", []string{"a", "b", "c"}},
{` " a " `, []string{" a "}},
{"$GOARCH", []string{runtime.GOARCH}},
{"$GOOS", []string{runtime.GOOS}},
{"$GOFILE", []string{"proc.go"}},
{"$GOPACKAGE", []string{"sys"}},
{"a $XXNOTDEFINEDXX b", []string{"a", "", "b"}},
{"/$XXNOTDEFINED/", []string{"//"}},
{"yacc -o $GOARCH/yacc_$GOFILE", []string{"go", "tool", "yacc", "-o", runtime.GOARCH + "/yacc_proc.go"}},
}
func TestGenerateCommandParse(t *testing.T) {
g := &Generator{
r: nil, // Unused here.
path: "/usr/ken/sys/proc.go",
dir: "/usr/ken/sys",
file: "proc.go",
pkg: "sys",
commands: make(map[string][]string),
}
g.setShorthand([]string{"-command", "yacc", "go", "tool", "yacc"})
for _, test := range splitTests {
got := g.split("//go:generate " + test.in + "\n")
if !reflect.DeepEqual(got, test.out) {
t.Errorf("split(%q): got %q expected %q", test.in, got, test.out)
}
}
}

View File

@ -16,7 +16,7 @@ import (
) )
var cmdGet = &Command{ var cmdGet = &Command{
UsageLine: "get [-d] [-fix] [-t] [-u] [build flags] [packages]", UsageLine: "get [-d] [-f] [-fix] [-t] [-u] [build flags] [packages]",
Short: "download and install packages and dependencies", Short: "download and install packages and dependencies",
Long: ` Long: `
Get downloads and installs the packages named by the import paths, Get downloads and installs the packages named by the import paths,
@ -25,6 +25,11 @@ along with their dependencies.
The -d flag instructs get to stop after downloading the packages; that is, The -d flag instructs get to stop after downloading the packages; that is,
it instructs get not to install the packages. it instructs get not to install the packages.
The -f flag, valid only when -u is set, forces get -u not to verify that
each package has been checked out from the source control repository
implied by its import path. This can be useful if the source is a local fork
of the original.
The -fix flag instructs get to run the fix tool on the downloaded packages The -fix flag instructs get to run the fix tool on the downloaded packages
before resolving dependencies or building the code. before resolving dependencies or building the code.
@ -53,6 +58,7 @@ See also: go build, go install, go clean.
} }
var getD = cmdGet.Flag.Bool("d", false, "") var getD = cmdGet.Flag.Bool("d", false, "")
var getF = cmdGet.Flag.Bool("f", false, "")
var getT = cmdGet.Flag.Bool("t", false, "") var getT = cmdGet.Flag.Bool("t", false, "")
var getU = cmdGet.Flag.Bool("u", false, "") var getU = cmdGet.Flag.Bool("u", false, "")
var getFix = cmdGet.Flag.Bool("fix", false, "") var getFix = cmdGet.Flag.Bool("fix", false, "")
@ -63,6 +69,10 @@ func init() {
} }
func runGet(cmd *Command, args []string) { func runGet(cmd *Command, args []string) {
if *getF && !*getU {
fatalf("go get: cannot use -f flag without -u")
}
// Phase 1. Download/update. // Phase 1. Download/update.
var stk importStack var stk importStack
for _, arg := range downloadPaths(args) { for _, arg := range downloadPaths(args) {
@ -151,7 +161,9 @@ func download(arg string, stk *importStack, getTestDeps bool) {
} }
// Only process each package once. // Only process each package once.
if downloadCache[arg] { // (Unless we're fetching test dependencies for this package,
// in which case we want to process it again.)
if downloadCache[arg] && !getTestDeps {
return return
} }
downloadCache[arg] = true downloadCache[arg] = true
@ -264,6 +276,25 @@ func downloadPackage(p *Package) error {
return err return err
} }
repo = "<local>" // should be unused; make distinctive repo = "<local>" // should be unused; make distinctive
// Double-check where it came from.
if *getU && vcs.remoteRepo != nil && !*getF {
dir := filepath.Join(p.build.SrcRoot, rootPath)
if remote, err := vcs.remoteRepo(vcs, dir); err == nil {
if rr, err := repoRootForImportPath(p.ImportPath); err == nil {
repo := rr.repo
if rr.vcs.resolveRepo != nil {
resolved, err := rr.vcs.resolveRepo(rr.vcs, dir, repo)
if err == nil {
repo = resolved
}
}
if remote != repo {
return fmt.Errorf("%s is from %s, should be from %s", dir, remote, repo)
}
}
}
}
} else { } else {
// Analyze the import path to determine the version control system, // Analyze the import path to determine the version control system,
// repository, and the import path for the root of the repository. // repository, and the import path for the root of the repository.

View File

@ -0,0 +1,55 @@
// Copyright 2014 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 (
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
)
func TestAbsolutePath(t *testing.T) {
tmp, err := ioutil.TempDir("", "TestAbsolutePath")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmp)
file := filepath.Join(tmp, "a.go")
err = ioutil.WriteFile(file, []byte{}, 0644)
if err != nil {
t.Fatal(err)
}
dir := filepath.Join(tmp, "dir")
err = os.Mkdir(dir, 0777)
if err != nil {
t.Fatal(err)
}
wd, err := os.Getwd()
if err != nil {
t.Fatal(err)
}
defer os.Chdir(wd)
// Chdir so current directory and a.go reside on the same drive.
err = os.Chdir(dir)
if err != nil {
t.Fatal(err)
}
noVolume := file[len(filepath.VolumeName(file)):]
wrongPath := filepath.Join(dir, noVolume)
output, err := exec.Command("go", "build", noVolume).CombinedOutput()
if err == nil {
t.Fatal("build should fail")
}
if strings.Contains(string(output), wrongPath) {
t.Fatalf("wrong output found: %v %v", err, string(output))
}
}

View File

@ -81,7 +81,8 @@ single directory, the command is applied to a single synthesized
package made up of exactly those files, ignoring any build constraints package made up of exactly those files, ignoring any build constraints
in those files and ignoring any other files in the directory. in those files and ignoring any other files in the directory.
File names that begin with "." or "_" are ignored by the go tool. Directory and file names that begin with "." or "_" are ignored
by the go tool, as are directories named "testdata".
`, `,
} }
@ -154,6 +155,11 @@ A few common code hosting sites have special syntax:
import "launchpad.net/~user/project/branch" import "launchpad.net/~user/project/branch"
import "launchpad.net/~user/project/branch/sub/directory" import "launchpad.net/~user/project/branch/sub/directory"
IBM DevOps Services (Git)
import "hub.jazz.net/git/user/project"
import "hub.jazz.net/git/user/project/sub/directory"
For code hosted on other servers, import paths may either be qualified For code hosted on other servers, import paths may either be qualified
with the version control type, or the go tool can dynamically fetch with the version control type, or the go tool can dynamically fetch
the import path over https/http and discover where the code resides the import path over https/http and discover where the code resides
@ -229,7 +235,26 @@ listed in the GOPATH environment variable (see 'go help gopath').
The go command attempts to download the version of the The go command attempts to download the version of the
package appropriate for the Go release being used. package appropriate for the Go release being used.
Run 'go help install' for more. Run 'go help get' for more.
Import path checking
When the custom import path feature described above redirects to a
known code hosting site, each of the resulting packages has two possible
import paths, using the custom domain or the known hosting site.
A package statement is said to have an "import comment" if it is immediately
followed (before the next newline) by a comment of one of these two forms:
package math // import "path"
package math /* import "path" */
The go command will refuse to install a package with an import comment
unless it is being referred to by that import path. In this way, import comments
let package authors make sure the custom import path is used and not a
direct path to the underlying code hosting site.
See https://golang.org/s/go14customimport for details.
`, `,
} }

View File

@ -30,28 +30,29 @@ syntax of package template. The default output is equivalent to -f
'{{.ImportPath}}'. The struct being passed to the template is: '{{.ImportPath}}'. The struct being passed to the template is:
type Package struct { type Package struct {
Dir string // directory containing package sources Dir string // directory containing package sources
ImportPath string // import path of package in dir ImportPath string // import path of package in dir
Name string // package name ImportComment string // path in import comment on package statement
Doc string // package documentation string Name string // package name
Target string // install path Doc string // package documentation string
Goroot bool // is this package in the Go root? Target string // install path
Standard bool // is this package part of the standard Go library? Goroot bool // is this package in the Go root?
Stale bool // would 'go install' do anything for this package? Standard bool // is this package part of the standard Go library?
Root string // Go root or Go path dir containing this package Stale bool // would 'go install' do anything for this package?
Root string // Go root or Go path dir containing this package
// Source files // Source files
GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles) GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
CgoFiles []string // .go sources files that import "C" CgoFiles []string // .go sources files that import "C"
IgnoredGoFiles []string // .go sources ignored due to build constraints IgnoredGoFiles []string // .go sources ignored due to build constraints
CFiles []string // .c source files CFiles []string // .c source files
CXXFiles []string // .cc, .cxx and .cpp source files CXXFiles []string // .cc, .cxx and .cpp source files
MFiles []string // .m source files MFiles []string // .m source files
HFiles []string // .h, .hh, .hpp and .hxx source files HFiles []string // .h, .hh, .hpp and .hxx source files
SFiles []string // .s source files SFiles []string // .s source files
SwigFiles []string // .swig files SwigFiles []string // .swig files
SwigCXXFiles []string // .swigcxx files SwigCXXFiles []string // .swigcxx files
SysoFiles []string // .syso object files to add to archive SysoFiles []string // .syso object files to add to archive
// Cgo directives // Cgo directives
CgoCFLAGS []string // cgo: flags for C compiler CgoCFLAGS []string // cgo: flags for C compiler

View File

@ -79,6 +79,7 @@ var commands = []*Command{
cmdEnv, cmdEnv,
cmdFix, cmdFix,
cmdFmt, cmdFmt,
cmdGenerate,
cmdGet, cmdGet,
cmdInstall, cmdInstall,
cmdList, cmdList,
@ -536,7 +537,7 @@ func matchPackages(pattern string) []string {
}) })
for _, src := range buildContext.SrcDirs() { for _, src := range buildContext.SrcDirs() {
if pattern == "std" && src != gorootSrcPkg { if pattern == "std" && src != gorootSrc {
continue continue
} }
src = filepath.Clean(src) + string(filepath.Separator) src = filepath.Clean(src) + string(filepath.Separator)
@ -618,7 +619,7 @@ func matchPackagesInFS(pattern string) []string {
// The initial case is not Cleaned, though, so we do this explicitly. // The initial case is not Cleaned, though, so we do this explicitly.
// //
// This converts a path like "./io/" to "io". Without this step, running // This converts a path like "./io/" to "io". Without this step, running
// "cd $GOROOT/src/pkg; go list ./io/..." would incorrectly skip the io // "cd $GOROOT/src; go list ./io/..." would incorrectly skip the io
// package, because prepending the prefix "./" to the unclean path would // package, because prepending the prefix "./" to the unclean path would
// result in "././io", and match("././io") returns false. // result in "././io", and match("././io") returns false.
path = filepath.Clean(path) path = filepath.Clean(path)

View File

@ -14,6 +14,7 @@ import (
"os" "os"
pathpkg "path" pathpkg "path"
"path/filepath" "path/filepath"
"runtime"
"sort" "sort"
"strings" "strings"
"time" "time"
@ -25,16 +26,17 @@ type Package struct {
// Note: These fields are part of the go command's public API. // Note: These fields are part of the go command's public API.
// See list.go. It is okay to add fields, but not to change or // See list.go. It is okay to add fields, but not to change or
// remove existing ones. Keep in sync with list.go // remove existing ones. Keep in sync with list.go
Dir string `json:",omitempty"` // directory containing package sources Dir string `json:",omitempty"` // directory containing package sources
ImportPath string `json:",omitempty"` // import path of package in dir ImportPath string `json:",omitempty"` // import path of package in dir
Name string `json:",omitempty"` // package name ImportComment string `json:",omitempty"` // path in import comment on package statement
Doc string `json:",omitempty"` // package documentation string Name string `json:",omitempty"` // package name
Target string `json:",omitempty"` // install path Doc string `json:",omitempty"` // package documentation string
Goroot bool `json:",omitempty"` // is this package found in the Go root? Target string `json:",omitempty"` // install path
Standard bool `json:",omitempty"` // is this package part of the standard Go library? Goroot bool `json:",omitempty"` // is this package found in the Go root?
Stale bool `json:",omitempty"` // would 'go install' do anything for this package? Standard bool `json:",omitempty"` // is this package part of the standard Go library?
Root string `json:",omitempty"` // Go root or Go path dir containing this package Stale bool `json:",omitempty"` // would 'go install' do anything for this package?
ConflictDir string `json:",omitempty"` // Dir is hidden by this other directory Root string `json:",omitempty"` // Go root or Go path dir containing this package
ConflictDir string `json:",omitempty"` // Dir is hidden by this other directory
// Source files // Source files
GoFiles []string `json:",omitempty"` // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles) GoFiles []string `json:",omitempty"` // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
@ -103,6 +105,7 @@ func (p *Package) copyBuild(pp *build.Package) {
p.Dir = pp.Dir p.Dir = pp.Dir
p.ImportPath = pp.ImportPath p.ImportPath = pp.ImportPath
p.ImportComment = pp.ImportComment
p.Name = pp.Name p.Name = pp.Name
p.Doc = pp.Doc p.Doc = pp.Doc
p.Root = pp.Root p.Root = pp.Root
@ -218,7 +221,7 @@ func dirToImportPath(dir string) string {
} }
func makeImportValid(r rune) rune { func makeImportValid(r rune) rune {
// Should match Go spec, compilers, and ../../pkg/go/parser/parser.go:/isValidImport. // Should match Go spec, compilers, and ../../go/parser/parser.go:/isValidImport.
const illegalChars = `!"#$%&'()*,:;<=>?[\]^{|}` + "`\uFFFD" const illegalChars = `!"#$%&'()*,:;<=>?[\]^{|}` + "`\uFFFD"
if !unicode.IsGraphic(r) || unicode.IsSpace(r) || strings.ContainsRune(illegalChars, r) { if !unicode.IsGraphic(r) || unicode.IsSpace(r) || strings.ContainsRune(illegalChars, r) {
return '_' return '_'
@ -244,6 +247,9 @@ func loadImport(path string, srcDir string, stk *importStack, importPos []token.
importPath = dirToImportPath(filepath.Join(srcDir, path)) importPath = dirToImportPath(filepath.Join(srcDir, path))
} }
if p := packageCache[importPath]; p != nil { if p := packageCache[importPath]; p != nil {
if perr := disallowInternal(srcDir, p, stk); perr != p {
return perr
}
return reusePackage(p, stk) return reusePackage(p, stk)
} }
@ -258,11 +264,14 @@ func loadImport(path string, srcDir string, stk *importStack, importPos []token.
// //
// TODO: After Go 1, decide when to pass build.AllowBinary here. // TODO: After Go 1, decide when to pass build.AllowBinary here.
// See issue 3268 for mistakes to avoid. // See issue 3268 for mistakes to avoid.
bp, err := buildContext.Import(path, srcDir, 0) bp, err := buildContext.Import(path, srcDir, build.ImportComment)
bp.ImportPath = importPath bp.ImportPath = importPath
if gobin != "" { if gobin != "" {
bp.BinDir = gobin bp.BinDir = gobin
} }
if err == nil && !isLocal && bp.ImportComment != "" && bp.ImportComment != path {
err = fmt.Errorf("code in directory %s expects import %q", bp.Dir, bp.ImportComment)
}
p.load(stk, bp, err) p.load(stk, bp, err)
if p.Error != nil && len(importPos) > 0 { if p.Error != nil && len(importPos) > 0 {
pos := importPos[0] pos := importPos[0]
@ -270,6 +279,10 @@ func loadImport(path string, srcDir string, stk *importStack, importPos []token.
p.Error.Pos = pos.String() p.Error.Pos = pos.String()
} }
if perr := disallowInternal(srcDir, p, stk); perr != p {
return perr
}
return p return p
} }
@ -298,12 +311,82 @@ func reusePackage(p *Package, stk *importStack) *Package {
return p return p
} }
// disallowInternal checks that srcDir is allowed to import p.
// If the import is allowed, disallowInternal returns the original package p.
// If not, it returns a new package containing just an appropriate error.
func disallowInternal(srcDir string, p *Package, stk *importStack) *Package {
// golang.org/s/go14internal:
// An import of a path containing the element “internal”
// is disallowed if the importing code is outside the tree
// rooted at the parent of the “internal” directory.
//
// ... For Go 1.4, we will implement the rule first for $GOROOT, but not $GOPATH.
// Only applies to $GOROOT.
if !p.Standard {
return p
}
// The stack includes p.ImportPath.
// If that's the only thing on the stack, we started
// with a name given on the command line, not an
// import. Anything listed on the command line is fine.
if len(*stk) == 1 {
return p
}
// Check for "internal" element: four cases depending on begin of string and/or end of string.
i, ok := findInternal(p.ImportPath)
if !ok {
return p
}
// Internal is present.
// Map import path back to directory corresponding to parent of internal.
if i > 0 {
i-- // rewind over slash in ".../internal"
}
parent := p.Dir[:i+len(p.Dir)-len(p.ImportPath)]
if hasPathPrefix(filepath.ToSlash(srcDir), filepath.ToSlash(parent)) {
return p
}
// Internal is present, and srcDir is outside parent's tree. Not allowed.
perr := *p
perr.Error = &PackageError{
ImportStack: stk.copy(),
Err: "use of internal package not allowed",
}
perr.Incomplete = true
return &perr
}
// findInternal looks for the final "internal" path element in the given import path.
// If there isn't one, findInternal returns ok=false.
// Otherwise, findInternal returns ok=true and the index of the "internal".
func findInternal(path string) (index int, ok bool) {
// Four cases, depending on internal at start/end of string or not.
// The order matters: we must return the index of the final element,
// because the final one produces the most restrictive requirement
// on the importer.
switch {
case strings.HasSuffix(path, "/internal"):
return len(path) - len("internal"), true
case strings.Contains(path, "/internal/"):
return strings.LastIndex(path, "/internal/") + 1, true
case path == "internal", strings.HasPrefix(path, "internal/"):
return 0, true
}
return 0, false
}
type targetDir int type targetDir int
const ( const (
toRoot targetDir = iota // to bin dir inside package root (default) toRoot targetDir = iota // to bin dir inside package root (default)
toTool // GOROOT/pkg/tool toTool // GOROOT/pkg/tool
toBin // GOROOT/bin toBin // GOROOT/bin
stalePath // the old import path; fail to build
) )
// goTools is a map of Go program import path to install target directory. // goTools is a map of Go program import path to install target directory.
@ -316,10 +399,14 @@ var goTools = map[string]targetDir{
"cmd/nm": toTool, "cmd/nm": toTool,
"cmd/objdump": toTool, "cmd/objdump": toTool,
"cmd/pack": toTool, "cmd/pack": toTool,
"cmd/pprof": toTool,
"cmd/yacc": toTool, "cmd/yacc": toTool,
"code.google.com/p/go.tools/cmd/cover": toTool, "golang.org/x/tools/cmd/cover": toTool,
"code.google.com/p/go.tools/cmd/godoc": toBin, "golang.org/x/tools/cmd/godoc": toBin,
"code.google.com/p/go.tools/cmd/vet": toTool, "golang.org/x/tools/cmd/vet": toTool,
"code.google.com/p/go.tools/cmd/cover": stalePath,
"code.google.com/p/go.tools/cmd/godoc": stalePath,
"code.google.com/p/go.tools/cmd/vet": stalePath,
} }
// expandScanner expands a scanner.List error into all the errors in the list. // expandScanner expands a scanner.List error into all the errors in the list.
@ -380,6 +467,13 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package
} }
if p.Name == "main" { if p.Name == "main" {
// Report an error when the old code.google.com/p/go.tools paths are used.
if goTools[p.ImportPath] == stalePath {
newPath := strings.Replace(p.ImportPath, "code.google.com/p/go.", "golang.org/x/", 1)
e := fmt.Sprintf("the %v command has moved; use %v instead.", p.ImportPath, newPath)
p.Error = &PackageError{Err: e}
return p
}
_, elem := filepath.Split(p.Dir) _, elem := filepath.Split(p.Dir)
full := buildContext.GOOS + "_" + buildContext.GOARCH + "/" + elem full := buildContext.GOOS + "_" + buildContext.GOARCH + "/" + elem
if buildContext.GOOS != toolGOOS || buildContext.GOARCH != toolGOARCH { if buildContext.GOOS != toolGOOS || buildContext.GOARCH != toolGOARCH {
@ -482,7 +576,7 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package
// Build list of imported packages and full dependency list. // Build list of imported packages and full dependency list.
imports := make([]*Package, 0, len(p.Imports)) imports := make([]*Package, 0, len(p.Imports))
deps := make(map[string]bool) deps := make(map[string]*Package)
for i, path := range importPaths { for i, path := range importPaths {
if path == "C" { if path == "C" {
continue continue
@ -505,10 +599,10 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package
path = p1.ImportPath path = p1.ImportPath
importPaths[i] = path importPaths[i] = path
} }
deps[path] = true deps[path] = p1
imports = append(imports, p1) imports = append(imports, p1)
for _, dep := range p1.Deps { for _, dep := range p1.deps {
deps[dep] = true deps[dep.ImportPath] = dep
} }
if p1.Incomplete { if p1.Incomplete {
p.Incomplete = true p.Incomplete = true
@ -522,7 +616,7 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package
} }
sort.Strings(p.Deps) sort.Strings(p.Deps)
for _, dep := range p.Deps { for _, dep := range p.Deps {
p1 := packageCache[dep] p1 := deps[dep]
if p1 == nil { if p1 == nil {
panic("impossible: missing entry in package cache for " + dep + " imported by " + p.ImportPath) panic("impossible: missing entry in package cache for " + dep + " imported by " + p.ImportPath)
} }
@ -538,6 +632,16 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package
} }
p.Target = p.target p.Target = p.target
// Check for C code compiled with Plan 9 C compiler.
// No longer allowed except in runtime and runtime/cgo, for now.
if len(p.CFiles) > 0 && !p.usesCgo() && (!p.Standard || p.ImportPath != "runtime") {
p.Error = &PackageError{
ImportStack: stk.copy(),
Err: fmt.Sprintf("C source files not allowed when not using cgo: %s", strings.Join(p.CFiles, " ")),
}
return p
}
// In the absence of errors lower in the dependency tree, // In the absence of errors lower in the dependency tree,
// check for case-insensitive collisions of import paths. // check for case-insensitive collisions of import paths.
if len(p.DepsErrors) == 0 { if len(p.DepsErrors) == 0 {
@ -599,6 +703,12 @@ func computeStale(pkgs ...*Package) {
} }
} }
// The runtime version string takes one of two forms:
// "go1.X[.Y]" for Go releases, and "devel +hash" at tip.
// Determine whether we are in a released copy by
// inspecting the version.
var isGoRelease = strings.HasPrefix(runtime.Version(), "go1")
// isStale reports whether package p needs to be rebuilt. // isStale reports whether package p needs to be rebuilt.
func isStale(p *Package, topRoot map[string]bool) bool { func isStale(p *Package, topRoot map[string]bool) bool {
if p.Standard && (p.ImportPath == "unsafe" || buildContext.Compiler == "gccgo") { if p.Standard && (p.ImportPath == "unsafe" || buildContext.Compiler == "gccgo") {
@ -619,7 +729,16 @@ func isStale(p *Package, topRoot map[string]bool) bool {
return false return false
} }
if buildA || p.target == "" || p.Stale { // If we are running a release copy of Go, do not rebuild the standard packages.
// They may not be writable anyway, but they are certainly not changing.
// This makes 'go build -a' skip the standard packages when using an official release.
// See issue 4106 and issue 8290.
pkgBuildA := buildA
if p.Standard && isGoRelease {
pkgBuildA = false
}
if pkgBuildA || p.target == "" || p.Stale {
return true return true
} }
@ -707,24 +826,13 @@ func loadPackage(arg string, stk *importStack) *Package {
arg = sub arg = sub
} }
} }
if strings.HasPrefix(arg, "cmd/") { if strings.HasPrefix(arg, "cmd/") && !strings.Contains(arg[4:], "/") {
if p := cmdCache[arg]; p != nil { if p := cmdCache[arg]; p != nil {
return p return p
} }
stk.push(arg) stk.push(arg)
defer stk.pop() defer stk.pop()
if strings.Contains(arg[4:], "/") {
p := &Package{
Error: &PackageError{
ImportStack: stk.copy(),
Err: fmt.Sprintf("invalid import path: cmd/... is reserved for Go commands"),
hard: true,
},
}
return p
}
bp, err := buildContext.ImportDir(filepath.Join(gorootSrc, arg), 0) bp, err := buildContext.ImportDir(filepath.Join(gorootSrc, arg), 0)
bp.ImportPath = arg bp.ImportPath = arg
bp.Goroot = true bp.Goroot = true

View File

@ -6,6 +6,7 @@ package main
import ( import (
"bytes" "bytes"
"errors"
"fmt" "fmt"
"go/ast" "go/ast"
"go/build" "go/build"
@ -65,16 +66,23 @@ non-test installation.
In addition to the build flags, the flags handled by 'go test' itself are: In addition to the build flags, the flags handled by 'go test' itself are:
-c Compile the test binary to pkg.test but do not run it. -c
(Where pkg is the last element of the package's import path.) Compile the test binary to pkg.test but do not run it
(where pkg is the last element of the package's import path).
The file name can be changed with the -o flag.
-exec xprog
Run the test binary using xprog. The behavior is the same as
in 'go run'. See 'go help run' for details.
-i -i
Install packages that are dependencies of the test. Install packages that are dependencies of the test.
Do not run the test. Do not run the test.
-exec xprog -o file
Run the test binary using xprog. The behavior is the same as Compile the test binary to the named file.
in 'go run'. See 'go help run' for details. The test still runs (unless -c or -i is specified).
The test binary also accepts flags that control execution of the test; these The test binary also accepts flags that control execution of the test; these
flags are also accessible by 'go test'. See 'go help testflag' for details. flags are also accessible by 'go test'. See 'go help testflag' for details.
@ -122,6 +130,7 @@ control the execution of any test:
-blockprofile block.out -blockprofile block.out
Write a goroutine blocking profile to the specified file Write a goroutine blocking profile to the specified file
when all tests are complete. when all tests are complete.
Writes test binary as -c would.
-blockprofilerate n -blockprofilerate n
Control the detail provided in goroutine blocking profiles by Control the detail provided in goroutine blocking profiles by
@ -153,8 +162,7 @@ control the execution of any test:
Sets -cover. Sets -cover.
-coverprofile cover.out -coverprofile cover.out
Write a coverage profile to the specified file after all tests Write a coverage profile to the file after all tests have passed.
have passed.
Sets -cover. Sets -cover.
-cpu 1,2,4 -cpu 1,2,4
@ -164,10 +172,11 @@ control the execution of any test:
-cpuprofile cpu.out -cpuprofile cpu.out
Write a CPU profile to the specified file before exiting. Write a CPU profile to the specified file before exiting.
Writes test binary as -c would.
-memprofile mem.out -memprofile mem.out
Write a memory profile to the specified file after all tests Write a memory profile to the file after all tests have passed.
have passed. Writes test binary as -c would.
-memprofilerate n -memprofilerate n
Enable more precise (and expensive) memory profiles by setting Enable more precise (and expensive) memory profiles by setting
@ -274,10 +283,10 @@ var (
testCoverMode string // -covermode flag testCoverMode string // -covermode flag
testCoverPaths []string // -coverpkg flag testCoverPaths []string // -coverpkg flag
testCoverPkgs []*Package // -coverpkg flag testCoverPkgs []*Package // -coverpkg flag
testO string // -o flag
testProfile bool // some profiling flag testProfile bool // some profiling flag
testNeedBinary bool // profile needs to keep binary around testNeedBinary bool // profile needs to keep binary around
testV bool // -v flag testV bool // -v flag
testFiles []string // -file flag(s) TODO: not respected
testTimeout string // -timeout flag testTimeout string // -timeout flag
testArgs []string testArgs []string
testBench bool testBench bool
@ -291,6 +300,7 @@ var testMainDeps = map[string]bool{
// Dependencies for testmain. // Dependencies for testmain.
"testing": true, "testing": true,
"regexp": true, "regexp": true,
"os": true,
} }
func runTest(cmd *Command, args []string) { func runTest(cmd *Command, args []string) {
@ -308,6 +318,9 @@ func runTest(cmd *Command, args []string) {
if testC && len(pkgs) != 1 { if testC && len(pkgs) != 1 {
fatalf("cannot use -c flag with multiple packages") fatalf("cannot use -c flag with multiple packages")
} }
if testO != "" && len(pkgs) != 1 {
fatalf("cannot use -o flag with multiple packages")
}
if testProfile && len(pkgs) != 1 { if testProfile && len(pkgs) != 1 {
fatalf("cannot use test profile flag with multiple packages") fatalf("cannot use test profile flag with multiple packages")
} }
@ -524,6 +537,13 @@ func contains(x []string, s string) bool {
return false return false
} }
var windowsBadWords = []string{
"install",
"patch",
"setup",
"update",
}
func (b *builder) test(p *Package) (buildAction, runAction, printAction *action, err error) { func (b *builder) test(p *Package) (buildAction, runAction, printAction *action, err error) {
if len(p.TestGoFiles)+len(p.XTestGoFiles) == 0 { if len(p.TestGoFiles)+len(p.XTestGoFiles) == 0 {
build := b.action(modeBuild, modeBuild, p) build := b.action(modeBuild, modeBuild, p)
@ -695,7 +715,7 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,
omitDWARF: !testC && !testNeedBinary, omitDWARF: !testC && !testNeedBinary,
} }
// The generated main also imports testing and regexp. // The generated main also imports testing, regexp, and os.
stk.push("testmain") stk.push("testmain")
for dep := range testMainDeps { for dep := range testMainDeps {
if dep == ptest.ImportPath { if dep == ptest.ImportPath {
@ -734,11 +754,13 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, nil, err
} }
if t.NeedTest || ptest.coverMode != "" { if len(ptest.GoFiles) > 0 {
pmain.imports = append(pmain.imports, ptest) pmain.imports = append(pmain.imports, ptest)
t.ImportTest = true
} }
if t.NeedXtest { if pxtest != nil {
pmain.imports = append(pmain.imports, pxtest) pmain.imports = append(pmain.imports, pxtest)
t.ImportXtest = true
} }
if ptest != p && localCover { if ptest != p && localCover {
@ -790,17 +812,54 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,
a.objdir = testDir + string(filepath.Separator) a.objdir = testDir + string(filepath.Separator)
a.objpkg = filepath.Join(testDir, "main.a") a.objpkg = filepath.Join(testDir, "main.a")
a.target = filepath.Join(testDir, testBinary) + exeSuffix a.target = filepath.Join(testDir, testBinary) + exeSuffix
pmainAction := a if goos == "windows" {
// There are many reserved words on Windows that,
// if used in the name of an executable, cause Windows
// to try to ask for extra permissions.
// The word list includes setup, install, update, and patch,
// but it does not appear to be defined anywhere.
// We have run into this trying to run the
// go.codereview/patch tests.
// For package names containing those words, use test.test.exe
// instead of pkgname.test.exe.
// Note that this file name is only used in the Go command's
// temporary directory. If the -c or other flags are
// given, the code below will still use pkgname.test.exe.
// There are two user-visible effects of this change.
// First, you can actually run 'go test' in directories that
// have names that Windows thinks are installer-like,
// without getting a dialog box asking for more permissions.
// Second, in the Windows process listing during go test,
// the test shows up as test.test.exe, not pkgname.test.exe.
// That second one is a drawback, but it seems a small
// price to pay for the test running at all.
// If maintaining the list of bad words is too onerous,
// we could just do this always on Windows.
for _, bad := range windowsBadWords {
if strings.Contains(testBinary, bad) {
a.target = filepath.Join(testDir, "test.test") + exeSuffix
break
}
}
}
buildAction = a
if testC || testNeedBinary { if testC || testNeedBinary {
// -c or profiling flag: create action to copy binary to ./test.out. // -c or profiling flag: create action to copy binary to ./test.out.
runAction = &action{ target := filepath.Join(cwd, testBinary+exeSuffix)
f: (*builder).install, if testO != "" {
deps: []*action{pmainAction}, target = testO
p: pmain, if !filepath.IsAbs(target) {
target: filepath.Join(cwd, testBinary+exeSuffix), target = filepath.Join(cwd, target)
}
} }
pmainAction = runAction // in case we are running the test buildAction = &action{
f: (*builder).install,
deps: []*action{buildAction},
p: pmain,
target: target,
}
runAction = buildAction // make sure runAction != nil even if not running test
} }
if testC { if testC {
printAction = &action{p: p, deps: []*action{runAction}} // nop printAction = &action{p: p, deps: []*action{runAction}} // nop
@ -808,7 +867,7 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,
// run test // run test
runAction = &action{ runAction = &action{
f: (*builder).runTest, f: (*builder).runTest,
deps: []*action{pmainAction}, deps: []*action{buildAction},
p: p, p: p,
ignoreFail: true, ignoreFail: true,
} }
@ -824,7 +883,7 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,
} }
} }
return pmainAction, runAction, printAction, nil return buildAction, runAction, printAction, nil
} }
func testImportStack(top string, p *Package, target string) []string { func testImportStack(top string, p *Package, target string) []string {
@ -1068,6 +1127,31 @@ func (b *builder) notest(a *action) error {
return nil return nil
} }
// isTestMain tells whether fn is a TestMain(m *testing.M) function.
func isTestMain(fn *ast.FuncDecl) bool {
if fn.Name.String() != "TestMain" ||
fn.Type.Results != nil && len(fn.Type.Results.List) > 0 ||
fn.Type.Params == nil ||
len(fn.Type.Params.List) != 1 ||
len(fn.Type.Params.List[0].Names) > 1 {
return false
}
ptr, ok := fn.Type.Params.List[0].Type.(*ast.StarExpr)
if !ok {
return false
}
// We can't easily check that the type is *testing.M
// because we don't know how testing has been imported,
// but at least check that it's *M or *something.M.
if name, ok := ptr.X.(*ast.Ident); ok && name.Name == "M" {
return true
}
if sel, ok := ptr.X.(*ast.SelectorExpr); ok && sel.Sel.Name == "M" {
return true
}
return false
}
// isTest tells whether name looks like a test (or benchmark, according to prefix). // isTest tells whether name looks like a test (or benchmark, according to prefix).
// It is a Test (say) if there is a character after Test that is not a lower-case letter. // It is a Test (say) if there is a character after Test that is not a lower-case letter.
// We don't want TesticularCancer. // We don't want TesticularCancer.
@ -1093,12 +1177,12 @@ func loadTestFuncs(ptest *Package) (*testFuncs, error) {
Package: ptest, Package: ptest,
} }
for _, file := range ptest.TestGoFiles { for _, file := range ptest.TestGoFiles {
if err := t.load(filepath.Join(ptest.Dir, file), "_test", &t.NeedTest); err != nil { if err := t.load(filepath.Join(ptest.Dir, file), "_test", &t.ImportTest, &t.NeedTest); err != nil {
return nil, err return nil, err
} }
} }
for _, file := range ptest.XTestGoFiles { for _, file := range ptest.XTestGoFiles {
if err := t.load(filepath.Join(ptest.Dir, file), "_xtest", &t.NeedXtest); err != nil { if err := t.load(filepath.Join(ptest.Dir, file), "_xtest", &t.ImportXtest, &t.NeedXtest); err != nil {
return nil, err return nil, err
} }
} }
@ -1121,13 +1205,16 @@ func writeTestmain(out string, t *testFuncs) error {
} }
type testFuncs struct { type testFuncs struct {
Tests []testFunc Tests []testFunc
Benchmarks []testFunc Benchmarks []testFunc
Examples []testFunc Examples []testFunc
Package *Package TestMain *testFunc
NeedTest bool Package *Package
NeedXtest bool ImportTest bool
Cover []coverInfo NeedTest bool
ImportXtest bool
NeedXtest bool
Cover []coverInfo
} }
func (t *testFuncs) CoverMode() string { func (t *testFuncs) CoverMode() string {
@ -1162,7 +1249,7 @@ type testFunc struct {
var testFileSet = token.NewFileSet() var testFileSet = token.NewFileSet()
func (t *testFuncs) load(filename, pkg string, seen *bool) error { func (t *testFuncs) load(filename, pkg string, doImport, seen *bool) error {
f, err := parser.ParseFile(testFileSet, filename, nil, parser.ParseComments) f, err := parser.ParseFile(testFileSet, filename, nil, parser.ParseComments)
if err != nil { if err != nil {
return expandScanner(err) return expandScanner(err)
@ -1177,17 +1264,24 @@ func (t *testFuncs) load(filename, pkg string, seen *bool) error {
} }
name := n.Name.String() name := n.Name.String()
switch { switch {
case isTestMain(n):
if t.TestMain != nil {
return errors.New("multiple definitions of TestMain")
}
t.TestMain = &testFunc{pkg, name, ""}
*doImport, *seen = true, true
case isTest(name, "Test"): case isTest(name, "Test"):
t.Tests = append(t.Tests, testFunc{pkg, name, ""}) t.Tests = append(t.Tests, testFunc{pkg, name, ""})
*seen = true *doImport, *seen = true, true
case isTest(name, "Benchmark"): case isTest(name, "Benchmark"):
t.Benchmarks = append(t.Benchmarks, testFunc{pkg, name, ""}) t.Benchmarks = append(t.Benchmarks, testFunc{pkg, name, ""})
*seen = true *doImport, *seen = true, true
} }
} }
ex := doc.Examples(f) ex := doc.Examples(f)
sort.Sort(byOrder(ex)) sort.Sort(byOrder(ex))
for _, e := range ex { for _, e := range ex {
*doImport = true // import test file whether executed or not
if e.Output == "" && !e.EmptyOutput { if e.Output == "" && !e.EmptyOutput {
// Don't run examples with no output. // Don't run examples with no output.
continue continue
@ -1208,14 +1302,17 @@ var testmainTmpl = template.Must(template.New("main").Parse(`
package main package main
import ( import (
{{if not .TestMain}}
"os"
{{end}}
"regexp" "regexp"
"testing" "testing"
{{if .NeedTest}} {{if .ImportTest}}
_test {{.Package.ImportPath | printf "%q"}} {{if .NeedTest}}_test{{else}}_{{end}} {{.Package.ImportPath | printf "%q"}}
{{end}} {{end}}
{{if .NeedXtest}} {{if .ImportXtest}}
_xtest {{.Package.ImportPath | printf "%s_test" | printf "%q"}} {{if .NeedXtest}}_xtest{{else}}_{{end}} {{.Package.ImportPath | printf "%s_test" | printf "%q"}}
{{end}} {{end}}
{{range $i, $p := .Cover}} {{range $i, $p := .Cover}}
_cover{{$i}} {{$p.Package.ImportPath | printf "%q"}} _cover{{$i}} {{$p.Package.ImportPath | printf "%q"}}
@ -1302,7 +1399,12 @@ func main() {
CoveredPackages: {{printf "%q" .Covered}}, CoveredPackages: {{printf "%q" .Covered}},
}) })
{{end}} {{end}}
testing.Main(matchString, tests, benchmarks, examples) m := testing.MainStart(matchString, tests, benchmarks, examples)
{{with .TestMain}}
{{.Package}}.{{.Name}}(m)
{{else}}
os.Exit(m.Run())
{{end}}
} }
`)) `))

View File

@ -0,0 +1,13 @@
// Copyright 2014 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.
// Simple test for go generate.
// We include a build tag that go generate should ignore.
// +build ignore
//go:generate echo Success
package p

View File

@ -0,0 +1,10 @@
// Copyright 2014 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.
// Test that go generate handles command aliases.
//go:generate -command run echo Now is the time
//go:generate run for all good men
package p

View File

@ -0,0 +1,9 @@
// Copyright 2014 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.
// Test go generate variable substitution.
//go:generate echo $GOARCH $GOFILE $GOPACKAGE xyz$GOPACKAGE/$GOFILE/123
package p

View File

@ -0,0 +1,3 @@
package p
import "bad"

View File

@ -0,0 +1,3 @@
package p
import "conflict"

View File

@ -0,0 +1 @@
package bad // import

View File

@ -0,0 +1 @@
package conflict // import "a"

View File

@ -0,0 +1 @@
package conflict /* import "b" */

View File

@ -0,0 +1 @@
package x // import "works/x"

View File

@ -0,0 +1 @@
package x // important! not an import comment

View File

@ -0,0 +1 @@
package x // import "my/x"

View File

@ -0,0 +1,3 @@
package p
import _ "works/x"

View File

@ -0,0 +1,3 @@
package p
import "wrongplace"

View File

@ -0,0 +1,11 @@
package pkg_test
import "os"
func init() {
os.Stdout.Write([]byte("File with non-runnable example was built.\n"))
}
func Example_test() {
// This test will not be run, it has no "Output:" comment.
}

View File

@ -0,0 +1,10 @@
package pkg
import (
"os"
"testing"
)
func TestBuilt(t *testing.T) {
os.Stdout.Write([]byte("A normal test was executed.\n"))
}

View File

@ -0,0 +1 @@
package badc

View File

@ -0,0 +1,5 @@
package badexec
func init() {
panic("badexec")
}

View File

@ -0,0 +1 @@
package badsyntax

View File

@ -0,0 +1,3 @@
package badsyntax
func func func func func!

View File

@ -0,0 +1 @@
package badvar

View File

@ -0,0 +1,5 @@
package badvar_test
func f() {
_ = notdefined
}

View File

@ -0,0 +1 @@
package p_test

View File

@ -0,0 +1,7 @@
package p
import "fmt"
func f() {
fmt.Printf("%d")
}

View File

@ -0,0 +1,3 @@
package p
import _ "net/http/internal"

View File

@ -0,0 +1,3 @@
package p
import _ "./x/y/z/internal/w"

View File

@ -0,0 +1 @@
package w

View File

@ -65,9 +65,9 @@ type testFlagSpec struct {
var testFlagDefn = []*testFlagSpec{ var testFlagDefn = []*testFlagSpec{
// local. // local.
{name: "c", boolVar: &testC}, {name: "c", boolVar: &testC},
{name: "file", multiOK: true},
{name: "cover", boolVar: &testCover}, {name: "cover", boolVar: &testCover},
{name: "coverpkg"}, {name: "coverpkg"},
{name: "o"},
// build flags. // build flags.
{name: "a", boolVar: &buildA}, {name: "a", boolVar: &buildA},
@ -153,6 +153,9 @@ func testFlags(args []string) (packageNames, passToTest []string) {
// bool flags. // bool flags.
case "a", "c", "i", "n", "x", "v", "race", "cover", "work": case "a", "c", "i", "n", "x", "v", "race", "cover", "work":
setBoolFlag(f.boolVar, value) setBoolFlag(f.boolVar, value)
case "o":
testO = value
testNeedBinary = true
case "p": case "p":
setIntFlag(&buildP, value) setIntFlag(&buildP, value)
case "exec": case "exec":
@ -184,8 +187,6 @@ func testFlags(args []string) (packageNames, passToTest []string) {
buildContext.BuildTags = strings.Fields(value) buildContext.BuildTags = strings.Fields(value)
case "compiler": case "compiler":
buildCompiler{}.Set(value) buildCompiler{}.Set(value)
case "file":
testFiles = append(testFiles, value)
case "bench": case "bench":
// record that we saw the flag; don't care about the value // record that we saw the flag; don't care about the value
testBench = true testBench = true

View File

@ -0,0 +1,21 @@
// Copyright 2014 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.
// This file contains extra hooks for testing the go command.
// It is compiled into the Go binary only when building the
// test copy; it does not get compiled into the standard go
// command, so these testing hooks are not present in the
// go command that everyone uses.
// +build testgo
package main
import "os"
func init() {
if v := os.Getenv("TESTGO_IS_GO_RELEASE"); v != "" {
isGoRelease = v == "1"
}
}

View File

@ -47,13 +47,13 @@ const toolWindowsExtension = ".exe"
func tool(toolName string) string { func tool(toolName string) string {
toolPath := filepath.Join(toolDir, toolName) toolPath := filepath.Join(toolDir, toolName)
if toolIsWindows && toolName != "pprof" { if toolIsWindows {
toolPath += toolWindowsExtension toolPath += toolWindowsExtension
} }
// Give a nice message if there is no tool with that name. // Give a nice message if there is no tool with that name.
if _, err := os.Stat(toolPath); err != nil { if _, err := os.Stat(toolPath); err != nil {
if isInGoToolsRepo(toolName) { if isInGoToolsRepo(toolName) {
fmt.Fprintf(os.Stderr, "go tool: no such tool %q; to install:\n\tgo get code.google.com/p/go.tools/cmd/%s\n", toolName, toolName) fmt.Fprintf(os.Stderr, "go tool: no such tool %q; to install:\n\tgo get golang.org/x/tools/cmd/%s\n", toolName, toolName)
} else { } else {
fmt.Fprintf(os.Stderr, "go tool: no such tool %q\n", toolName) fmt.Fprintf(os.Stderr, "go tool: no such tool %q\n", toolName)
} }
@ -91,16 +91,6 @@ func runTool(cmd *Command, args []string) {
if toolPath == "" { if toolPath == "" {
return return
} }
if toolIsWindows && toolName == "pprof" {
args = append([]string{"perl", toolPath}, args[1:]...)
var err error
toolPath, err = exec.LookPath("perl")
if err != nil {
fmt.Fprintf(os.Stderr, "go tool: perl not found\n")
setExitStatus(3)
return
}
}
if toolN { if toolN {
fmt.Printf("%s %s\n", toolPath, strings.Join(args[1:], " ")) fmt.Printf("%s %s\n", toolPath, strings.Join(args[1:], " "))
return return

View File

@ -33,6 +33,9 @@ type vcsCmd struct {
scheme []string scheme []string
pingCmd string pingCmd string
remoteRepo func(v *vcsCmd, rootDir string) (remoteRepo string, err error)
resolveRepo func(v *vcsCmd, rootDir, remoteRepo string) (realRepo string, err error)
} }
// A tagCmd describes a command to list available tags // A tagCmd describes a command to list available tags
@ -81,8 +84,17 @@ var vcsHg = &vcsCmd{
tagSyncCmd: "update -r {tag}", tagSyncCmd: "update -r {tag}",
tagSyncDefault: "update default", tagSyncDefault: "update default",
scheme: []string{"https", "http", "ssh"}, scheme: []string{"https", "http", "ssh"},
pingCmd: "identify {scheme}://{repo}", pingCmd: "identify {scheme}://{repo}",
remoteRepo: hgRemoteRepo,
}
func hgRemoteRepo(vcsHg *vcsCmd, rootDir string) (remoteRepo string, err error) {
out, err := vcsHg.runOutput(rootDir, "paths default")
if err != nil {
return "", err
}
return strings.TrimSpace(string(out)), nil
} }
// vcsGit describes how to use Git. // vcsGit describes how to use Git.
@ -104,8 +116,38 @@ var vcsGit = &vcsCmd{
tagSyncCmd: "checkout {tag}", tagSyncCmd: "checkout {tag}",
tagSyncDefault: "checkout master", tagSyncDefault: "checkout master",
scheme: []string{"git", "https", "http", "git+ssh"}, scheme: []string{"git", "https", "http", "git+ssh"},
pingCmd: "ls-remote {scheme}://{repo}", pingCmd: "ls-remote {scheme}://{repo}",
remoteRepo: gitRemoteRepo,
}
func gitRemoteRepo(vcsGit *vcsCmd, rootDir string) (remoteRepo string, err error) {
outb, err := vcsGit.runOutput(rootDir, "remote -v")
if err != nil {
return "", err
}
out := string(outb)
// Expect:
// origin https://github.com/rsc/pdf (fetch)
// origin https://github.com/rsc/pdf (push)
// use first line only.
if !strings.HasPrefix(out, "origin\t") {
return "", fmt.Errorf("unable to parse output of git remote -v")
}
out = strings.TrimPrefix(out, "origin\t")
i := strings.Index(out, "\n")
if i < 0 {
return "", fmt.Errorf("unable to parse output of git remote -v")
}
out = out[:i]
i = strings.LastIndex(out, " ")
if i < 0 {
return "", fmt.Errorf("unable to parse output of git remote -v")
}
out = out[:i]
return strings.TrimSpace(string(out)), nil
} }
// vcsBzr describes how to use Bazaar. // vcsBzr describes how to use Bazaar.
@ -123,8 +165,51 @@ var vcsBzr = &vcsCmd{
tagSyncCmd: "update -r {tag}", tagSyncCmd: "update -r {tag}",
tagSyncDefault: "update -r revno:-1", tagSyncDefault: "update -r revno:-1",
scheme: []string{"https", "http", "bzr", "bzr+ssh"}, scheme: []string{"https", "http", "bzr", "bzr+ssh"},
pingCmd: "info {scheme}://{repo}", pingCmd: "info {scheme}://{repo}",
remoteRepo: bzrRemoteRepo,
resolveRepo: bzrResolveRepo,
}
func bzrRemoteRepo(vcsBzr *vcsCmd, rootDir string) (remoteRepo string, err error) {
outb, err := vcsBzr.runOutput(rootDir, "config parent_location")
if err != nil {
return "", err
}
return strings.TrimSpace(string(outb)), nil
}
func bzrResolveRepo(vcsBzr *vcsCmd, rootDir, remoteRepo string) (realRepo string, err error) {
outb, err := vcsBzr.runOutput(rootDir, "info "+remoteRepo)
if err != nil {
return "", err
}
out := string(outb)
// Expect:
// ...
// (branch root|repository branch): <URL>
// ...
found := false
for _, prefix := range []string{"\n branch root: ", "\n repository branch: "} {
i := strings.Index(out, prefix)
if i >= 0 {
out = out[i+len(prefix):]
found = true
break
}
}
if !found {
return "", fmt.Errorf("unable to parse output of bzr info")
}
i := strings.Index(out, "\n")
if i < 0 {
return "", fmt.Errorf("unable to parse output of bzr info")
}
out = out[:i]
return strings.TrimSpace(string(out)), nil
} }
// vcsSvn describes how to use Subversion. // vcsSvn describes how to use Subversion.
@ -138,8 +223,34 @@ var vcsSvn = &vcsCmd{
// There is no tag command in subversion. // There is no tag command in subversion.
// The branch information is all in the path names. // The branch information is all in the path names.
scheme: []string{"https", "http", "svn", "svn+ssh"}, scheme: []string{"https", "http", "svn", "svn+ssh"},
pingCmd: "info {scheme}://{repo}", pingCmd: "info {scheme}://{repo}",
remoteRepo: svnRemoteRepo,
}
func svnRemoteRepo(vcsSvn *vcsCmd, rootDir string) (remoteRepo string, err error) {
outb, err := vcsSvn.runOutput(rootDir, "info")
if err != nil {
return "", err
}
out := string(outb)
// Expect:
// ...
// Repository Root: <URL>
// ...
i := strings.Index(out, "\nRepository Root: ")
if i < 0 {
return "", fmt.Errorf("unable to parse output of svn info")
}
out = out[i+len("\nRepository Root: "):]
i = strings.Index(out, "\n")
if i < 0 {
return "", fmt.Errorf("unable to parse output of svn info")
}
out = out[:i]
return strings.TrimSpace(string(out)), nil
} }
func (v *vcsCmd) String() string { func (v *vcsCmd) String() string {
@ -361,7 +472,14 @@ var httpPrefixRE = regexp.MustCompile(`^https?:`)
func repoRootForImportPath(importPath string) (*repoRoot, error) { func repoRootForImportPath(importPath string) (*repoRoot, error) {
rr, err := repoRootForImportPathStatic(importPath, "") rr, err := repoRootForImportPathStatic(importPath, "")
if err == errUnknownSite { if err == errUnknownSite {
rr, err = repoRootForImportDynamic(importPath) // If there are wildcards, look up the thing before the wildcard,
// hoping it applies to the wildcarded parts too.
// This makes 'go get rsc.io/pdf/...' work in a fresh GOPATH.
lookup := strings.TrimSuffix(importPath, "/...")
if i := strings.Index(lookup, "/.../"); i >= 0 {
lookup = lookup[:i]
}
rr, err = repoRootForImportDynamic(lookup)
// repoRootForImportDynamic returns error detail // repoRootForImportDynamic returns error detail
// that is irrelevant if the user didn't intend to use a // that is irrelevant if the user didn't intend to use a
@ -465,11 +583,11 @@ func repoRootForImportPathStatic(importPath, scheme string) (*repoRoot, error) {
func repoRootForImportDynamic(importPath string) (*repoRoot, error) { func repoRootForImportDynamic(importPath string) (*repoRoot, error) {
slash := strings.Index(importPath, "/") slash := strings.Index(importPath, "/")
if slash < 0 { if slash < 0 {
return nil, errors.New("import path doesn't contain a slash") return nil, errors.New("import path does not contain a slash")
} }
host := importPath[:slash] host := importPath[:slash]
if !strings.Contains(host, ".") { if !strings.Contains(host, ".") {
return nil, errors.New("import path doesn't contain a hostname") return nil, errors.New("import path does not begin with hostname")
} }
urlStr, body, err := httpsOrHTTP(importPath) urlStr, body, err := httpsOrHTTP(importPath)
if err != nil { if err != nil {
@ -613,6 +731,15 @@ var vcsPaths = []*vcsPath{
check: launchpadVCS, check: launchpadVCS,
}, },
// IBM DevOps Services (JazzHub)
{
prefix: "hub.jazz.net/git",
re: `^(?P<root>hub.jazz.net/git/[a-z0-9]+/[A-Za-z0-9_.\-]+)(/[A-Za-z0-9_.\-]+)*$`,
vcs: "git",
repo: "https://{root}",
check: noVCSSuffix,
},
// General syntax for any server. // General syntax for any server.
{ {
re: `^(?P<root>(?P<repo>([a-z0-9.\-]+\.)+[a-z0-9.\-]+(:[0-9]+)?/[A-Za-z0-9_.\-/]*?)\.(?P<vcs>bzr|git|hg|svn))(/[A-Za-z0-9_.\-]+)*$`, re: `^(?P<root>(?P<repo>([a-z0-9.\-]+\.)+[a-z0-9.\-]+(:[0-9]+)?/[A-Za-z0-9_.\-/]*?)\.(?P<vcs>bzr|git|hg|svn))(/[A-Za-z0-9_.\-]+)*$`,

View File

@ -0,0 +1,124 @@
// Copyright 2014 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 (
"runtime"
"testing"
)
// Test that RepoRootForImportPath creates the correct RepoRoot for a given importPath.
// TODO(cmang): Add tests for SVN and BZR.
func TestRepoRootForImportPath(t *testing.T) {
if testing.Short() {
t.Skip("skipping test to avoid external network")
}
switch runtime.GOOS {
case "nacl", "android":
t.Skipf("no networking available on %s", runtime.GOOS)
}
tests := []struct {
path string
want *repoRoot
}{
{
"code.google.com/p/go",
&repoRoot{
vcs: vcsHg,
repo: "https://code.google.com/p/go",
},
},
/*{
"code.google.com/r/go",
&repoRoot{
vcs: vcsHg,
repo: "https://code.google.com/r/go",
},
},*/
{
"github.com/golang/groupcache",
&repoRoot{
vcs: vcsGit,
repo: "https://github.com/golang/groupcache",
},
},
// IBM DevOps Services tests
{
"hub.jazz.net/git/user1/pkgname",
&repoRoot{
vcs: vcsGit,
repo: "https://hub.jazz.net/git/user1/pkgname",
},
},
{
"hub.jazz.net/git/user1/pkgname/submodule/submodule/submodule",
&repoRoot{
vcs: vcsGit,
repo: "https://hub.jazz.net/git/user1/pkgname",
},
},
{
"hub.jazz.net",
nil,
},
{
"hub2.jazz.net",
nil,
},
{
"hub.jazz.net/someotherprefix",
nil,
},
{
"hub.jazz.net/someotherprefix/user1/pkgname",
nil,
},
// Spaces are not valid in user names or package names
{
"hub.jazz.net/git/User 1/pkgname",
nil,
},
{
"hub.jazz.net/git/user1/pkg name",
nil,
},
// Dots are not valid in user names
{
"hub.jazz.net/git/user.1/pkgname",
nil,
},
{
"hub.jazz.net/git/user/pkg.name",
&repoRoot{
vcs: vcsGit,
repo: "https://hub.jazz.net/git/user/pkg.name",
},
},
// User names cannot have uppercase letters
{
"hub.jazz.net/git/USER/pkgname",
nil,
},
}
for _, test := range tests {
got, err := repoRootForImportPath(test.path)
want := test.want
if want == nil {
if err == nil {
t.Errorf("RepoRootForImport(%q): Error expected but not received", test.path)
}
continue
}
if err != nil {
t.Errorf("RepoRootForImport(%q): %v", test.path, err)
continue
}
if got.vcs.name != want.vcs.name || got.repo != want.repo {
t.Errorf("RepoRootForImport(%q) = VCS(%s) Repo(%s), want VCS(%s) Repo(%s)", test.path, got.vcs, got.repo, want.vcs, want.repo)
}
}
}

View File

@ -4,6 +4,8 @@
package main package main
import "path/filepath"
func init() { func init() {
addBuildFlagsNX(cmdVet) addBuildFlagsNX(cmdVet)
} }
@ -15,7 +17,7 @@ var cmdVet = &Command{
Long: ` Long: `
Vet runs the Go vet command on the packages named by the import paths. Vet runs the Go vet command on the packages named by the import paths.
For more about vet, see 'godoc code.google.com/p/go.tools/cmd/vet'. For more about vet, see 'godoc golang.org/x/tools/cmd/vet'.
For more about specifying packages, see 'go help packages'. For more about specifying packages, see 'go help packages'.
To run the vet tool with specific options, run 'go tool vet'. To run the vet tool with specific options, run 'go tool vet'.
@ -28,10 +30,21 @@ See also: go fmt, go fix.
} }
func runVet(cmd *Command, args []string) { func runVet(cmd *Command, args []string) {
for _, pkg := range packages(args) { for _, p := range packages(args) {
// Use pkg.gofiles instead of pkg.Dir so that // Vet expects to be given a set of files all from the same package.
// the command only applies to this package, // Run once for package p and once for package p_test.
// not to packages in subdirectories. if len(p.GoFiles)+len(p.CgoFiles)+len(p.TestGoFiles) > 0 {
run(tool("vet"), relPaths(stringList(pkg.gofiles, pkg.sfiles))) runVetFiles(p, stringList(p.GoFiles, p.CgoFiles, p.TestGoFiles, p.SFiles))
}
if len(p.XTestGoFiles) > 0 {
runVetFiles(p, stringList(p.XTestGoFiles))
}
} }
} }
func runVetFiles(p *Package, files []string) {
for i := range files {
files[i] = filepath.Join(p.Dir, files[i])
}
run(tool("vet"), relPaths(files))
}

View File

@ -67,7 +67,7 @@ To remove the parentheses:
To convert the package tree from explicit slice upper bounds to implicit ones: To convert the package tree from explicit slice upper bounds to implicit ones:
gofmt -r 'α[β:len(α)] -> α[β:]' -w $GOROOT/src/pkg gofmt -r 'α[β:len(α)] -> α[β:]' -w $GOROOT/src
The simplify command The simplify command

View File

@ -87,13 +87,13 @@ func processFile(filename string, in io.Reader, out io.Writer, stdin bool) error
return err return err
} }
file, adjust, err := parse(fileSet, filename, src, stdin) file, sourceAdj, indentAdj, err := parse(fileSet, filename, src, stdin)
if err != nil { if err != nil {
return err return err
} }
if rewrite != nil { if rewrite != nil {
if adjust == nil { if sourceAdj == nil {
file = rewrite(file) file = rewrite(file)
} else { } else {
fmt.Fprintf(os.Stderr, "warning: rewrite ignored for incomplete programs\n") fmt.Fprintf(os.Stderr, "warning: rewrite ignored for incomplete programs\n")
@ -106,15 +106,10 @@ func processFile(filename string, in io.Reader, out io.Writer, stdin bool) error
simplify(file) simplify(file)
} }
var buf bytes.Buffer res, err := format(fileSet, file, sourceAdj, indentAdj, src, printer.Config{Mode: printerMode, Tabwidth: tabWidth})
err = (&printer.Config{Mode: printerMode, Tabwidth: tabWidth}).Fprint(&buf, fileSet, file)
if err != nil { if err != nil {
return err return err
} }
res := buf.Bytes()
if adjust != nil {
res = adjust(src, res)
}
if !bytes.Equal(src, res) { if !bytes.Equal(src, res) {
// formatting has changed // formatting has changed
@ -122,7 +117,7 @@ func processFile(filename string, in io.Reader, out io.Writer, stdin bool) error
fmt.Fprintln(out, filename) fmt.Fprintln(out, filename)
} }
if *write { if *write {
err = ioutil.WriteFile(filename, res, 0) err = ioutil.WriteFile(filename, res, 0644)
if err != nil { if err != nil {
return err return err
} }
@ -186,6 +181,11 @@ func gofmtMain() {
initRewrite() initRewrite()
if flag.NArg() == 0 { if flag.NArg() == 0 {
if *write {
fmt.Fprintln(os.Stderr, "error: cannot use -w with standard input")
exitCode = 2
return
}
if err := processFile("<standard input>", os.Stdin, os.Stdout, true); err != nil { if err := processFile("<standard input>", os.Stdin, os.Stdout, true); err != nil {
report(err) report(err)
} }
@ -235,19 +235,29 @@ func diff(b1, b2 []byte) (data []byte, err error) {
} }
// parse parses src, which was read from filename, // ----------------------------------------------------------------------------
// as a Go source file or statement list. // Support functions
func parse(fset *token.FileSet, filename string, src []byte, stdin bool) (*ast.File, func(orig, src []byte) []byte, error) { //
// The functions parse, format, and isSpace below are identical to the
// respective functions in src/go/format/format.go - keep them in sync!
//
// TODO(gri) Factor out this functionality, eventually.
// parse parses src, which was read from the named file,
// as a Go source file, declaration, or statement list.
func parse(fset *token.FileSet, filename string, src []byte, fragmentOk bool) (
file *ast.File,
sourceAdj func(src []byte, indent int) []byte,
indentAdj int,
err error,
) {
// Try as whole source file. // Try as whole source file.
file, err := parser.ParseFile(fset, filename, src, parserMode) file, err = parser.ParseFile(fset, filename, src, parserMode)
if err == nil { // If there's no error, return. If the error is that the source file didn't begin with a
return file, nil, nil // package line and source fragments are ok, fall through to
}
// If the error is that the source file didn't begin with a
// package line and this is standard input, fall through to
// try as a source fragment. Stop and return on any other error. // try as a source fragment. Stop and return on any other error.
if !stdin || !strings.Contains(err.Error(), "expected 'package'") { if err == nil || !fragmentOk || !strings.Contains(err.Error(), "expected 'package'") {
return nil, nil, err return
} }
// If this is a declaration list, make it a source file // If this is a declaration list, make it a source file
@ -257,19 +267,19 @@ func parse(fset *token.FileSet, filename string, src []byte, stdin bool) (*ast.F
psrc := append([]byte("package p;"), src...) psrc := append([]byte("package p;"), src...)
file, err = parser.ParseFile(fset, filename, psrc, parserMode) file, err = parser.ParseFile(fset, filename, psrc, parserMode)
if err == nil { if err == nil {
adjust := func(orig, src []byte) []byte { sourceAdj = func(src []byte, indent int) []byte {
// Remove the package clause. // Remove the package clause.
// Gofmt has turned the ; into a \n. // Gofmt has turned the ; into a \n.
src = src[len("package p\n"):] src = src[indent+len("package p\n"):]
return matchSpace(orig, src) return bytes.TrimSpace(src)
} }
return file, adjust, nil return
} }
// If the error is that the source file didn't begin with a // If the error is that the source file didn't begin with a
// declaration, fall through to try as a statement list. // declaration, fall through to try as a statement list.
// Stop and return on any other error. // Stop and return on any other error.
if !strings.Contains(err.Error(), "expected declaration") { if !strings.Contains(err.Error(), "expected declaration") {
return nil, nil, err return
} }
// If this is a statement list, make it a source file // If this is a statement list, make it a source file
@ -277,68 +287,101 @@ func parse(fset *token.FileSet, filename string, src []byte, stdin bool) (*ast.F
// into a function body. This handles expressions too. // into a function body. This handles expressions too.
// Insert using a ;, not a newline, so that the line numbers // Insert using a ;, not a newline, so that the line numbers
// in fsrc match the ones in src. // in fsrc match the ones in src.
fsrc := append(append([]byte("package p; func _() {"), src...), '}') fsrc := append(append([]byte("package p; func _() {"), src...), '\n', '}')
file, err = parser.ParseFile(fset, filename, fsrc, parserMode) file, err = parser.ParseFile(fset, filename, fsrc, parserMode)
if err == nil { if err == nil {
adjust := func(orig, src []byte) []byte { sourceAdj = func(src []byte, indent int) []byte {
// Cap adjusted indent to zero.
if indent < 0 {
indent = 0
}
// Remove the wrapping. // Remove the wrapping.
// Gofmt has turned the ; into a \n\n. // Gofmt has turned the ; into a \n\n.
src = src[len("package p\n\nfunc _() {"):] // There will be two non-blank lines with indent, hence 2*indent.
src = src[:len(src)-len("}\n")] src = src[2*indent+len("package p\n\nfunc _() {"):]
// Gofmt has also indented the function body one level. src = src[:len(src)-(indent+len("\n}\n"))]
// Remove that indent. return bytes.TrimSpace(src)
src = bytes.Replace(src, []byte("\n\t"), []byte("\n"), -1)
return matchSpace(orig, src)
} }
return file, adjust, nil // Gofmt has also indented the function body one level.
// Adjust that with indentAdj.
indentAdj = -1
} }
// Failed, and out of options. // Succeeded, or out of options.
return nil, nil, err return
} }
func cutSpace(b []byte) (before, middle, after []byte) { // format formats the given package file originally obtained from src
i := 0 // and adjusts the result based on the original source via sourceAdj
for i < len(b) && (b[i] == ' ' || b[i] == '\t' || b[i] == '\n') { // and indentAdj.
i++ func format(
fset *token.FileSet,
file *ast.File,
sourceAdj func(src []byte, indent int) []byte,
indentAdj int,
src []byte,
cfg printer.Config,
) ([]byte, error) {
if sourceAdj == nil {
// Complete source file.
var buf bytes.Buffer
err := cfg.Fprint(&buf, fset, file)
if err != nil {
return nil, err
}
return buf.Bytes(), nil
} }
j := len(b)
for j > 0 && (b[j-1] == ' ' || b[j-1] == '\t' || b[j-1] == '\n') { // Partial source file.
j-- // Determine and prepend leading space.
i, j := 0, 0
for j < len(src) && isSpace(src[j]) {
if src[j] == '\n' {
i = j + 1 // byte offset of last line in leading space
}
j++
} }
if i <= j { var res []byte
return b[:i], b[i:j], b[j:] res = append(res, src[:i]...)
// Determine and prepend indentation of first code line.
// Spaces are ignored unless there are no tabs,
// in which case spaces count as one tab.
indent := 0
hasSpace := false
for _, b := range src[i:j] {
switch b {
case ' ':
hasSpace = true
case '\t':
indent++
}
} }
return nil, nil, b[j:] if indent == 0 && hasSpace {
indent = 1
}
for i := 0; i < indent; i++ {
res = append(res, '\t')
}
// Format the source.
// Write it without any leading and trailing space.
cfg.Indent = indent + indentAdj
var buf bytes.Buffer
err := cfg.Fprint(&buf, fset, file)
if err != nil {
return nil, err
}
res = append(res, sourceAdj(buf.Bytes(), cfg.Indent)...)
// Determine and append trailing space.
i = len(src)
for i > 0 && isSpace(src[i-1]) {
i--
}
return append(res, src[i:]...), nil
} }
// matchSpace reformats src to use the same space context as orig. func isSpace(b byte) bool {
// 1) If orig begins with blank lines, matchSpace inserts them at the beginning of src. return b == ' ' || b == '\t' || b == '\n' || b == '\r'
// 2) matchSpace copies the indentation of the first non-blank line in orig
// to every non-blank line in src.
// 3) matchSpace copies the trailing space from orig and uses it in place
// of src's trailing space.
func matchSpace(orig []byte, src []byte) []byte {
before, _, after := cutSpace(orig)
i := bytes.LastIndex(before, []byte{'\n'})
before, indent := before[:i+1], before[i+1:]
_, src, _ = cutSpace(src)
var b bytes.Buffer
b.Write(before)
for len(src) > 0 {
line := src
if i := bytes.IndexByte(line, '\n'); i >= 0 {
line, src = line[:i+1], line[i+1:]
} else {
src = nil
}
if len(line) > 0 && line[0] != '\n' { // not blank
b.Write(indent)
}
b.Write(line)
}
b.Write(after)
return b.Bytes()
} }

View File

@ -6,18 +6,60 @@ package main
import ( import (
"bytes" "bytes"
"flag"
"io/ioutil" "io/ioutil"
"os"
"path/filepath" "path/filepath"
"strings" "strings"
"testing" "testing"
"text/scanner"
) )
func runTest(t *testing.T, in, out, flags string) { var update = flag.Bool("update", false, "update .golden files")
// gofmtFlags looks for a comment of the form
//
// //gofmt flags
//
// within the first maxLines lines of the given file,
// and returns the flags string, if any. Otherwise it
// returns the empty string.
func gofmtFlags(filename string, maxLines int) string {
f, err := os.Open(filename)
if err != nil {
return "" // ignore errors - they will be found later
}
defer f.Close()
// initialize scanner
var s scanner.Scanner
s.Init(f)
s.Error = func(*scanner.Scanner, string) {} // ignore errors
s.Mode = scanner.GoTokens &^ scanner.SkipComments // want comments
// look for //gofmt comment
for s.Line <= maxLines {
switch s.Scan() {
case scanner.Comment:
const prefix = "//gofmt "
if t := s.TokenText(); strings.HasPrefix(t, prefix) {
return strings.TrimSpace(t[len(prefix):])
}
case scanner.EOF:
return ""
}
}
return ""
}
func runTest(t *testing.T, in, out string) {
// process flags // process flags
*simplifyAST = false *simplifyAST = false
*rewriteRule = "" *rewriteRule = ""
stdin := false stdin := false
for _, flag := range strings.Split(flags, " ") { for _, flag := range strings.Split(gofmtFlags(in, 20), " ") {
elts := strings.SplitN(flag, "=", 2) elts := strings.SplitN(flag, "=", 2)
name := elts[0] name := elts[0]
value := "" value := ""
@ -56,6 +98,17 @@ func runTest(t *testing.T, in, out, flags string) {
} }
if got := buf.Bytes(); !bytes.Equal(got, expected) { if got := buf.Bytes(); !bytes.Equal(got, expected) {
if *update {
if in != out {
if err := ioutil.WriteFile(out, got, 0666); err != nil {
t.Error(err)
}
return
}
// in == out: don't accidentally destroy input
t.Errorf("WARNING: -update did not rewrite input file %s", in)
}
t.Errorf("(gofmt %s) != %s (see %s.gofmt)", in, out, in) t.Errorf("(gofmt %s) != %s (see %s.gofmt)", in, out, in)
d, err := diff(expected, got) d, err := diff(expected, got)
if err == nil { if err == nil {
@ -67,51 +120,37 @@ func runTest(t *testing.T, in, out, flags string) {
} }
} }
var tests = []struct { // TestRewrite processes testdata/*.input files and compares them to the
in, flags string // corresponding testdata/*.golden files. The gofmt flags used to process
}{ // a file must be provided via a comment of the form
{"gofmt.go", ""}, //
{"gofmt_test.go", ""}, // //gofmt flags
{"testdata/composites.input", "-s"}, //
{"testdata/slices1.input", "-s"}, // in the processed file within the first 20 lines, if any.
{"testdata/slices2.input", "-s"},
{"testdata/old.input", ""},
{"testdata/rewrite1.input", "-r=Foo->Bar"},
{"testdata/rewrite2.input", "-r=int->bool"},
{"testdata/rewrite3.input", "-r=x->x"},
{"testdata/rewrite4.input", "-r=(x)->x"},
{"testdata/rewrite5.input", "-r=x+x->2*x"},
{"testdata/rewrite6.input", "-r=fun(x)->Fun(x)"},
{"testdata/rewrite7.input", "-r=fun(x...)->Fun(x)"},
{"testdata/rewrite8.input", "-r=interface{}->int"},
{"testdata/stdin*.input", "-stdin"},
{"testdata/comments.input", ""},
{"testdata/import.input", ""},
{"testdata/crlf.input", ""}, // test case for issue 3961; see also TestCRLF
{"testdata/typeswitch.input", ""}, // test case for issue 4470
}
func TestRewrite(t *testing.T) { func TestRewrite(t *testing.T) {
for _, test := range tests { // determine input files
match, err := filepath.Glob(test.in) match, err := filepath.Glob("testdata/*.input")
if err != nil { if err != nil {
t.Error(err) t.Fatal(err)
continue }
// add larger examples
match = append(match, "gofmt.go", "gofmt_test.go")
for _, in := range match {
out := in // for files where input and output are identical
if strings.HasSuffix(in, ".input") {
out = in[:len(in)-len(".input")] + ".golden"
} }
for _, in := range match { runTest(t, in, out)
out := in if in != out {
if strings.HasSuffix(in, ".input") { // Check idempotence.
out = in[:len(in)-len(".input")] + ".golden" runTest(t, out, out)
}
runTest(t, in, out, test.flags)
if in != out {
// Check idempotence.
runTest(t, out, out, test.flags)
}
} }
} }
} }
// Test case for issue 3961.
func TestCRLF(t *testing.T) { func TestCRLF(t *testing.T) {
const input = "testdata/crlf.input" // must contain CR/LF's const input = "testdata/crlf.input" // must contain CR/LF's
const golden = "testdata/crlf.golden" // must not contain any CR's const golden = "testdata/crlf.golden" // must not contain any CR's

View File

@ -32,7 +32,7 @@ var (
) )
func gofmt(fset *token.FileSet, filename string, src *bytes.Buffer) error { func gofmt(fset *token.FileSet, filename string, src *bytes.Buffer) error {
f, _, err := parse(fset, filename, src.Bytes(), false) f, _, _, err := parse(fset, filename, src.Bytes(), false)
if err != nil { if err != nil {
return err return err
} }
@ -60,7 +60,7 @@ func testFile(t *testing.T, b1, b2 *bytes.Buffer, filename string) {
// exclude files w/ syntax errors (typically test cases) // exclude files w/ syntax errors (typically test cases)
fset := token.NewFileSet() fset := token.NewFileSet()
if _, _, err = parse(fset, filename, b1.Bytes(), false); err != nil { if _, _, _, err = parse(fset, filename, b1.Bytes(), false); err != nil {
if *verbose { if *verbose {
fmt.Fprintf(os.Stderr, "ignoring %s\n", err) fmt.Fprintf(os.Stderr, "ignoring %s\n", err)
} }

View File

@ -226,9 +226,6 @@ func match(m map[string]reflect.Value, pattern, val reflect.Value) bool {
return true return true
case reflect.Struct: case reflect.Struct:
if p.NumField() != v.NumField() {
return false
}
for i := 0; i < p.NumField(); i++ { for i := 0; i < p.NumField(); i++ {
if !match(m, p.Field(i), v.Field(i)) { if !match(m, p.Field(i), v.Field(i)) {
return false return false

View File

@ -68,9 +68,10 @@ func (s *simplifier) Visit(node ast.Node) ast.Visitor {
// a slice expression of the form: s[a:len(s)] // a slice expression of the form: s[a:len(s)]
// can be simplified to: s[a:] // can be simplified to: s[a:]
// if s is "simple enough" (for now we only accept identifiers) // if s is "simple enough" (for now we only accept identifiers)
if s.hasDotImport { if n.Max != nil || s.hasDotImport {
// if dot imports are present, we cannot be certain that an // - 3-index slices always require the 2nd and 3rd index
// unresolved "len" identifier refers to the predefined len() // - if dot imports are present, we cannot be certain that an
// unresolved "len" identifier refers to the predefined len()
break break
} }
if s, _ := n.X.(*ast.Ident); s != nil && s.Obj != nil { if s, _ := n.X.(*ast.Ident); s != nil && s.Obj != nil {
@ -96,16 +97,26 @@ func (s *simplifier) Visit(node ast.Node) ast.Visitor {
// x, y := b[:n], b[n:] // x, y := b[:n], b[n:]
case *ast.RangeStmt: case *ast.RangeStmt:
// a range of the form: for x, _ = range v {...} // - a range of the form: for x, _ = range v {...}
// can be simplified to: for x = range v {...} // can be simplified to: for x = range v {...}
if ident, _ := n.Value.(*ast.Ident); ident != nil && ident.Name == "_" { // - a range of the form: for _ = range v {...}
// can be simplified to: for range v {...}
if isBlank(n.Value) {
n.Value = nil n.Value = nil
} }
if isBlank(n.Key) && n.Value == nil {
n.Key = nil
}
} }
return s return s
} }
func isBlank(x ast.Expr) bool {
ident, ok := x.(*ast.Ident)
return ok && ident.Name == "_"
}
func simplify(f *ast.File) { func simplify(f *ast.File) {
var s simplifier var s simplifier
@ -117,5 +128,34 @@ func simplify(f *ast.File) {
} }
} }
// remove empty declarations such as "const ()", etc
removeEmptyDeclGroups(f)
ast.Walk(&s, f) ast.Walk(&s, f)
} }
func removeEmptyDeclGroups(f *ast.File) {
i := 0
for _, d := range f.Decls {
if g, ok := d.(*ast.GenDecl); !ok || !isEmpty(f, g) {
f.Decls[i] = d
i++
}
}
f.Decls = f.Decls[:i]
}
func isEmpty(f *ast.File, g *ast.GenDecl) bool {
if g.Doc != nil || g.Specs != nil {
return false
}
for _, c := range f.Comments {
// if there is a comment in the declaration, it is not considered empty
if g.Pos() <= c.Pos() && c.End() <= g.End() {
return false
}
}
return true
}

View File

@ -1,3 +1,5 @@
//gofmt -s
package P package P
type T struct { type T struct {

View File

@ -1,3 +1,5 @@
//gofmt -s
package P package P
type T struct { type T struct {

View File

@ -2,6 +2,7 @@
Source containing CR/LF line endings. Source containing CR/LF line endings.
The gofmt'ed output must only have LF The gofmt'ed output must only have LF
line endings. line endings.
Test case for issue 3961.
*/ */
package main package main

View File

@ -2,6 +2,7 @@
Source containing CR/LF line endings. Source containing CR/LF line endings.
The gofmt'ed output must only have LF The gofmt'ed output must only have LF
line endings. line endings.
Test case for issue 3961.
*/ */
package main package main

View File

@ -1,3 +1,5 @@
//gofmt -r=Foo->Bar
// Copyright 2011 The Go Authors. All rights reserved. // Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.

View File

@ -1,3 +1,5 @@
//gofmt -r=Foo->Bar
// Copyright 2011 The Go Authors. All rights reserved. // Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.

View File

@ -1,3 +1,5 @@
//gofmt -r=int->bool
// Copyright 2011 The Go Authors. All rights reserved. // Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.

View File

@ -1,3 +1,5 @@
//gofmt -r=int->bool
// Copyright 2011 The Go Authors. All rights reserved. // Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.

View File

@ -1,3 +1,5 @@
//gofmt -r=x->x
// Copyright 2011 The Go Authors. All rights reserved. // Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.

View File

@ -1,3 +1,5 @@
//gofmt -r=x->x
// Copyright 2011 The Go Authors. All rights reserved. // Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.

View File

@ -1,3 +1,5 @@
//gofmt -r=(x)->x
// Copyright 2012 The Go Authors. All rights reserved. // Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.

View File

@ -1,3 +1,5 @@
//gofmt -r=(x)->x
// Copyright 2012 The Go Authors. All rights reserved. // Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.

View File

@ -1,3 +1,5 @@
//gofmt -r=x+x->2*x
// Copyright 2011 The Go Authors. All rights reserved. // Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.

View File

@ -1,3 +1,5 @@
//gofmt -r=x+x->2*x
// Copyright 2011 The Go Authors. All rights reserved. // Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.

View File

@ -1,3 +1,5 @@
//gofmt -r=fun(x)->Fun(x)
// Copyright 2013 The Go Authors. All rights reserved. // Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.

View File

@ -1,3 +1,5 @@
//gofmt -r=fun(x)->Fun(x)
// Copyright 2013 The Go Authors. All rights reserved. // Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.

View File

@ -1,3 +1,5 @@
//gofmt -r=fun(x...)->Fun(x)
// Copyright 2013 The Go Authors. All rights reserved. // Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.

View File

@ -1,3 +1,5 @@
//gofmt -r=fun(x...)->Fun(x)
// Copyright 2013 The Go Authors. All rights reserved. // Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.

View File

@ -1,3 +1,5 @@
//gofmt -r=interface{}->int
// Copyright 2013 The Go Authors. All rights reserved. // Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.

View File

@ -1,3 +1,5 @@
//gofmt -r=interface{}->int
// Copyright 2013 The Go Authors. All rights reserved. // Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.

View File

@ -1,3 +1,5 @@
//gofmt -s
// Test cases for slice expression simplification. // Test cases for slice expression simplification.
package p package p
@ -15,6 +17,7 @@ var (
_ = a[3:(len(a))] _ = a[3:(len(a))]
_ = a[len(a) : len(a)-1] _ = a[len(a) : len(a)-1]
_ = a[0:len(b)] _ = a[0:len(b)]
_ = a[2:len(a):len(a)]
_ = a[:] _ = a[:]
_ = a[:10] _ = a[:10]
@ -22,6 +25,7 @@ var (
_ = a[:(len(a))] _ = a[:(len(a))]
_ = a[:len(a)-1] _ = a[:len(a)-1]
_ = a[:len(b)] _ = a[:len(b)]
_ = a[:len(a):len(a)]
_ = s[0:] _ = s[0:]
_ = s[1:10] _ = s[1:10]
@ -29,6 +33,7 @@ var (
_ = s[3:(len(s))] _ = s[3:(len(s))]
_ = s[len(a) : len(s)-1] _ = s[len(a) : len(s)-1]
_ = s[0:len(b)] _ = s[0:len(b)]
_ = s[2:len(s):len(s)]
_ = s[:] _ = s[:]
_ = s[:10] _ = s[:10]
@ -36,6 +41,7 @@ var (
_ = s[:(len(s))] _ = s[:(len(s))]
_ = s[:len(s)-1] _ = s[:len(s)-1]
_ = s[:len(b)] _ = s[:len(b)]
_ = s[:len(s):len(s)]
_ = t.s[0:] _ = t.s[0:]
_ = t.s[1:10] _ = t.s[1:10]
@ -43,6 +49,7 @@ var (
_ = t.s[3:(len(t.s))] _ = t.s[3:(len(t.s))]
_ = t.s[len(a) : len(t.s)-1] _ = t.s[len(a) : len(t.s)-1]
_ = t.s[0:len(b)] _ = t.s[0:len(b)]
_ = t.s[2:len(t.s):len(t.s)]
_ = t.s[:] _ = t.s[:]
_ = t.s[:10] _ = t.s[:10]
@ -50,6 +57,7 @@ var (
_ = t.s[:(len(t.s))] _ = t.s[:(len(t.s))]
_ = t.s[:len(t.s)-1] _ = t.s[:len(t.s)-1]
_ = t.s[:len(b)] _ = t.s[:len(b)]
_ = t.s[:len(t.s):len(t.s)]
) )
func _() { func _() {

View File

@ -1,3 +1,5 @@
//gofmt -s
// Test cases for slice expression simplification. // Test cases for slice expression simplification.
package p package p
@ -15,6 +17,7 @@ var (
_ = a[3:(len(a))] _ = a[3:(len(a))]
_ = a[len(a) : len(a)-1] _ = a[len(a) : len(a)-1]
_ = a[0:len(b)] _ = a[0:len(b)]
_ = a[2:len(a):len(a)]
_ = a[:] _ = a[:]
_ = a[:10] _ = a[:10]
@ -22,6 +25,7 @@ var (
_ = a[:(len(a))] _ = a[:(len(a))]
_ = a[:len(a)-1] _ = a[:len(a)-1]
_ = a[:len(b)] _ = a[:len(b)]
_ = a[:len(a):len(a)]
_ = s[0:] _ = s[0:]
_ = s[1:10] _ = s[1:10]
@ -29,6 +33,7 @@ var (
_ = s[3:(len(s))] _ = s[3:(len(s))]
_ = s[len(a) : len(s)-1] _ = s[len(a) : len(s)-1]
_ = s[0:len(b)] _ = s[0:len(b)]
_ = s[2:len(s):len(s)]
_ = s[:] _ = s[:]
_ = s[:10] _ = s[:10]
@ -36,6 +41,7 @@ var (
_ = s[:(len(s))] _ = s[:(len(s))]
_ = s[:len(s)-1] _ = s[:len(s)-1]
_ = s[:len(b)] _ = s[:len(b)]
_ = s[:len(s):len(s)]
_ = t.s[0:] _ = t.s[0:]
_ = t.s[1:10] _ = t.s[1:10]
@ -43,6 +49,7 @@ var (
_ = t.s[3:(len(t.s))] _ = t.s[3:(len(t.s))]
_ = t.s[len(a) : len(t.s)-1] _ = t.s[len(a) : len(t.s)-1]
_ = t.s[0:len(b)] _ = t.s[0:len(b)]
_ = t.s[2:len(t.s):len(t.s)]
_ = t.s[:] _ = t.s[:]
_ = t.s[:10] _ = t.s[:10]
@ -50,6 +57,7 @@ var (
_ = t.s[:(len(t.s))] _ = t.s[:(len(t.s))]
_ = t.s[:len(t.s)-1] _ = t.s[:len(t.s)-1]
_ = t.s[:len(b)] _ = t.s[:len(b)]
_ = t.s[:len(t.s):len(t.s)]
) )
func _() { func _() {

View File

@ -1,3 +1,5 @@
//gofmt -s
// Test cases for slice expression simplification. // Test cases for slice expression simplification.
// Because of a dot import, these slices must remain untouched. // Because of a dot import, these slices must remain untouched.
package p package p

Some files were not shown because too many files have changed in this diff Show More