From 041edbcc79ff6922436bc04cff6f8a7fe96566e0 Mon Sep 17 00:00:00 2001 From: Andrew Gerrand Date: Tue, 21 Feb 2012 11:24:29 +1100 Subject: [PATCH] misc/goplay: remain in work directory, build in temp directory Fixes #2935. R=golang-dev, r CC=golang-dev https://golang.org/cl/5684048 --- misc/goplay/goplay.go | 90 ++++++++++++++++++++++++++++++++----------- 1 file changed, 67 insertions(+), 23 deletions(-) diff --git a/misc/goplay/goplay.go b/misc/goplay/goplay.go index 28c91d9283..82523e3fd1 100644 --- a/misc/goplay/goplay.go +++ b/misc/goplay/goplay.go @@ -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