summaryrefslogtreecommitdiff
path: root/misc
diff options
context:
space:
mode:
authorOndřej Surý <ondrej@sury.org>2011-01-17 12:40:45 +0100
committerOndřej Surý <ondrej@sury.org>2011-01-17 12:40:45 +0100
commit3e45412327a2654a77944249962b3652e6142299 (patch)
treebc3bf69452afa055423cbe0c5cfa8ca357df6ccf /misc
parentc533680039762cacbc37db8dc7eed074c3e497be (diff)
downloadgolang-3e45412327a2654a77944249962b3652e6142299.tar.gz
Imported Upstream version 2011.01.12upstream/2011.01.12
Diffstat (limited to 'misc')
-rwxr-xr-xmisc/arm/a23
-rw-r--r--misc/bash/go2
-rwxr-xr-xmisc/bbedit/Go.plist11
-rw-r--r--misc/cgo/gmp/Makefile2
-rw-r--r--misc/cgo/life/Makefile20
-rw-r--r--misc/cgo/life/c-life.c2
-rw-r--r--misc/cgo/life/golden.out17
-rw-r--r--misc/cgo/life/life.go2
-rw-r--r--misc/cgo/life/life.h1
-rw-r--r--misc/cgo/life/main.go2
-rwxr-xr-xmisc/cgo/life/test.bash11
-rw-r--r--misc/cgo/stdio/Makefile11
-rw-r--r--misc/cgo/stdio/align.go78
-rw-r--r--misc/cgo/stdio/chain.go4
-rw-r--r--misc/cgo/stdio/fib.go2
-rw-r--r--misc/cgo/stdio/file.go28
-rw-r--r--misc/cgo/stdio/hello.go23
-rwxr-xr-xmisc/cgo/stdio/test.bash5
-rw-r--r--misc/cgo/stdio/test.go144
-rw-r--r--misc/cgo/stdio/test1.go29
-rw-r--r--misc/dashboard/README14
-rw-r--r--misc/dashboard/buildcontrol.py41
-rw-r--r--misc/dashboard/buildcron.sh4
-rw-r--r--misc/dashboard/builder.sh25
-rw-r--r--misc/dashboard/builder/Makefile14
-rw-r--r--misc/dashboard/builder/doc.go54
-rw-r--r--misc/dashboard/builder/exec.go65
-rw-r--r--misc/dashboard/builder/hg.go54
-rw-r--r--misc/dashboard/builder/http.go70
-rw-r--r--misc/dashboard/builder/main.go340
-rw-r--r--misc/dashboard/godashboard/package.py184
-rw-r--r--misc/dashboard/godashboard/project-edit.html10
-rw-r--r--misc/dashboard/godashboard/project-notify.txt2
-rw-r--r--misc/dashboard/godashboard/toutf8.py14
-rwxr-xr-xmisc/dashboard/googlecode_upload.py248
-rw-r--r--misc/emacs/go-mode.el6
-rw-r--r--misc/fraise/go.plist93
-rw-r--r--misc/fraise/readme.txt16
-rw-r--r--misc/goplay/Makefile13
-rw-r--r--misc/goplay/README1
-rw-r--r--misc/goplay/doc.go25
-rw-r--r--misc/goplay/goplay.go314
-rw-r--r--misc/kate/go.xml2
-rw-r--r--misc/vim/syntax/go.vim10
-rw-r--r--misc/zsh/go14
45 files changed, 1922 insertions, 128 deletions
diff --git a/misc/arm/a b/misc/arm/a
index 140b47e29..701f4941f 100755
--- a/misc/arm/a
+++ b/misc/arm/a
@@ -29,7 +29,7 @@ exp ()
rloc=/data/local/tmp/retval
rsize=$(adb shell "ls -l $rloc"|tr -s ' '|cut -d' ' -f4)
rcheck=38
-if [ $rsize != $rcheck ] ; then
+if [ "$rsize" != "$rcheck" ]; then
# echo "debug: retval size incorrect want $rcheck, got $rsize. uploading"
echo >/tmp/adb.retval '#!/system/bin/sh
"$@"
@@ -39,11 +39,20 @@ echo RETVAL: $?'
fi
# run the main binary
-if [ "$*" != "$1" ]; then
- args=$(echo $*| cut -d' ' -f2-)
+if [ "-g" == "$1" ]; then
+ adb forward tcp:$2 tcp:$2
+ args=$(echo $*| cut -d' ' -f4-)
+ adb push $3 /data/local/tmp/$3 >/dev/null 2>&1
+ adb shell "$(exp GOARCH) $(exp GOTRACEBACK) $(exp GOGC) \
+ gdbserver :$2 /data/local/tmp/retval /data/local/tmp/$3 $args" \
+ 2>&1|tr -d '\r' |tee /tmp/adb.out|grep -v RETVAL
+else
+ if [ "$*" != "$1" ]; then
+ args=$(echo $*| cut -d' ' -f2-)
+ fi
+ adb push $1 /data/local/tmp/$1 >/dev/null 2>&1
+ adb shell "$(exp GOARCH) $(exp GOTRACEBACK) $(exp GOGC) \
+ /data/local/tmp/retval /data/local/tmp/$1 $args" \
+ 2>&1|tr -d '\r' |tee /tmp/adb.out|grep -v RETVAL
fi
-adb push $1 /data/local/tmp/$1 >/dev/null 2>&1
-adb shell "$(exp GOARCH) $(exp GOTRACEBACK) $(exp GOGC) \
- /data/local/tmp/retval /data/local/tmp/$1 $args" \
- 2>&1|tr -d '\r' |tee /tmp/adb.out|grep -v RETVAL
exit $(grep RETVAL /tmp/adb.out|tr -d '\n\r'| cut -d' ' -f2)
diff --git a/misc/bash/go b/misc/bash/go
index 711020ac9..caced154f 100644
--- a/misc/bash/go
+++ b/misc/bash/go
@@ -3,4 +3,4 @@
complete -f -X '!*.8' 8l
complete -f -X '!*.6' 6l
complete -f -X '!*.5' 5l
-complete -f -X '!*.go' 8g 6g 5g
+complete -f -X '!*.go' 8g 6g 5g gofmt gccgo
diff --git a/misc/bbedit/Go.plist b/misc/bbedit/Go.plist
index 71bb9bc5e..39c8f0dc3 100755
--- a/misc/bbedit/Go.plist
+++ b/misc/bbedit/Go.plist
@@ -6,15 +6,22 @@
BBLMColorsSyntax = YES;
BBLMIsCaseSensitive = YES;
BBLMKeywordList = (
+ append,
bool,
break,
byte,
cap,
case,
chan,
+ close,
+ closed,
cmplx,
+ complex,
+ complex128,
+ complex64,
const,
continue,
+ copy,
default,
defer,
else,
@@ -43,8 +50,12 @@
new,
nil,
package,
+ panic,
+ print,
+ println,
range,
real,
+ recover,
return,
select,
string,
diff --git a/misc/cgo/gmp/Makefile b/misc/cgo/gmp/Makefile
index ad5db33c2..fc6209f27 100644
--- a/misc/cgo/gmp/Makefile
+++ b/misc/cgo/gmp/Makefile
@@ -2,7 +2,7 @@
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
-include ../../../src/Make.$(GOARCH)
+include ../../../src/Make.inc
TARG=gmp
diff --git a/misc/cgo/life/Makefile b/misc/cgo/life/Makefile
index cbcdc9927..5a10380ed 100644
--- a/misc/cgo/life/Makefile
+++ b/misc/cgo/life/Makefile
@@ -2,30 +2,20 @@
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
-include ../../../src/Make.$(GOARCH)
+include ../../../src/Make.inc
TARG=life
CGOFILES=\
- life.go
+ life.go\
-LDPATH_freebsd=-Wl,-R,`pwd`
-LDPATH_linux=-Wl,-R,`pwd`
-LDPATH_darwin=
+CGO_OFILES=\
+ c-life.o\
-CGO_LDFLAGS=_cgo_export.o c-life.so $(LDPATH_$(GOOS))
-CGO_DEPS=_cgo_export.o c-life.so
-
-CLEANFILES += life
+CLEANFILES+=life
include ../../../src/Make.pkg
-c-life.o: c-life.c _cgo_export.h
- gcc $(_CGO_CFLAGS_$(GOARCH)) -g -c -fPIC $(CFLAGS) c-life.c
-
-c-life.so: c-life.o
- gcc $(_CGO_CFLAGS_$(GOARCH)) -o $@ c-life.o $(_CGO_LDFLAGS_$(GOOS))
-
life: install main.go
$(GC) main.go
$(LD) -o $@ main.$O
diff --git a/misc/cgo/life/c-life.c b/misc/cgo/life/c-life.c
index 71555a9c7..657245595 100644
--- a/misc/cgo/life/c-life.c
+++ b/misc/cgo/life/c-life.c
@@ -6,6 +6,8 @@
#include "life.h"
#include "_cgo_export.h"
+const int MYCONST = 0;
+
// Do the actual manipulation of the life board in C. This could be
// done easily in Go, we are just using C for demonstration
// purposes.
diff --git a/misc/cgo/life/golden.out b/misc/cgo/life/golden.out
new file mode 100644
index 000000000..539d2106d
--- /dev/null
+++ b/misc/cgo/life/golden.out
@@ -0,0 +1,17 @@
+* life
+
+
+ XXX XXX
+
+
+
+
+
+
+
+ XXX XXX
+
+
+
+
+
diff --git a/misc/cgo/life/life.go b/misc/cgo/life/life.go
index 036802853..ec000ce3a 100644
--- a/misc/cgo/life/life.go
+++ b/misc/cgo/life/life.go
@@ -23,7 +23,7 @@ var chans [4]chan bool
//export GoStart
// Double return value is just for testing.
func GoStart(i, xdim, ydim, xstart, xend, ystart, yend C.int, a *C.int, n *C.int) (int, int) {
- c := make(chan bool)
+ c := make(chan bool, int(C.MYCONST))
go func() {
C.DoStep(xdim, ydim, xstart, xend, ystart, yend, a, n)
c <- true
diff --git a/misc/cgo/life/life.h b/misc/cgo/life/life.h
index b6e94cf1d..b2011b25f 100644
--- a/misc/cgo/life/life.h
+++ b/misc/cgo/life/life.h
@@ -4,3 +4,4 @@
extern void Step(int, int, int *, int *);
extern void DoStep(int, int, int, int, int, int, int *, int *);
+extern const int MYCONST;
diff --git a/misc/cgo/life/main.go b/misc/cgo/life/main.go
index 7c2c0c73e..9cfed434b 100644
--- a/misc/cgo/life/main.go
+++ b/misc/cgo/life/main.go
@@ -29,7 +29,7 @@ func main() {
}
}
- life.Run(*gen, *dim, *dim, &a)
+ life.Run(*gen, *dim, *dim, a[:])
for i := 0; i < *dim; i++ {
for j := 0; j < *dim; j++ {
diff --git a/misc/cgo/life/test.bash b/misc/cgo/life/test.bash
new file mode 100755
index 000000000..5c5fba1a9
--- /dev/null
+++ b/misc/cgo/life/test.bash
@@ -0,0 +1,11 @@
+#!/bin/sh
+# Copyright 2010 The Go Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+set -e
+gomake life
+echo '*' life >run.out
+./life >>run.out
+diff run.out golden.out
+gomake clean
diff --git a/misc/cgo/stdio/Makefile b/misc/cgo/stdio/Makefile
index 2e3d46631..fc925e607 100644
--- a/misc/cgo/stdio/Makefile
+++ b/misc/cgo/stdio/Makefile
@@ -2,16 +2,19 @@
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
-include ../../../src/Make.$(GOARCH)
+include ../../../src/Make.inc
TARG=stdio
CGOFILES=\
- file.go
+ align.go\
+ file.go\
+ test.go\
+ test1.go\
CLEANFILES+=hello fib chain run.out
include ../../../src/Make.pkg
%: install %.go
- $(QUOTED_GOBIN)/$(GC) $*.go
- $(QUOTED_GOBIN)/$(LD) -o $@ $*.$O
+ $(GC) $*.go
+ $(LD) -o $@ $*.$O
diff --git a/misc/cgo/stdio/align.go b/misc/cgo/stdio/align.go
new file mode 100644
index 000000000..6cdfd902f
--- /dev/null
+++ b/misc/cgo/stdio/align.go
@@ -0,0 +1,78 @@
+package stdio
+
+/*
+#include <stdio.h>
+
+typedef unsigned char Uint8;
+typedef unsigned short Uint16;
+
+typedef enum {
+ MOD1 = 0x0000,
+ MODX = 0x8000
+} SDLMod;
+
+typedef enum {
+ A = 1,
+ B = 322,
+ SDLK_LAST
+} SDLKey;
+
+typedef struct SDL_keysym {
+ Uint8 scancode;
+ SDLKey sym;
+ SDLMod mod;
+ Uint16 unicode;
+} SDL_keysym;
+
+typedef struct SDL_KeyboardEvent {
+ Uint8 typ;
+ Uint8 which;
+ Uint8 state;
+ SDL_keysym keysym;
+} SDL_KeyboardEvent;
+
+void makeEvent(SDL_KeyboardEvent *event) {
+ unsigned char *p;
+ int i;
+
+ p = (unsigned char*)event;
+ for (i=0; i<sizeof *event; i++) {
+ p[i] = i;
+ }
+}
+
+int same(SDL_KeyboardEvent* e, Uint8 typ, Uint8 which, Uint8 state, Uint8 scan, SDLKey sym, SDLMod mod, Uint16 uni) {
+ return e->typ == typ && e->which == which && e->state == state && e->keysym.scancode == scan && e->keysym.sym == sym && e->keysym.mod == mod && e->keysym.unicode == uni;
+}
+
+void cTest(SDL_KeyboardEvent *event) {
+ printf("C: %#x %#x %#x %#x %#x %#x %#x\n", event->typ, event->which, event->state,
+ event->keysym.scancode, event->keysym.sym, event->keysym.mod, event->keysym.unicode);
+ fflush(stdout);
+}
+
+*/
+import "C"
+
+import (
+ "fmt"
+ "syscall"
+)
+
+func TestAlign() {
+ if syscall.ARCH == "amd64" {
+ // alignment is known to be broken on amd64.
+ // http://code.google.com/p/go/issues/detail?id=609
+ return
+ }
+ var evt C.SDL_KeyboardEvent
+ C.makeEvent(&evt)
+ if C.same(&evt, evt.typ, evt.which, evt.state, evt.keysym.scancode, evt.keysym.sym, evt.keysym.mod, evt.keysym.unicode) == 0 {
+ fmt.Println("*** bad alignment")
+ C.cTest(&evt)
+ fmt.Printf("Go: %#x %#x %#x %#x %#x %#x %#x\n",
+ evt.typ, evt.which, evt.state, evt.keysym.scancode,
+ evt.keysym.sym, evt.keysym.mod, evt.keysym.unicode)
+ fmt.Println(evt)
+ }
+}
diff --git a/misc/cgo/stdio/chain.go b/misc/cgo/stdio/chain.go
index dd5e01542..c2b105072 100644
--- a/misc/cgo/stdio/chain.go
+++ b/misc/cgo/stdio/chain.go
@@ -22,7 +22,7 @@ func link(left chan<- int, right <-chan int) {
runtime.LockOSThread()
for {
v := <-right
- stdio.Puts(strconv.Itoa(v))
+ stdio.Stdout.WriteString(strconv.Itoa(v) + "\n")
left <- 1+v
}
}
@@ -38,6 +38,6 @@ func main() {
for i := 0; i < R; i++ {
right <- 0
x := <-leftmost
- stdio.Puts(strconv.Itoa(x))
+ stdio.Stdout.WriteString(strconv.Itoa(x) + "\n")
}
}
diff --git a/misc/cgo/stdio/fib.go b/misc/cgo/stdio/fib.go
index 63ae04988..c02e31fd8 100644
--- a/misc/cgo/stdio/fib.go
+++ b/misc/cgo/stdio/fib.go
@@ -26,7 +26,7 @@ func fibber(c, out chan int64, i int64) {
}
for {
j := <-c
- stdio.Puts(strconv.Itoa64(j))
+ stdio.Stdout.WriteString(strconv.Itoa64(j) + "\n")
out <- j
<-out
i += j
diff --git a/misc/cgo/stdio/file.go b/misc/cgo/stdio/file.go
index 7d1f22280..021cbf909 100644
--- a/misc/cgo/stdio/file.go
+++ b/misc/cgo/stdio/file.go
@@ -10,33 +10,35 @@ see ../gmp/gmp.go.
package stdio
-// TODO(rsc): Remove fflushstdout when C.fflush(C.stdout) works in cgo.
-
/*
#include <stdio.h>
#include <stdlib.h>
+#include <sys/stat.h>
+#include <errno.h>
-void fflushstdout(void) { fflush(stdout); }
+char* greeting = "hello, world";
*/
import "C"
import "unsafe"
-/*
type File C.FILE
var Stdout = (*File)(C.stdout)
var Stderr = (*File)(C.stderr)
-func (f *File) WriteString(s string) {
- p := C.CString(s);
- C.fputs(p, (*C.FILE)(f));
- C.free(p);
-}
-*/
+// Test reference to library symbol.
+// Stdout and stderr are too special to be a reliable test.
+var myerr = C.sys_errlist
-func Puts(s string) {
+func (f *File) WriteString(s string) {
p := C.CString(s)
- C.puts(p)
+ C.fputs(p, (*C.FILE)(f))
C.free(unsafe.Pointer(p))
- C.fflushstdout()
+ f.Flush()
+}
+
+func (f *File) Flush() {
+ C.fflush((*C.FILE)(f))
}
+
+var Greeting = C.GoString(C.greeting)
diff --git a/misc/cgo/stdio/hello.go b/misc/cgo/stdio/hello.go
index 47f9de02f..9cb6e6884 100644
--- a/misc/cgo/stdio/hello.go
+++ b/misc/cgo/stdio/hello.go
@@ -4,9 +4,26 @@
package main
-import "stdio"
+import (
+ "os"
+ "stdio"
+)
func main() {
- // stdio.Stdout.WriteString("hello, world\n");
- stdio.Puts("hello, world")
+ stdio.Stdout.WriteString(stdio.Greeting + "\n")
+
+ l := stdio.Atol("123")
+ if l != 123 {
+ println("Atol 123: ", l)
+ panic("bad atol")
+ }
+
+ n, err := stdio.Strtol("asdf", 123)
+ if n != 0 || err != os.EINVAL {
+ println("Strtol: ", n, err)
+ panic("bad atoi2")
+ }
+
+ stdio.TestAlign()
+ stdio.TestEnum()
}
diff --git a/misc/cgo/stdio/test.bash b/misc/cgo/stdio/test.bash
index b8b5f6911..82e3f7b45 100755
--- a/misc/cgo/stdio/test.bash
+++ b/misc/cgo/stdio/test.bash
@@ -4,8 +4,7 @@
# license that can be found in the LICENSE file.
set -e
-GOBIN="${GOBIN:-$HOME/bin}"
-"$GOBIN"/gomake hello fib chain
+gomake hello fib chain
echo '*' hello >run.out
./hello >>run.out
echo '*' fib >>run.out
@@ -13,4 +12,4 @@ echo '*' fib >>run.out
echo '*' chain >>run.out
./chain >>run.out
diff run.out golden.out
-"$GOBIN"/gomake clean
+gomake clean
diff --git a/misc/cgo/stdio/test.go b/misc/cgo/stdio/test.go
new file mode 100644
index 000000000..8f21603ca
--- /dev/null
+++ b/misc/cgo/stdio/test.go
@@ -0,0 +1,144 @@
+// Copyright 2010 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 test cases for cgo.
+
+package stdio
+
+/*
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+#define SHIFT(x, y) ((x)<<(y))
+#define KILO SHIFT(1, 10)
+
+enum E {
+ Enum1 = 1,
+ Enum2 = 2,
+};
+
+typedef unsigned char uuid_t[20];
+
+void uuid_generate(uuid_t x) {
+ x[0] = 0;
+}
+
+struct S {
+ int x;
+};
+
+extern enum E myConstFunc(struct S* const ctx, int const id, struct S **const filter);
+
+enum E myConstFunc(struct S *const ctx, int const id, struct S **const filter) { return 0; }
+
+// issue 1222
+typedef union {
+ long align;
+} xxpthread_mutex_t;
+
+struct ibv_async_event {
+ union {
+ int x;
+ } element;
+};
+
+struct ibv_context {
+ xxpthread_mutex_t mutex;
+};
+*/
+import "C"
+import (
+ "os"
+ "unsafe"
+)
+
+const EINVAL = C.EINVAL /* test #define */
+
+var KILO = C.KILO
+
+func uuidgen() {
+ var uuid C.uuid_t
+ C.uuid_generate(&uuid[0])
+}
+
+func Size(name string) (int64, os.Error) {
+ var st C.struct_stat
+ p := C.CString(name)
+ _, err := C.stat(p, &st)
+ C.free(unsafe.Pointer(p))
+ if err != nil {
+ return 0, err
+ }
+ return int64(C.ulong(st.st_size)), nil
+}
+
+func Strtol(s string, base int) (int, os.Error) {
+ p := C.CString(s)
+ n, err := C.strtol(p, nil, C.int(base))
+ C.free(unsafe.Pointer(p))
+ return int(n), err
+}
+
+func Atol(s string) int {
+ p := C.CString(s)
+ n := C.atol(p)
+ C.free(unsafe.Pointer(p))
+ return int(n)
+}
+
+func TestConst() {
+ C.myConstFunc(nil, 0, nil)
+}
+
+func TestEnum() {
+ if C.Enum1 != 1 || C.Enum2 != 2 {
+ println("bad enum", C.Enum1, C.Enum2)
+ }
+}
+
+func TestAtol() {
+ l := Atol("123")
+ if l != 123 {
+ println("Atol 123: ", l)
+ panic("bad atol")
+ }
+}
+
+func TestErrno() {
+ n, err := Strtol("asdf", 123)
+ if n != 0 || err != os.EINVAL {
+ println("Strtol: ", n, err)
+ panic("bad strtol")
+ }
+}
+
+func TestMultipleAssign() {
+ p := C.CString("123")
+ n, m := C.strtol(p, nil, 345), C.strtol(p, nil, 10)
+ if n != 0 || m != 234 {
+ println("Strtol x2: ", n, m)
+ panic("bad strtol x2")
+ }
+ C.free(unsafe.Pointer(p))
+}
+
+var (
+ uint = (C.uint)(0)
+ ulong C.ulong
+ char C.char
+)
+
+type Context struct {
+ ctx *C.struct_ibv_context
+}
+
+func Test() {
+ TestAlign()
+ TestAtol()
+ TestEnum()
+ TestErrno()
+ TestConst()
+}
diff --git a/misc/cgo/stdio/test1.go b/misc/cgo/stdio/test1.go
new file mode 100644
index 000000000..dce2ef83c
--- /dev/null
+++ b/misc/cgo/stdio/test1.go
@@ -0,0 +1,29 @@
+// Copyright 2010 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 test cases for cgo.
+
+package stdio
+
+/*
+// issue 1222
+typedef union {
+ long align;
+} xxpthread_mutex_t;
+
+struct ibv_async_event {
+ union {
+ int x;
+ } element;
+};
+
+struct ibv_context {
+ xxpthread_mutex_t mutex;
+};
+*/
+import "C"
+
+type AsyncEvent struct {
+ event C.struct_ibv_async_event
+}
diff --git a/misc/dashboard/README b/misc/dashboard/README
index b2bc3c2d3..72d5546a4 100644
--- a/misc/dashboard/README
+++ b/misc/dashboard/README
@@ -24,11 +24,19 @@ export GOARCH=XXX
export GOOS=XXX
export GOBIN=/gobuild/bin
export PATH=$PATH:/gobuild/bin
-export BUILDER=XXX
+export BUILDER=$GOOS-$GOARCH
export BUILDHOST=godashboard.appspot.com
-* Write the key ~gobuild/.gobuildkey (you need to get it from someone who knows
- the key)
+* Write the key ~gobuild/.gobuildkey
+ You need to get it from someone who knows the key.
+ You may also use a filename of the form .gobuildkey-$BUILDER if you
+ wish to run builders for multiple targets.
+
+* Append your username and password googlecode.com credentials from
+ https://code.google.com/hosting/settings
+ to the buildkey file in the format "Username\nPassword\n".
+ (This is for uploading tarballs to the project downloads section,
+ and is an optional step.)
* sudo apt-get install bison gcc libc6-dev ed make
* cd ~gobuild
diff --git a/misc/dashboard/buildcontrol.py b/misc/dashboard/buildcontrol.py
index 91b684f79..ec503e7ff 100644
--- a/misc/dashboard/buildcontrol.py
+++ b/misc/dashboard/buildcontrol.py
@@ -18,6 +18,8 @@ buildhost = ''
buildport = -1
buildkey = ''
+upload_project = "go"
+
def main(args):
global buildport, buildhost, buildkey
@@ -35,14 +37,23 @@ def main(args):
buildport = int(os.environ['BUILDPORT'])
try:
- buildkey = file('%s/.gobuildkey-%s' % (os.environ['HOME'], os.environ['BUILDER']), 'r').read().strip()
+ buildkeyfile = file('%s/.gobuildkey-%s' % (os.environ['HOME'], os.environ['BUILDER']), 'r')
+ buildkey = buildkeyfile.readline().strip()
except IOError:
try:
- buildkey = file('%s/.gobuildkey' % os.environ['HOME'], 'r').read().strip()
+ buildkeyfile = file('%s/.gobuildkey' % os.environ['HOME'], 'r')
+ buildkey = buildkeyfile.readline().strip()
except IOError:
print >>sys.stderr, "Need key in ~/.gobuildkey-%s or ~/.gobuildkey" % os.environ['BUILDER']
return
+ # get upload credentials
+ try:
+ username = buildkeyfile.readline().strip()
+ password = buildkeyfile.readline().strip()
+ except:
+ username, password = None, None
+
if args[1] == 'init':
return doInit(args)
elif args[1] == 'hwget':
@@ -55,6 +66,8 @@ def main(args):
return doRecord(args)
elif args[1] == 'benchmarks':
return doBenchmarks(args)
+ elif args[1] == 'upload':
+ return doUpload(args, username, password)
else:
return usage(args[0])
@@ -68,6 +81,7 @@ Commands:
next <builder>: get the next revision number to by built by the given builder
record <builder> <rev> <ok|log file>: record a build result
benchmarks <builder> <rev> <log file>: record benchmark numbers
+ upload <builder> <summary> <tar file>: upload tarball to googlecode
''' % name)
return 1
@@ -165,6 +179,29 @@ def doBenchmarks(args):
e.append(b)
return command('benchmarks', {'node': c.node, 'builder': builder, 'benchmarkdata': binascii.b2a_base64(''.join(e))})
+def doUpload(args, username, password):
+ # fail gracefully if no username or password set
+ if not username or not password:
+ return
+
+ if len(args) != 5:
+ return usage(args[0])
+ builder = args[2]
+ summary = args[3]
+ filename = args[4]
+
+ from googlecode_upload import upload
+ code, msg, url = upload(
+ filename, # filename
+ upload_project, # 'go'
+ username,
+ password,
+ summary,
+ builder.split('-'), # labels
+ )
+ if code != 201:
+ raise Failed('Upload returned code %s msg "%s".' % (code, msg))
+
def encodeMultipartFormdata(fields, files):
"""fields is a sequence of (name, value) elements for regular form fields.
files is a sequence of (name, filename, value) elements for data to be uploaded as files"""
diff --git a/misc/dashboard/buildcron.sh b/misc/dashboard/buildcron.sh
index 5f4300796..7aa70ce57 100644
--- a/misc/dashboard/buildcron.sh
+++ b/misc/dashboard/buildcron.sh
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/usr/bin/env bash
# Copyright 2010 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.
@@ -48,7 +48,7 @@ fi
mkdir -p $GOROOT/bin
cd $GOROOT/..
-cp go/misc/dashboard/builder.sh go/misc/dashboard/buildcontrol.py .
+cp go/misc/dashboard/{builder.sh,buildcontrol.py,googlecode_upload.py} .
chmod a+x builder.sh buildcontrol.py
cd go
../buildcontrol.py next $BUILDER
diff --git a/misc/dashboard/builder.sh b/misc/dashboard/builder.sh
index b302acec2..4a8d117bf 100644
--- a/misc/dashboard/builder.sh
+++ b/misc/dashboard/builder.sh
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/usr/bin/env bash
# Copyright 2009 The Go Authors. All rights reserved.
# Use of this source code is governed by a BSD-style
@@ -31,8 +31,9 @@ fi
export PATH=$PATH:`pwd`/candidate/bin
export GOBIN=`pwd`/candidate/bin
+export GOROOT_FINAL=/usr/local/go
-while true ; do
+while true ; do (
cd go || fatal "Cannot cd into 'go'"
hg pull -u || fatal "hg sync failed"
rev=`python ../buildcontrol.py next $BUILDER`
@@ -72,7 +73,23 @@ while true ; do
python ../../../buildcontrol.py benchmarks $BUILDER $rev ../../benchmarks || fatal "Cannot record benchmarks"
cd .. || fatal "failed to cd out of pkg"
fi
+ # check if we're at a release (via the hg summary)
+ # if so, package the tar.gz and upload to googlecode
+ SUMMARY=$(hg log -l 1 | grep summary\: | awk '{print $2}')
+ if [[ "x${SUMMARY:0:7}" == "xrelease" ]]; then
+ echo "Uploading binary to googlecode"
+ TARBALL="go.$SUMMARY.$BUILDER.tar.gz"
+ ./clean.bash --nopkg
+ # move contents of candidate/ to candidate/go/ for archival
+ cd ../.. || fatal "Cannot cd up"
+ mv candidate go-candidate || fatal "Cannot rename candidate"
+ mkdir candidate || fatal "Cannot mkdir candidate"
+ mv go-candidate candidate/go || fatal "Cannot mv directory"
+ cd candidate || fatal "Cannot cd candidate"
+ # build tarball
+ tar czf ../$TARBALL go || fatal "Cannot create tarball"
+ ../buildcontrol.py upload $BUILDER $SUMMARY ../$TARBALL || fatal "Cannot upload tarball"
+ fi
fi
- cd ../.. || fatal "Cannot cd up"
sleep 10
-done
+) done
diff --git a/misc/dashboard/builder/Makefile b/misc/dashboard/builder/Makefile
new file mode 100644
index 000000000..7270a3f42
--- /dev/null
+++ b/misc/dashboard/builder/Makefile
@@ -0,0 +1,14 @@
+# Copyright 2009 The Go Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+include ../../../src/Make.inc
+
+TARG=gobuilder
+GOFILES=\
+ exec.go\
+ hg.go\
+ http.go\
+ main.go\
+
+include ../../../src/Make.cmd
diff --git a/misc/dashboard/builder/doc.go b/misc/dashboard/builder/doc.go
new file mode 100644
index 000000000..54a9adfc0
--- /dev/null
+++ b/misc/dashboard/builder/doc.go
@@ -0,0 +1,54 @@
+// Copyright 2010 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.
+
+/*
+
+Go Builder is a continuous build client for the Go project.
+It integrates with the Go Dashboard AppEngine application.
+
+Go Builder is intended to run continuously as a background process.
+
+It periodically pulls updates from the Go Mercurial repository.
+
+When a newer revision is found, Go Builder creates a clone of the repository,
+runs all.bash, and reports build success or failure to the Go Dashboard.
+
+For a successful build, Go Builder will also run benchmarks
+(cd $GOROOT/src/pkg; make bench) and send the results to the Go Dashboard.
+
+For a release revision (a change description that matches "release.YYYY-MM-DD"),
+Go Builder will create a tar.gz archive of the GOROOT and deliver it to the
+Go Google Code project's downloads section.
+
+Usage:
+
+ gobuilder goos-goarch...
+
+ Several goos-goarch combinations can be provided, and the builder will
+ build them in serial.
+
+Optional flags:
+
+ -dashboard="godashboard.appspot.com": Go Dashboard Host
+ The location of the Go Dashboard application to which Go Builder will
+ report its results.
+
+ -bench: Run benchmarks
+
+ -release: Build and deliver binary release archive
+
+The key file should be located at $HOME/.gobuilder or, for a builder-specific
+key, $HOME/.gobuilder-$BUILDER (eg, $HOME/.gobuilder-linux-amd64).
+
+The build key file is a text file of the format:
+
+ godashboard-key
+ googlecode-username
+ googlecode-password
+
+If the Google Code credentials are not provided the archival step
+will be skipped.
+
+*/
+package documentation
diff --git a/misc/dashboard/builder/exec.go b/misc/dashboard/builder/exec.go
new file mode 100644
index 000000000..6236c915a
--- /dev/null
+++ b/misc/dashboard/builder/exec.go
@@ -0,0 +1,65 @@
+package main
+
+import (
+ "bytes"
+ "exec"
+ "io"
+ "os"
+ "strings"
+)
+
+// run is a simple wrapper for exec.Run/Close
+func run(envv []string, dir string, argv ...string) os.Error {
+ bin, err := pathLookup(argv[0])
+ if err != nil {
+ return err
+ }
+ p, err := exec.Run(bin, argv, envv, dir,
+ exec.DevNull, exec.DevNull, exec.PassThrough)
+ if err != nil {
+ return err
+ }
+ return p.Close()
+}
+
+// runLog runs a process and returns the combined stdout/stderr,
+// as well as writing it to logfile (if specified).
+func runLog(envv []string, logfile, dir string, argv ...string) (output string, exitStatus int, err os.Error) {
+ bin, err := pathLookup(argv[0])
+ if err != nil {
+ return
+ }
+ p, err := exec.Run(bin, argv, envv, dir,
+ exec.DevNull, exec.Pipe, exec.MergeWithStdout)
+ if err != nil {
+ return
+ }
+ defer p.Close()
+ b := new(bytes.Buffer)
+ var w io.Writer = b
+ if logfile != "" {
+ f, err := os.Open(logfile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
+ if err != nil {
+ return
+ }
+ defer f.Close()
+ w = io.MultiWriter(f, b)
+ }
+ _, err = io.Copy(w, p.Stdout)
+ if err != nil {
+ return
+ }
+ wait, err := p.Wait(0)
+ if err != nil {
+ return
+ }
+ return b.String(), wait.WaitStatus.ExitStatus(), nil
+}
+
+// Find bin in PATH if a relative or absolute path hasn't been specified
+func pathLookup(s string) (string, os.Error) {
+ if strings.HasPrefix(s, "/") || strings.HasPrefix(s, "./") || strings.HasPrefix(s, "../") {
+ return s, nil
+ }
+ return exec.LookPath(s)
+}
diff --git a/misc/dashboard/builder/hg.go b/misc/dashboard/builder/hg.go
new file mode 100644
index 000000000..5d2f63a17
--- /dev/null
+++ b/misc/dashboard/builder/hg.go
@@ -0,0 +1,54 @@
+package main
+
+import (
+ "fmt"
+ "os"
+ "strconv"
+ "strings"
+)
+
+type Commit struct {
+ num int // mercurial revision number
+ node string // mercurial hash
+ parent string // hash of commit's parent
+ user string // author's Name <email>
+ date string // date of commit
+ desc string // description
+}
+
+// getCommit returns details about the Commit specified by the revision hash
+func getCommit(rev string) (c Commit, err os.Error) {
+ defer func() {
+ if err != nil {
+ err = fmt.Errorf("getCommit: %s: %s", rev, err)
+ }
+ }()
+ parts, err := getCommitParts(rev)
+ if err != nil {
+ return
+ }
+ num, err := strconv.Atoi(parts[0])
+ if err != nil {
+ return
+ }
+ parent := ""
+ if num > 0 {
+ prev := strconv.Itoa(num - 1)
+ if pparts, err := getCommitParts(prev); err == nil {
+ parent = pparts[1]
+ }
+ }
+ user := strings.Replace(parts[2], "&lt;", "<", -1)
+ user = strings.Replace(user, "&gt;", ">", -1)
+ return Commit{num, parts[1], parent, user, parts[3], parts[4]}, nil
+}
+
+func getCommitParts(rev string) (parts []string, err os.Error) {
+ const format = "{rev}>{node}>{author|escape}>{date}>{desc}"
+ s, _, err := runLog(nil, "", goroot,
+ "hg", "log", "-r", rev, "-l", "1", "--template", format)
+ if err != nil {
+ return
+ }
+ return strings.Split(s, ">", 5), nil
+}
diff --git a/misc/dashboard/builder/http.go b/misc/dashboard/builder/http.go
new file mode 100644
index 000000000..02f281061
--- /dev/null
+++ b/misc/dashboard/builder/http.go
@@ -0,0 +1,70 @@
+package main
+
+import (
+ "bytes"
+ "encoding/base64"
+ "encoding/binary"
+ "fmt"
+ "http"
+ "os"
+ "regexp"
+)
+
+// getHighWater returns the current highwater revision hash for this builder
+func (b *Builder) getHighWater() (rev string, err os.Error) {
+ url := fmt.Sprintf("http://%s/hw-get?builder=%s", *dashboard, b.name)
+ r, _, err := http.Get(url)
+ if err != nil {
+ return
+ }
+ buf := new(bytes.Buffer)
+ _, err = buf.ReadFrom(r.Body)
+ if err != nil {
+ return
+ }
+ r.Body.Close()
+ return buf.String(), nil
+}
+
+// recordResult sends build results to the dashboard
+func (b *Builder) recordResult(buildLog string, c Commit) os.Error {
+ return httpCommand("build", map[string]string{
+ "builder": b.name,
+ "key": b.key,
+ "node": c.node,
+ "parent": c.parent,
+ "user": c.user,
+ "date": c.date,
+ "desc": c.desc,
+ "log": buildLog,
+ })
+}
+
+// match lines like: "package.BechmarkFunc 100000 999 ns/op"
+var benchmarkRegexp = regexp.MustCompile("([^\n\t ]+)[\t ]+([0-9]+)[\t ]+([0-9]+) ns/op")
+
+// recordBenchmarks sends benchmark results to the dashboard
+func (b *Builder) recordBenchmarks(benchLog string, c Commit) os.Error {
+ results := benchmarkRegexp.FindAllStringSubmatch(benchLog, -1)
+ var buf bytes.Buffer
+ b64 := base64.NewEncoder(base64.StdEncoding, &buf)
+ for _, r := range results {
+ for _, s := range r[1:] {
+ binary.Write(b64, binary.BigEndian, uint16(len(s)))
+ b64.Write([]byte(s))
+ }
+ }
+ b64.Close()
+ return httpCommand("benchmarks", map[string]string{
+ "builder": b.name,
+ "key": b.key,
+ "node": c.node,
+ "benchmarkdata": buf.String(),
+ })
+}
+
+func httpCommand(cmd string, args map[string]string) os.Error {
+ url := fmt.Sprintf("http://%v/%v", *dashboard, cmd)
+ _, err := http.PostForm(url, args)
+ return err
+}
diff --git a/misc/dashboard/builder/main.go b/misc/dashboard/builder/main.go
new file mode 100644
index 000000000..32a2e10da
--- /dev/null
+++ b/misc/dashboard/builder/main.go
@@ -0,0 +1,340 @@
+package main
+
+import (
+ "container/vector"
+ "flag"
+ "fmt"
+ "io/ioutil"
+ "log"
+ "os"
+ "path"
+ "regexp"
+ "strconv"
+ "strings"
+ "time"
+)
+
+const (
+ codeProject = "go"
+ codePyScript = "misc/dashboard/googlecode_upload.py"
+ hgUrl = "https://go.googlecode.com/hg/"
+ waitInterval = 10e9 // time to wait before checking for new revs
+ mkdirPerm = 0750
+)
+
+type Builder struct {
+ name string
+ goos, goarch string
+ key string
+ codeUsername string
+ codePassword string
+}
+
+type BenchRequest struct {
+ builder *Builder
+ commit Commit
+ path string
+}
+
+var (
+ buildroot = flag.String("buildroot", path.Join(os.TempDir(), "gobuilder"), "Directory under which to build")
+ dashboard = flag.String("dashboard", "godashboard.appspot.com", "Go Dashboard Host")
+ runBenchmarks = flag.Bool("bench", false, "Run benchmarks")
+ buildRelease = flag.Bool("release", false, "Build and upload binary release archives")
+ buildRevision = flag.String("rev", "", "Build specified revision and exit")
+ buildCmd = flag.String("cmd", "./all.bash", "Build command (specify absolute or relative to go/src/)")
+)
+
+var (
+ goroot string
+ releaseRegexp = regexp.MustCompile(`^release\.[0-9\-.]+`)
+ benchRequests vector.Vector
+)
+
+func main() {
+ flag.Usage = func() {
+ fmt.Fprintf(os.Stderr, "usage: %s goos-goarch...\n", os.Args[0])
+ flag.PrintDefaults()
+ os.Exit(2)
+ }
+ flag.Parse()
+ if len(flag.Args()) == 0 {
+ flag.Usage()
+ }
+ goroot = path.Join(*buildroot, "goroot")
+ builders := make([]*Builder, len(flag.Args()))
+ for i, builder := range flag.Args() {
+ b, err := NewBuilder(builder)
+ if err != nil {
+ log.Exit(err)
+ }
+ builders[i] = b
+ }
+ if err := os.RemoveAll(*buildroot); err != nil {
+ log.Exitf("Error removing build root (%s): %s", *buildroot, err)
+ }
+ if err := os.Mkdir(*buildroot, mkdirPerm); err != nil {
+ log.Exitf("Error making build root (%s): %s", *buildroot, err)
+ }
+ if err := run(nil, *buildroot, "hg", "clone", hgUrl, goroot); err != nil {
+ log.Exit("Error cloning repository:", err)
+ }
+ // if specified, build revision and return
+ if *buildRevision != "" {
+ c, err := getCommit(*buildRevision)
+ if err != nil {
+ log.Exit("Error finding revision: ", err)
+ }
+ for _, b := range builders {
+ if err := b.buildCommit(c); err != nil {
+ log.Println(err)
+ }
+ runQueuedBenchmark()
+ }
+ return
+ }
+ // check for new commits and build them
+ for {
+ err := run(nil, goroot, "hg", "pull", "-u")
+ if err != nil {
+ log.Println("hg pull failed:", err)
+ time.Sleep(waitInterval)
+ continue
+ }
+ built := false
+ for _, b := range builders {
+ if b.build() {
+ built = true
+ }
+ }
+ // only run benchmarks if we didn't build anything
+ // so that they don't hold up the builder queue
+ if !built {
+ if !runQueuedBenchmark() {
+ // if we have no benchmarks to do, pause
+ time.Sleep(waitInterval)
+ }
+ // after running one benchmark,
+ // continue to find and build new revisions.
+ }
+ }
+}
+
+func runQueuedBenchmark() bool {
+ if benchRequests.Len() == 0 {
+ return false
+ }
+ runBenchmark(benchRequests.Pop().(BenchRequest))
+ return true
+}
+
+func runBenchmark(r BenchRequest) {
+ // run benchmarks and send to dashboard
+ log.Println(r.builder.name, "benchmarking", r.commit.num)
+ defer os.RemoveAll(r.path)
+ pkg := path.Join(r.path, "go", "src", "pkg")
+ bin := path.Join(r.path, "go", "bin")
+ env := []string{
+ "GOOS=" + r.builder.goos,
+ "GOARCH=" + r.builder.goarch,
+ "PATH=" + bin + ":" + os.Getenv("PATH"),
+ }
+ logfile := path.Join(r.path, "bench.log")
+ benchLog, _, err := runLog(env, logfile, pkg, "gomake", "bench")
+ if err != nil {
+ log.Println(r.builder.name, "gomake bench:", err)
+ return
+ }
+ if err = r.builder.recordBenchmarks(benchLog, r.commit); err != nil {
+ log.Println("recordBenchmarks:", err)
+ }
+}
+
+func NewBuilder(builder string) (*Builder, os.Error) {
+ b := &Builder{name: builder}
+
+ // get goos/goarch from builder string
+ s := strings.Split(builder, "-", 3)
+ if len(s) == 2 {
+ b.goos, b.goarch = s[0], s[1]
+ } else {
+ return nil, fmt.Errorf("unsupported builder form: %s", builder)
+ }
+
+ // read keys from keyfile
+ fn := path.Join(os.Getenv("HOME"), ".gobuildkey")
+ if s := fn + "-" + b.name; isFile(s) { // builder-specific file
+ fn = s
+ }
+ c, err := ioutil.ReadFile(fn)
+ if err != nil {
+ return nil, fmt.Errorf("readKeys %s (%s): %s", b.name, fn, err)
+ }
+ v := strings.Split(string(c), "\n", -1)
+ b.key = v[0]
+ if len(v) >= 3 {
+ b.codeUsername, b.codePassword = v[1], v[2]
+ }
+
+ return b, nil
+}
+
+// build checks for a new commit for this builder
+// and builds it if one is found.
+// It returns true if a build was attempted.
+func (b *Builder) build() bool {
+ defer func() {
+ err := recover()
+ if err != nil {
+ log.Println(b.name, "build:", err)
+ }
+ }()
+ c, err := b.nextCommit()
+ if err != nil {
+ log.Println(err)
+ return false
+ }
+ if c == nil {
+ return false
+ }
+ err = b.buildCommit(*c)
+ if err != nil {
+ log.Println(err)
+ }
+ return true
+}
+
+// nextCommit returns the next unbuilt Commit for this builder
+func (b *Builder) nextCommit() (nextC *Commit, err os.Error) {
+ defer func() {
+ if err != nil {
+ err = fmt.Errorf("%s nextCommit: %s", b.name, err)
+ }
+ }()
+ hw, err := b.getHighWater()
+ if err != nil {
+ return
+ }
+ c, err := getCommit(hw)
+ if err != nil {
+ return
+ }
+ next := c.num + 1
+ c, err = getCommit(strconv.Itoa(next))
+ if err == nil && c.num == next {
+ return &c, nil
+ }
+ return nil, nil
+}
+
+func (b *Builder) buildCommit(c Commit) (err os.Error) {
+ defer func() {
+ if err != nil {
+ err = fmt.Errorf("%s buildCommit: %d: %s", b.name, c.num, err)
+ }
+ }()
+
+ log.Println(b.name, "building", c.num)
+
+ // create place in which to do work
+ workpath := path.Join(*buildroot, b.name+"-"+strconv.Itoa(c.num))
+ err = os.Mkdir(workpath, mkdirPerm)
+ if err != nil {
+ return
+ }
+ benchRequested := false
+ defer func() {
+ if !benchRequested {
+ os.RemoveAll(workpath)
+ }
+ }()
+
+ // clone repo
+ err = run(nil, workpath, "hg", "clone", goroot, "go")
+ if err != nil {
+ return
+ }
+
+ // update to specified revision
+ err = run(nil, path.Join(workpath, "go"),
+ "hg", "update", "-r", strconv.Itoa(c.num))
+ if err != nil {
+ return
+ }
+
+ // set up environment for build/bench execution
+ env := []string{
+ "GOOS=" + b.goos,
+ "GOARCH=" + b.goarch,
+ "GOHOSTOS=" + os.Getenv("GOHOSTOS"),
+ "GOHOSTARCH=" + os.Getenv("GOHOSTARCH"),
+ "GOROOT_FINAL=/usr/local/go",
+ "PATH=" + os.Getenv("PATH"),
+ }
+ srcDir := path.Join(workpath, "go", "src")
+
+ // build
+ logfile := path.Join(workpath, "build.log")
+ buildLog, status, err := runLog(env, logfile, srcDir, *buildCmd)
+ if err != nil {
+ return fmt.Errorf("all.bash: %s", err)
+ }
+ if status != 0 {
+ // record failure
+ return b.recordResult(buildLog, c)
+ }
+
+ // record success
+ if err = b.recordResult("", c); err != nil {
+ return fmt.Errorf("recordResult: %s", err)
+ }
+
+ // send benchmark request if benchmarks are enabled
+ if *runBenchmarks {
+ benchRequests.Insert(0, BenchRequest{
+ builder: b,
+ commit: c,
+ path: workpath,
+ })
+ benchRequested = true
+ }
+
+ // finish here if codeUsername and codePassword aren't set
+ if b.codeUsername == "" || b.codePassword == "" || !*buildRelease {
+ return
+ }
+
+ // if this is a release, create tgz and upload to google code
+ if release := releaseRegexp.FindString(c.desc); release != "" {
+ // clean out build state
+ err = run(env, srcDir, "./clean.bash", "--nopkg")
+ if err != nil {
+ return fmt.Errorf("clean.bash: %s", err)
+ }
+ // upload binary release
+ fn := fmt.Sprintf("go.%s.%s-%s.tar.gz", release, b.goos, b.goarch)
+ err = run(nil, workpath, "tar", "czf", fn, "go")
+ if err != nil {
+ return fmt.Errorf("tar: %s", err)
+ }
+ err = run(nil, workpath, path.Join(goroot, codePyScript),
+ "-s", release,
+ "-p", codeProject,
+ "-u", b.codeUsername,
+ "-w", b.codePassword,
+ "-l", fmt.Sprintf("%s,%s", b.goos, b.goarch),
+ fn)
+ }
+
+ return
+}
+
+func isDirectory(name string) bool {
+ s, err := os.Stat(name)
+ return err == nil && s.IsDirectory()
+}
+
+func isFile(name string) bool {
+ s, err := os.Stat(name)
+ return err == nil && (s.IsRegular() || s.IsSymlink())
+}
diff --git a/misc/dashboard/godashboard/package.py b/misc/dashboard/godashboard/package.py
index 6c3bd9995..cf59bf3e8 100644
--- a/misc/dashboard/godashboard/package.py
+++ b/misc/dashboard/godashboard/package.py
@@ -17,6 +17,7 @@ from google.appengine.ext.webapp import template
from google.appengine.ext.webapp.util import run_wsgi_app
from google.appengine.api import users
from google.appengine.api import mail
+from google.appengine.api import urlfetch
import binascii
import datetime
import hashlib
@@ -29,6 +30,11 @@ import time
import urllib2
import sets
+# local imports
+import toutf8
+
+template.register_template_library('toutf8')
+
# Storage model for package info recorded on server.
# Just path, count, and time of last install.
class Package(db.Model):
@@ -50,36 +56,91 @@ re_bitbucket = re.compile(r'^bitbucket\.org/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\-]+$')
re_googlecode = re.compile(r'^[a-z0-9\-]+\.googlecode\.com/(svn|hg)$')
re_github = re.compile(r'^github\.com/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\-]+$')
+def vc_to_web(path):
+ if re_bitbucket.match(path):
+ check_url = 'http://' + path + '/?cmd=heads'
+ web = 'http://' + path + '/'
+ elif re_github.match(path):
+ # github doesn't let you fetch the .git directory anymore.
+ # fetch .git/info/refs instead, like git clone would.
+ check_url = 'http://'+path+'.git/info/refs'
+ web = 'http://' + path
+ elif re_googlecode.match(path):
+ check_url = 'http://'+path
+ web = 'http://code.google.com/p/' + path[:path.index('.')]
+ else:
+ return False, False
+ return web, check_url
+
+re_bitbucket_web = re.compile(r'bitbucket\.org/([a-z0-9A-Z_.\-]+)/([a-z0-9A-Z_.\-]+)')
+re_googlecode_web = re.compile(r'code.google.com/p/([a-z0-9\-]+)')
+re_github_web = re.compile(r'github\.com/([a-z0-9A-Z_.\-]+)/([a-z0-9A-Z_.\-]+)')
+re_striphttp = re.compile(r'http://(www\.)?')
+
+def web_to_vc(url):
+ url = re_striphttp.sub('', url)
+ m = re_bitbucket_web.match(url)
+ if m:
+ return 'bitbucket.org/'+m.group(1)+'/'+m.group(2)
+ m = re_github_web.match(url)
+ if m:
+ return 'github.com/'+m.group(1)+'/'+m.group(2)
+ m = re_googlecode_web.match(url)
+ if m:
+ path = m.group(1)+'.googlecode.com/'
+ # perform http request to path/hg to check if they're using mercurial
+ vcs = 'svn'
+ try:
+ response = urlfetch.fetch('http://'+path+'hg', deadline=1)
+ if response.status_code == 200:
+ vcs = 'hg'
+ except: pass
+ return path + vcs
+ return False
+
MaxPathLength = 100
+CacheTimeout = 3600
class PackagePage(webapp.RequestHandler):
def get(self):
if self.request.get('fmt') == 'json':
return self.json()
- q = Package.all()
- q.order('-last_install')
- by_time = q.fetch(100)
+ html = memcache.get('view-package')
+ if not html:
+ q = Package.all()
+ q.order('-last_install')
+ by_time = q.fetch(100)
- q = Package.all()
- q.order('-count')
- by_count = q.fetch(100)
+ q = Package.all()
+ q.order('-count')
+ by_count = q.fetch(100)
- self.response.headers['Content-Type'] = 'text/html; charset=utf-8'
- path = os.path.join(os.path.dirname(__file__), 'package.html')
- self.response.out.write(template.render(path, {"by_time": by_time, "by_count": by_count}))
+ self.response.headers['Content-Type'] = 'text/html; charset=utf-8'
+ path = os.path.join(os.path.dirname(__file__), 'package.html')
+ html = template.render(
+ path,
+ {"by_time": by_time, "by_count": by_count}
+ )
+ memcache.set('view-package', html, time=CacheTimeout)
+
+ self.response.out.write(html)
def json(self):
- self.response.set_status(200)
- self.response.headers['Content-Type'] = 'text/plain; charset=utf-8'
- q = Package.all()
- s = '{"packages": ['
- sep = ''
- for r in q.fetch(1000):
- s += '%s\n\t{"path": "%s", "last_install": "%s", "count": "%s"}' % (sep, r.path, r.last_install, r.count)
- sep = ','
- s += '\n]}\n'
- self.response.out.write(s)
+ json = memcache.get('view-package-json')
+ if not json:
+ self.response.set_status(200)
+ self.response.headers['Content-Type'] = 'text/plain; charset=utf-8'
+ q = Package.all()
+ s = '{"packages": ['
+ sep = ''
+ for r in q.fetch(1000):
+ s += '%s\n\t{"path": "%s", "last_install": "%s", "count": "%s"}' % (sep, r.path, r.last_install, r.count)
+ sep = ','
+ s += '\n]}\n'
+ json = s
+ memcache.set('view-package-json', json, time=CacheTimeoout)
+ self.response.out.write(json)
def can_get_url(self, url):
try:
@@ -104,18 +165,8 @@ class PackagePage(webapp.RequestHandler):
p = Package.get_by_key_name(key)
if p is None:
# not in datastore - verify URL before creating
- if re_bitbucket.match(path):
- check_url = 'http://' + path + '/?cmd=heads'
- web = 'http://' + path + '/'
- elif re_github.match(path):
- # github doesn't let you fetch the .git directory anymore.
- # fetch .git/info/refs instead, like git clone would.
- check_url = 'http://'+path+'.git/info/refs'
- web = 'http://' + path
- elif re_googlecode.match(path):
- check_url = 'http://'+path
- web = 'http://code.google.com/p/' + path[:path.index('.')]
- else:
+ web, check_url = vc_to_web(path)
+ if not web:
logging.error('unrecognized path: %s', path)
return False
if not self.can_get_url(check_url):
@@ -150,9 +201,27 @@ class ProjectPage(webapp.RequestHandler):
self.redirect(users.create_logout_url("/project"))
elif self.request.path == "/project/edit" and admin:
self.edit()
+ elif self.request.path == "/project/assoc" and admin:
+ self.assoc()
else:
self.list()
+ def assoc(self):
+ projects = Project.all()
+ for p in projects:
+ if p.package:
+ continue
+ path = web_to_vc(p.web_url)
+ if not path:
+ continue
+ pkg = Package.get_by_key_name("pkg-"+path)
+ if not pkg:
+ self.response.out.write('no: %s %s<br>' % (p.web_url, path))
+ continue
+ p.package = pkg
+ p.put()
+ self.response.out.write('yes: %s %s<br>' % (p.web_url, path))
+
def post(self):
if self.request.path == "/project/edit":
self.edit(True)
@@ -177,30 +246,40 @@ class ProjectPage(webapp.RequestHandler):
self.list({"submitMsg": "Your project has been submitted."})
- def list(self, data={}):
- projects = Project.all().order('category').order('name')
-
- admin = users.is_current_user_admin()
- if not admin:
- projects = projects.filter('approved =', True)
-
- projects = list(projects)
-
- tags = sets.Set()
- for p in projects:
- for t in p.tags:
- tags.add(t)
-
- tag = self.request.get("tag", None)
+ def list(self, additional_data={}):
+ cache_key = 'view-project-data'
+ tag = self.request.get('tag', None)
if tag:
- projects = filter(lambda x: tag in x.tags, projects)
+ cache_key += '-'+tag
+ data = memcache.get(cache_key)
+ admin = users.is_current_user_admin()
+ if admin or not data:
+ projects = Project.all().order('category').order('name')
+ if not admin:
+ projects = projects.filter('approved =', True)
+ projects = list(projects)
+
+ tags = sets.Set()
+ for p in projects:
+ for t in p.tags:
+ tags.add(t)
+
+ if tag:
+ projects = filter(lambda x: tag in x.tags, projects)
+
+ data = {}
+ data['tag'] = tag
+ data['tags'] = tags
+ data['projects'] = projects
+ data['admin']= admin
+ if not admin:
+ memcache.set(cache_key, data, time=CacheTimeout)
+
+ for k, v in additional_data.items():
+ data[k] = v
self.response.headers['Content-Type'] = 'text/html; charset=utf-8'
path = os.path.join(os.path.dirname(__file__), 'project.html')
- data["tag"] = tag
- data["tags"] = tags
- data["projects"] = projects
- data["admin"] = admin
self.response.out.write(template.render(path, data))
def edit(self, save=False):
@@ -228,7 +307,8 @@ class ProjectPage(webapp.RequestHandler):
p.approved = self.request.get("approved") == "1"
p.tags = filter(lambda x: x, self.request.get("tags", "").split(","))
p.put()
- self.redirect("/project")
+ memcache.delete('view-project-data')
+ self.redirect('/project')
return
# get all project categories and tags
diff --git a/misc/dashboard/godashboard/project-edit.html b/misc/dashboard/godashboard/project-edit.html
index 5f1ca3b11..ce18fb3fb 100644
--- a/misc/dashboard/godashboard/project-edit.html
+++ b/misc/dashboard/godashboard/project-edit.html
@@ -1,11 +1,11 @@
<html>
<head>
+<link href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/themes/base/jquery-ui.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="http://www.google.com/jsapi"></script>
<script>
google.load("jquery", "1");
+google.load("jqueryui", "1.8.2");
</script>
-<script type="text/javascript" src="/static/jquery.autocomplete.min.js"></script>
-<link rel="stylesheet" type="text/css" href="/static/jquery.autocomplete.css" />
</head>
<body>
<form action="/project/edit?orig_name={{p.name}}" method="POST">
@@ -38,8 +38,10 @@ var cats = [
{% endfor %}
];
-$('#tags').autocomplete(tags);
-$('#cats').autocomplete(cats);
+google.setOnLoadCallback(function() {
+ $('#tags').autocomplete({source:tags});
+ $('#cats').autocomplete({source:cats});
+});
</script>
</body>
</html>
diff --git a/misc/dashboard/godashboard/project-notify.txt b/misc/dashboard/godashboard/project-notify.txt
index 3a165908c..f55bf6421 100644
--- a/misc/dashboard/godashboard/project-notify.txt
+++ b/misc/dashboard/godashboard/project-notify.txt
@@ -5,5 +5,5 @@ Description: {{project.descr}}
URL: {{project.web_url}}
To edit/approve/delete:
-http://godashboard.appspot.com/project/edit?name={{project.name|urlencode}}
+http://godashboard.appspot.com/project/edit?name={{project.name|toutf8|urlencode}}
diff --git a/misc/dashboard/godashboard/toutf8.py b/misc/dashboard/godashboard/toutf8.py
new file mode 100644
index 000000000..544c681b6
--- /dev/null
+++ b/misc/dashboard/godashboard/toutf8.py
@@ -0,0 +1,14 @@
+# Copyright 2010 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 is a Django custom template filter to work around the
+# fact that GAE's urlencode filter doesn't handle unicode strings.
+
+from google.appengine.ext import webapp
+
+register = webapp.template.create_template_register()
+
+@register.filter
+def toutf8(value):
+ return value.encode("utf-8")
diff --git a/misc/dashboard/googlecode_upload.py b/misc/dashboard/googlecode_upload.py
new file mode 100755
index 000000000..3b1d432ff
--- /dev/null
+++ b/misc/dashboard/googlecode_upload.py
@@ -0,0 +1,248 @@
+#!/usr/bin/env python2
+#
+# Copyright 2006, 2007 Google Inc. All Rights Reserved.
+# Author: danderson@google.com (David Anderson)
+#
+# Script for uploading files to a Google Code project.
+#
+# This is intended to be both a useful script for people who want to
+# streamline project uploads and a reference implementation for
+# uploading files to Google Code projects.
+#
+# To upload a file to Google Code, you need to provide a path to the
+# file on your local machine, a small summary of what the file is, a
+# project name, and a valid account that is a member or owner of that
+# project. You can optionally provide a list of labels that apply to
+# the file. The file will be uploaded under the same name that it has
+# in your local filesystem (that is, the "basename" or last path
+# component). Run the script with '--help' to get the exact syntax
+# and available options.
+#
+# Note that the upload script requests that you enter your
+# googlecode.com password. This is NOT your Gmail account password!
+# This is the password you use on googlecode.com for committing to
+# Subversion and uploading files. You can find your password by going
+# to http://code.google.com/hosting/settings when logged in with your
+# Gmail account. If you have already committed to your project's
+# Subversion repository, the script will automatically retrieve your
+# credentials from there (unless disabled, see the output of '--help'
+# for details).
+#
+# If you are looking at this script as a reference for implementing
+# your own Google Code file uploader, then you should take a look at
+# the upload() function, which is the meat of the uploader. You
+# basically need to build a multipart/form-data POST request with the
+# right fields and send it to https://PROJECT.googlecode.com/files .
+# Authenticate the request using HTTP Basic authentication, as is
+# shown below.
+#
+# Licensed under the terms of the Apache Software License 2.0:
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Questions, comments, feature requests and patches are most welcome.
+# Please direct all of these to the Google Code users group:
+# http://groups.google.com/group/google-code-hosting
+
+"""Google Code file uploader script.
+"""
+
+__author__ = 'danderson@google.com (David Anderson)'
+
+import httplib
+import os.path
+import optparse
+import getpass
+import base64
+import sys
+
+
+def upload(file, project_name, user_name, password, summary, labels=None):
+ """Upload a file to a Google Code project's file server.
+
+ Args:
+ file: The local path to the file.
+ project_name: The name of your project on Google Code.
+ user_name: Your Google account name.
+ password: The googlecode.com password for your account.
+ Note that this is NOT your global Google Account password!
+ summary: A small description for the file.
+ labels: an optional list of label strings with which to tag the file.
+
+ Returns: a tuple:
+ http_status: 201 if the upload succeeded, something else if an
+ error occured.
+ http_reason: The human-readable string associated with http_status
+ file_url: If the upload succeeded, the URL of the file on Google
+ Code, None otherwise.
+ """
+ # The login is the user part of user@gmail.com. If the login provided
+ # is in the full user@domain form, strip it down.
+ if user_name.endswith('@gmail.com'):
+ user_name = user_name[:user_name.index('@gmail.com')]
+
+ form_fields = [('summary', summary)]
+ if labels is not None:
+ form_fields.extend([('label', l.strip()) for l in labels])
+
+ content_type, body = encode_upload_request(form_fields, file)
+
+ upload_host = '%s.googlecode.com' % project_name
+ upload_uri = '/files'
+ auth_token = base64.b64encode('%s:%s'% (user_name, password))
+ headers = {
+ 'Authorization': 'Basic %s' % auth_token,
+ 'User-Agent': 'Googlecode.com uploader v0.9.4',
+ 'Content-Type': content_type,
+ }
+
+ server = httplib.HTTPSConnection(upload_host)
+ server.request('POST', upload_uri, body, headers)
+ resp = server.getresponse()
+ server.close()
+
+ if resp.status == 201:
+ location = resp.getheader('Location', None)
+ else:
+ location = None
+ return resp.status, resp.reason, location
+
+
+def encode_upload_request(fields, file_path):
+ """Encode the given fields and file into a multipart form body.
+
+ fields is a sequence of (name, value) pairs. file is the path of
+ the file to upload. The file will be uploaded to Google Code with
+ the same file name.
+
+ Returns: (content_type, body) ready for httplib.HTTP instance
+ """
+ BOUNDARY = '----------Googlecode_boundary_reindeer_flotilla'
+ CRLF = '\r\n'
+
+ body = []
+
+ # Add the metadata about the upload first
+ for key, value in fields:
+ body.extend(
+ ['--' + BOUNDARY,
+ 'Content-Disposition: form-data; name="%s"' % key,
+ '',
+ value,
+ ])
+
+ # Now add the file itself
+ file_name = os.path.basename(file_path)
+ f = open(file_path, 'rb')
+ file_content = f.read()
+ f.close()
+
+ body.extend(
+ ['--' + BOUNDARY,
+ 'Content-Disposition: form-data; name="filename"; filename="%s"'
+ % file_name,
+ # The upload server determines the mime-type, no need to set it.
+ 'Content-Type: application/octet-stream',
+ '',
+ file_content,
+ ])
+
+ # Finalize the form body
+ body.extend(['--' + BOUNDARY + '--', ''])
+
+ return 'multipart/form-data; boundary=%s' % BOUNDARY, CRLF.join(body)
+
+
+def upload_find_auth(file_path, project_name, summary, labels=None,
+ user_name=None, password=None, tries=3):
+ """Find credentials and upload a file to a Google Code project's file server.
+
+ file_path, project_name, summary, and labels are passed as-is to upload.
+
+ Args:
+ file_path: The local path to the file.
+ project_name: The name of your project on Google Code.
+ summary: A small description for the file.
+ labels: an optional list of label strings with which to tag the file.
+ config_dir: Path to Subversion configuration directory, 'none', or None.
+ user_name: Your Google account name.
+ tries: How many attempts to make.
+ """
+
+ while tries > 0:
+ if user_name is None:
+ # Read username if not specified or loaded from svn config, or on
+ # subsequent tries.
+ sys.stdout.write('Please enter your googlecode.com username: ')
+ sys.stdout.flush()
+ user_name = sys.stdin.readline().rstrip()
+ if password is None:
+ # Read password if not loaded from svn config, or on subsequent tries.
+ print 'Please enter your googlecode.com password.'
+ print '** Note that this is NOT your Gmail account password! **'
+ print 'It is the password you use to access Subversion repositories,'
+ print 'and can be found here: http://code.google.com/hosting/settings'
+ password = getpass.getpass()
+
+ status, reason, url = upload(file_path, project_name, user_name, password,
+ summary, labels)
+ # Returns 403 Forbidden instead of 401 Unauthorized for bad
+ # credentials as of 2007-07-17.
+ if status in [httplib.FORBIDDEN, httplib.UNAUTHORIZED]:
+ # Rest for another try.
+ user_name = password = None
+ tries = tries - 1
+ else:
+ # We're done.
+ break
+
+ return status, reason, url
+
+
+def main():
+ parser = optparse.OptionParser(usage='googlecode-upload.py -s SUMMARY '
+ '-p PROJECT [options] FILE')
+ parser.add_option('-s', '--summary', dest='summary',
+ help='Short description of the file')
+ parser.add_option('-p', '--project', dest='project',
+ help='Google Code project name')
+ parser.add_option('-u', '--user', dest='user',
+ help='Your Google Code username')
+ parser.add_option('-w', '--password', dest='password',
+ help='Your Google Code password')
+ parser.add_option('-l', '--labels', dest='labels',
+ help='An optional list of comma-separated labels to attach '
+ 'to the file')
+
+ options, args = parser.parse_args()
+
+ if not options.summary:
+ parser.error('File summary is missing.')
+ elif not options.project:
+ parser.error('Project name is missing.')
+ elif len(args) < 1:
+ parser.error('File to upload not provided.')
+ elif len(args) > 1:
+ parser.error('Only one file may be specified.')
+
+ file_path = args[0]
+
+ if options.labels:
+ labels = options.labels.split(',')
+ else:
+ labels = None
+
+ status, reason, url = upload_find_auth(file_path, options.project,
+ options.summary, labels,
+ options.user, options.password)
+ if url:
+ print 'The file was uploaded successfully.'
+ print 'URL: %s' % url
+ return 0
+ else:
+ print 'An error occurred. Your file was not uploaded.'
+ print 'Google Code upload server said: %s (%s)' % (reason, status)
+ return 1
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/misc/emacs/go-mode.el b/misc/emacs/go-mode.el
index e27ee7438..2624e87cb 100644
--- a/misc/emacs/go-mode.el
+++ b/misc/emacs/go-mode.el
@@ -27,8 +27,8 @@
(defvar go-mode-syntax-table
(let ((st (make-syntax-table)))
- ;; Symbols
- (modify-syntax-entry ?_ "_" st)
+ ;; Add _ to :word: character class
+ (modify-syntax-entry ?_ "w" st)
;; Operators (punctuation)
(modify-syntax-entry ?+ "." st)
@@ -92,7 +92,7 @@ some syntax analysis.")
;; Map key type
(,(concat "\\<map\\s *\\[" type-name) 1 font-lock-type-face)
;; Channel value type
- (,(concat "\\<chan\\s *\\(?:<-\\)?" type-name) 1 font-lock-type-face)
+ (,(concat "\\<chan\\>\\s *\\(?:<-\\)?" type-name) 1 font-lock-type-face)
;; new/make type
(,(concat "\\<\\(?:new\\|make\\)\\>\\(?:\\s \\|)\\)*(" type-name) 1 font-lock-type-face)
;; Type conversion
diff --git a/misc/fraise/go.plist b/misc/fraise/go.plist
new file mode 100644
index 000000000..298361501
--- /dev/null
+++ b/misc/fraise/go.plist
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>beginCommand</key>
+ <string></string>
+ <key>endCommand</key>
+ <string></string>
+ <key>beginInstruction</key>
+ <string></string>
+ <key>endInstruction</key>
+ <string></string>
+ <key>beginVariable</key>
+ <string></string>
+ <key>endVariable</key>
+ <string></string>
+ <key>firstString</key>
+ <string>&quot;</string>
+ <key>secondString</key>
+ <string>&apos;</string>
+ <key>firstSingleLineComment</key>
+ <string>//</string>
+ <key>secondSingleLineComment</key>
+ <string></string>
+ <key>beginFirstMultiLineComment</key>
+ <string>/*</string>
+ <key>endFirstMultiLineComment</key>
+ <string>*/</string>
+ <key>beginSecondMultiLineComment</key>
+ <string></string>
+ <key>endSecondMultiLineComment</key>
+ <string></string>
+ <key>functionDefinition</key>
+ <string>^func\s*.*\(.*\)\s?\{</string>
+ <key>removeFromFunction</key>
+ <string></string>
+ <key>keywordsCaseSensitive</key>
+ <true/>
+ <key>recolourKeywordIfAlreadyColoured</key>
+ <true/>
+ <key>keywords</key>
+ <array>
+ <string>break</string>
+ <string>case</string>
+ <string>chan</string>
+ <string>const</string>
+ <string>continue</string>
+ <string>default</string>
+ <string>defer</string>
+ <string>else</string>
+ <string>fallthrough</string>
+ <string>for</string>
+ <string>func</string>
+ <string>go</string>
+ <string>goto</string>
+ <string>if</string>
+ <string>import</string>
+ <string>interface</string>
+ <string>map</string>
+ <string>package</string>
+ <string>range</string>
+ <string>return</string>
+ <string>select</string>
+ <string>struct</string>
+ <string>switch</string>
+ <string>type</string>
+ <string>var</string>
+ <string>bool</string>
+ <string>byte</string>
+ <string>chan</string>
+ <string>complex64</string>
+ <string>complex128</string>
+ <string>float</string>
+ <string>float32</string>
+ <string>float64</string>
+ <string>int</string>
+ <string>int8</string>
+ <string>int16</string>
+ <string>int32</string>
+ <string>int64</string>
+ <string>map</string>
+ <string>string</string>
+ <string>uint</string>
+ <string>uintptr</string>
+ <string>uint8</string>
+ <string>uint16</string>
+ <string>uint32</string>
+ <string>uint64</string>
+ </array>
+ <key>autocompleteWords</key>
+ <array/>
+</dict>
+</plist>
diff --git a/misc/fraise/readme.txt b/misc/fraise/readme.txt
new file mode 100644
index 000000000..fb0f2c8c1
--- /dev/null
+++ b/misc/fraise/readme.txt
@@ -0,0 +1,16 @@
+##Instructions for enabling Go syntax highlighting in Fraise.app##
+1. Move go.plist to /Applications/Fraise.app/Contents/Resources/Syntax\ Definitions/
+2. Open /Applications/Fraise.app/Contents/Resources/SyntaxDefinitions.plist and add
+
+ <dict>
+ <key>name</key>
+ <string>GoogleGo</string>
+ <key>file</key>
+ <string>go</string>
+ <key>extensions</key>
+ <string>go</string>
+ </dict>
+
+before </array>
+
+3. Restart Fraise and you're good to Go! \ No newline at end of file
diff --git a/misc/goplay/Makefile b/misc/goplay/Makefile
new file mode 100644
index 000000000..28d024511
--- /dev/null
+++ b/misc/goplay/Makefile
@@ -0,0 +1,13 @@
+# Copyright 2010 The Go Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+include ../../src/Make.inc
+
+TARG=goplay
+
+GOFILES=\
+ goplay.go\
+
+include ../../src/Make.cmd
+
diff --git a/misc/goplay/README b/misc/goplay/README
new file mode 100644
index 000000000..e8a1d290f
--- /dev/null
+++ b/misc/goplay/README
@@ -0,0 +1 @@
+See doc.go.
diff --git a/misc/goplay/doc.go b/misc/goplay/doc.go
new file mode 100644
index 000000000..9685551bd
--- /dev/null
+++ b/misc/goplay/doc.go
@@ -0,0 +1,25 @@
+// Copyright 2010 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.
+
+// Goplay is a web interface for experimenting with Go code.
+// It is similar to the Go Playground: http://golang.org/doc/play/
+//
+// To use goplay, first build and install it:
+// $ cd $GOROOT/misc/goplay
+// $ gomake install
+// Then, run it:
+// $ goplay
+// and load http://localhost:3999/ in a web browser.
+//
+// You should see a Hello World program, which you can compile and run by
+// pressing shift-enter. There is also a "compile-on-keypress" feature that can
+// be enabled by checking a checkbox.
+//
+// WARNING! CUIDADO! ACHTUNG! ATTENZIONE!
+// A note on security: anyone with access to the goplay web interface can run
+// arbitrary code on your computer. Goplay is not a sandbox, and has no other
+// security mechanisms. Do not deploy it in untrusted environments.
+// By default, goplay listens only on localhost. This can be overridden with
+// the -http parameter. Do so at your own risk.
+package documentation
diff --git a/misc/goplay/goplay.go b/misc/goplay/goplay.go
new file mode 100644
index 000000000..5923360f6
--- /dev/null
+++ b/misc/goplay/goplay.go
@@ -0,0 +1,314 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "bytes"
+ "exec"
+ "flag"
+ "http"
+ "io"
+ "io/ioutil"
+ "log"
+ "os"
+ "runtime"
+ "strconv"
+ "template"
+)
+
+var (
+ httpListen = flag.String("http", "127.0.0.1:3999", "host:port to listen on")
+ htmlOutput = flag.Bool("html", false, "render program output as HTML")
+)
+
+var (
+ // a source of numbers, for naming temporary files
+ uniq = make(chan int)
+ // the architecture-identifying character of the tool chain, 5, 6, or 8
+ archChar string
+)
+
+func main() {
+ flag.Parse()
+
+ // set archChar
+ switch runtime.GOARCH {
+ case "arm":
+ archChar = "5"
+ case "amd64":
+ archChar = "6"
+ case "386":
+ archChar = "8"
+ default:
+ log.Exitln("unrecognized GOARCH:", runtime.GOARCH)
+ }
+
+ // source of unique numbers
+ go func() {
+ for i := 0; ; i++ {
+ uniq <- i
+ }
+ }()
+
+ http.HandleFunc("/", FrontPage)
+ http.HandleFunc("/compile", Compile)
+ log.Exit(http.ListenAndServe(*httpListen, nil))
+}
+
+// FrontPage is an HTTP handler that renders the goplay interface.
+// If a filename is supplied in the path component of the URI,
+// its contents will be put in the interface's text area.
+// Otherwise, the default "hello, world" program is displayed.
+func FrontPage(w http.ResponseWriter, req *http.Request) {
+ data, err := ioutil.ReadFile(req.URL.Path[1:])
+ if err != nil {
+ data = helloWorld
+ }
+ frontPage.Execute(data, w)
+}
+
+// Compile is an HTTP handler that reads Go source code from the request,
+// compiles and links the code (returning any errors), runs the program,
+// and sends the program's output as the HTTP response.
+func Compile(w http.ResponseWriter, req *http.Request) {
+ // x is the base name for .go, .6, executable files
+ x := os.TempDir() + "/compile" + strconv.Itoa(<-uniq)
+ src := x + ".go"
+ obj := x + "." + archChar
+ bin := x
+ if runtime.GOOS == "windows" {
+ bin += ".exe"
+ }
+
+ // write request Body to x.go
+ f, err := os.Open(src, os.O_CREAT|os.O_WRONLY|os.O_TRUNC, 0666)
+ if err != nil {
+ error(w, nil, err)
+ return
+ }
+ defer os.Remove(src)
+ defer f.Close()
+ _, err = io.Copy(f, req.Body)
+ if err != nil {
+ error(w, nil, err)
+ return
+ }
+ f.Close()
+
+ // build x.go, creating x.6
+ out, err := run(archChar+"g", "-o", obj, src)
+ defer os.Remove(obj)
+ if err != nil {
+ error(w, out, err)
+ return
+ }
+
+ // link x.6, creating x (the program binary)
+ out, err = run(archChar+"l", "-o", bin, obj)
+ defer os.Remove(bin)
+ if err != nil {
+ error(w, out, err)
+ return
+ }
+
+ // run x
+ out, err = run(bin)
+ if err != nil {
+ error(w, out, err)
+ }
+
+ // write the output of x as the http response
+ if *htmlOutput {
+ w.Write(out)
+ } else {
+ output.Execute(out, w)
+ }
+}
+
+// error writes compile, link, or runtime errors to the HTTP connection.
+// The JavaScript interface uses the 404 status code to identify the error.
+func error(w http.ResponseWriter, out []byte, err os.Error) {
+ w.WriteHeader(404)
+ if out != nil {
+ output.Execute(out, w)
+ } else {
+ output.Execute(err.String(), w)
+ }
+}
+
+// run executes the specified command and returns its output and an error.
+func run(cmd ...string) ([]byte, os.Error) {
+ // find the specified binary
+ bin, err := exec.LookPath(cmd[0])
+ if err != nil {
+ // report binary as well as the error
+ return nil, os.NewError(cmd[0] + ": " + err.String())
+ }
+
+ // run the binary and read its combined stdout and stderr into a buffer
+ p, err := exec.Run(bin, cmd, os.Environ(), "", exec.DevNull, exec.Pipe, exec.MergeWithStdout)
+ if err != nil {
+ return nil, err
+ }
+ var buf bytes.Buffer
+ io.Copy(&buf, p.Stdout)
+ w, err := p.Wait(0)
+ p.Close()
+ if err != nil {
+ return nil, err
+ }
+
+ // set the error return value if the program had a non-zero exit status
+ if !w.Exited() || w.ExitStatus() != 0 {
+ err = os.ErrorString("running " + cmd[0] + ": " + w.String())
+ }
+
+ return buf.Bytes(), err
+}
+
+var frontPage, output *template.Template // HTML templates
+
+func init() {
+ frontPage = template.New(nil)
+ frontPage.SetDelims("«", "»")
+ if err := frontPage.Parse(frontPageText); err != nil {
+ panic(err)
+ }
+ output = template.MustParse(outputText, nil)
+}
+
+var outputText = `<pre>{@|html}</pre>`
+
+var frontPageText = `<!doctype html>
+<html>
+<head>
+<style>
+pre, textarea {
+ font-family: Monaco, 'Courier New', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
+ font-size: 100%;
+}
+.hints {
+ font-size: 0.8em;
+ text-align: right;
+}
+#edit, #output, #errors { width: 100%; text-align: left; }
+#edit { height: 500px; }
+#output { color: #00c; }
+#errors { color: #c00; }
+</style>
+<script>
+
+function insertTabs(n) {
+ // find the selection start and end
+ var cont = document.getElementById("edit");
+ var start = cont.selectionStart;
+ var end = cont.selectionEnd;
+ // split the textarea content into two, and insert n tabs
+ var v = cont.value;
+ var u = v.substr(0, start);
+ for (var i=0; i<n; i++) {
+ u += "\t";
+ }
+ u += v.substr(end);
+ // set revised content
+ cont.value = u;
+ // reset caret position after inserted tabs
+ cont.selectionStart = start+n;
+ cont.selectionEnd = start+n;
+}
+
+function autoindent(el) {
+ var curpos = el.selectionStart;
+ var tabs = 0;
+ while (curpos > 0) {
+ curpos--;
+ if (el.value[curpos] == "\t") {
+ tabs++;
+ } else if (tabs > 0 || el.value[curpos] == "\n") {
+ break;
+ }
+ }
+ setTimeout(function() {
+ insertTabs(tabs);
+ }, 1);
+}
+
+function keyHandler() {
+ var e = window.event;
+ if (e.keyCode == 9) { // tab
+ insertTabs(1);
+ e.preventDefault();
+ return false;
+ }
+ if (e.keyCode == 13) { // enter
+ if (e.shiftKey) { // +shift
+ compile(e.target);
+ e.preventDefault();
+ return false;
+ } else {
+ autoindent(e.target);
+ }
+ }
+ return true;
+}
+
+var xmlreq;
+
+function autocompile() {
+ if(!document.getElementById("autocompile").checked) {
+ return;
+ }
+ compile();
+}
+
+function compile() {
+ var prog = document.getElementById("edit").value;
+ var req = new XMLHttpRequest();
+ xmlreq = req;
+ req.onreadystatechange = compileUpdate;
+ req.open("POST", "/compile", true);
+ req.setRequestHeader("Content-Type", "text/plain; charset=utf-8");
+ req.send(prog);
+}
+
+function compileUpdate() {
+ var req = xmlreq;
+ if(!req || req.readyState != 4) {
+ return;
+ }
+ if(req.status == 200) {
+ document.getElementById("output").innerHTML = req.responseText;
+ document.getElementById("errors").innerHTML = "";
+ } else {
+ document.getElementById("errors").innerHTML = req.responseText;
+ document.getElementById("output").innerHTML = "";
+ }
+}
+</script>
+</head>
+<body>
+<table width="100%"><tr><td width="60%" valign="top">
+<textarea autofocus="true" id="edit" spellcheck="false" onkeydown="keyHandler();" onkeyup="autocompile();">«@|html»</textarea>
+<div class="hints">
+(Shift-Enter to compile and run.)&nbsp;&nbsp;&nbsp;&nbsp;
+<input type="checkbox" id="autocompile" value="checked" /> Compile and run after each keystroke
+</div>
+<td width="3%">
+<td width="27%" align="right" valign="top">
+<div id="output"></div>
+</table>
+<div id="errors"></div>
+</body>
+</html>
+`
+
+var helloWorld = []byte(`package main
+
+import "fmt"
+
+func main() {
+ fmt.Println("hello, world")
+}
+`)
diff --git a/misc/kate/go.xml b/misc/kate/go.xml
index e8728fd84..3a5c39c94 100644
--- a/misc/kate/go.xml
+++ b/misc/kate/go.xml
@@ -42,6 +42,7 @@
<item> bool </item>
<item> byte </item>
<item> chan </item>
+ <item> complex </item>
<item> complex64 </item>
<item> complex128 </item>
<item> float </item>
@@ -62,6 +63,7 @@
<item> uint64 </item>
</list>
<list name="functions">
+ <item> append </item>
<item> cap </item>
<item> close </item>
<item> closed </item>
diff --git a/misc/vim/syntax/go.vim b/misc/vim/syntax/go.vim
index 244503ca3..7adbe8e35 100644
--- a/misc/vim/syntax/go.vim
+++ b/misc/vim/syntax/go.vim
@@ -85,8 +85,8 @@ syn match goType /\<func\>/
syn match goDeclaration /^func\>/
" Predefined functions and values
-syn keyword goBuiltins cap close closed cmplx copy imag len make
-syn keyword goBuiltins new panic panicln print println real
+syn keyword goBuiltins append cap close closed cmplx copy imag len
+syn keyword goBuiltins make new panic print println real recover
syn keyword goConstants iota true false nil
hi def link goBuiltins Keyword
@@ -95,8 +95,8 @@ hi def link goConstants Keyword
" Comments; their contents
syn keyword goTodo contained TODO FIXME XXX BUG
syn cluster goCommentGroup contains=goTodo
-syn region goComment start="/\*" end="\*/" contains=@goCommentGroup
-syn region goComment start="//" end="$" contains=@goCommentGroup
+syn region goComment start="/\*" end="\*/" contains=@goCommentGroup,@Spell
+syn region goComment start="//" end="$" contains=@goCommentGroup,@Spell
hi def link goComment Comment
hi def link goTodo Todo
@@ -136,7 +136,7 @@ syn region goBlock start="{" end="}" transparent fold
syn region goParen start='(' end=')' transparent
" Integers
-syn match goDecimalInt "\<\d\+\>"
+syn match goDecimalInt "\<\d\+\([Ee]\d\+\)\?\>"
syn match goHexadecimalInt "\<0x\x\+\>"
syn match goOctalInt "\<0\o\+\>"
syn match goOctalError "\<0\o*[89]\d*\>"
diff --git a/misc/zsh/go b/misc/zsh/go
new file mode 100644
index 000000000..f17763d93
--- /dev/null
+++ b/misc/zsh/go
@@ -0,0 +1,14 @@
+# install in /etc/zsh/zshrc or your personal .zshrc
+
+# gc
+prefixes=(5 6 8)
+for p in $prefixes; do
+ compctl -g "*.${p}" ${p}l
+ compctl -g "*.go" ${p}g
+done
+
+# standard go tools
+compctl -g "*.go" gofmt
+
+# gccgo
+compctl -g "*.go" gccgo