]> Cypherpunks repositories - gostls13.git/commitdiff
misc/goplay: remain in work directory, build in temp directory
authorAndrew Gerrand <adg@golang.org>
Tue, 21 Feb 2012 00:24:29 +0000 (11:24 +1100)
committerAndrew Gerrand <adg@golang.org>
Tue, 21 Feb 2012 00:24:29 +0000 (11:24 +1100)
Fixes #2935.

R=golang-dev, r
CC=golang-dev
https://golang.org/cl/5684048

misc/goplay/goplay.go

index 28c91d928316c9e2c3f085bfc74e6f82f96839ff..82523e3fd16cd0702e973c5f01b9de5f3dc71bbb 100644 (file)
@@ -5,13 +5,16 @@
 package main
 
 import (
+       "bytes"
        "flag"
-       "io"
        "io/ioutil"
        "log"
        "net/http"
        "os"
        "os/exec"
+       "path/filepath"
+       "regexp"
+       "runtime"
        "strconv"
        "text/template"
 )
@@ -63,26 +66,7 @@ func FrontPage(w http.ResponseWriter, req *http.Request) {
 // runs the program (returning any errors),
 // 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 files
-       x := "goplay" + strconv.Itoa(<-uniq) + ".go"
-
-       // write request Body to x.go
-       f, err := os.Create(x)
-       if err != nil {
-               error_(w, nil, err)
-               return
-       }
-       defer os.Remove(x)
-       defer f.Close()
-       _, err = io.Copy(f, req.Body)
-       if err != nil {
-               error_(w, nil, err)
-               return
-       }
-       f.Close()
-
-       // run x
-       out, err := run("go", "run", x)
+       out, err := compile(req)
        if err != nil {
                error_(w, out, err)
                return
@@ -96,6 +80,60 @@ func Compile(w http.ResponseWriter, req *http.Request) {
        }
 }
 
+var (
+       commentRe = regexp.MustCompile(`(?m)^#.*\n`)
+       tmpdir    string
+)
+
+func init() {
+       // find real temporary directory (for rewriting filename in output)
+       var err error
+       tmpdir, err = filepath.EvalSymlinks(os.TempDir())
+       if err != nil {
+               log.Fatal(err)
+       }
+}
+
+func compile(req *http.Request) (out []byte, err error) {
+       // x is the base name for .go, .6, executable files
+       x := filepath.Join(tmpdir, "compile"+strconv.Itoa(<-uniq))
+       src := x + ".go"
+       bin := x
+       if runtime.GOOS == "windows" {
+               bin += ".exe"
+       }
+
+       // rewrite filename in error output
+       defer func() {
+               if err != nil {
+                       // drop messages from the go tool like '# _/compile0'
+                       out = commentRe.ReplaceAll(out, nil)
+               }
+               out = bytes.Replace(out, []byte(src+":"), []byte("main.go:"), -1)
+       }()
+
+       // write body to x.go
+       body := new(bytes.Buffer)
+       if _, err = body.ReadFrom(req.Body); err != nil {
+               return
+       }
+       defer os.Remove(src)
+       if err = ioutil.WriteFile(src, body.Bytes(), 0666); err != nil {
+               return
+       }
+
+       // build x.go, creating x
+       dir, file := filepath.Split(src)
+       out, err = run(dir, "go", "build", "-o", bin, file)
+       defer os.Remove(bin)
+       if err != nil {
+               return
+       }
+
+       // run x
+       return run("", bin)
+}
+
 // 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 error) {
@@ -108,8 +146,14 @@ func error_(w http.ResponseWriter, out []byte, err error) {
 }
 
 // run executes the specified command and returns its output and an error.
-func run(cmd ...string) ([]byte, error) {
-       return exec.Command(cmd[0], cmd[1:]...).CombinedOutput()
+func run(dir string, args ...string) ([]byte, error) {
+       var buf bytes.Buffer
+       cmd := exec.Command(args[0], args[1:]...)
+       cmd.Dir = dir
+       cmd.Stdout = &buf
+       cmd.Stderr = cmd.Stdout
+       err := cmd.Run()
+       return buf.Bytes(), err
 }
 
 var frontPage = template.Must(template.New("frontPage").Parse(frontPageText)) // HTML template