From e2df7f42ace354646b9fbfdf8ef0b222ead6811f Mon Sep 17 00:00:00 2001 From: Andrew Gerrand Date: Thu, 14 Oct 2010 14:06:02 +1100 Subject: [PATCH] misc: add goplay R=rsc, r CC=golang-dev https://golang.org/cl/2473041 --- misc/goplay/Makefile | 13 ++ misc/goplay/README | 1 + misc/goplay/doc.go | 25 ++++ misc/goplay/goplay.go | 305 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 344 insertions(+) create mode 100644 misc/goplay/Makefile create mode 100644 misc/goplay/README create mode 100644 misc/goplay/doc.go create mode 100644 misc/goplay/goplay.go diff --git a/misc/goplay/Makefile b/misc/goplay/Makefile new file mode 100644 index 00000000000..28d0245119a --- /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 00000000000..e8a1d290fdd --- /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 00000000000..9685551bd5f --- /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 00000000000..02148064573 --- /dev/null +++ b/misc/goplay/goplay.go @@ -0,0 +1,305 @@ +// 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 := "/tmp/compile" + strconv.Itoa(<-uniq) + + // write request Body to x.go + f, err := os.Open(x+".go", os.O_CREAT|os.O_WRONLY|os.O_TRUNC, 0666) + if err != nil { + error(w, nil, err) + return + } + defer os.Remove(x + ".go") + 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", x+"."+archChar, x+".go") + defer os.Remove(x + "." + archChar) + if err != nil { + error(w, out, err) + return + } + + // link x.6, creating x (the program binary) + out, err = run(archChar+"l", "-o", x, x+"."+archChar) + defer os.Remove(x) + if err != nil { + error(w, out, err) + return + } + + // run x + out, err = run(x) + 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() + + // 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 = `
{@|html}
` + +var frontPageText = ` + + + + + + +
+ +
+(Shift-Enter to compile and run.)     + Compile and run after each keystroke +
+
+ +
+
+
+ + +` + +var helloWorld = []byte(`package main + +import "fmt" + +func main() { + fmt.Println("hello, world") +} +`)