diff options
Diffstat (limited to 'misc')
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], "<", "<", -1) + user = strings.Replace(user, ">", ">", -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>"</string> + <key>secondString</key> + <string>'</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.) +<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 |