// 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 ( "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.Fatalln("unrecognized GOARCH:", runtime.GOARCH) } // source of unique numbers go func() { for i := 0; ; i++ { uniq <- i } }() http.HandleFunc("/", FrontPage) http.HandleFunc("/compile", Compile) log.Fatal(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(w, data) } // 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.Create(src) 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(w, out) } } // 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(w, out) } else { output.Execute(w, err.String()) } } // run executes the specified command and returns its output and an error. func run(cmd ...string) ([]byte, os.Error) { return exec.Command(cmd[0], cmd[1:]...).CombinedOutput() } var frontPage = template.Must(template.New("frontPage").Parse(frontPageText)) // HTML template var output = template.Must(template.New("output").Parse(outputText)) // HTML template 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") } `)