gobuilder: write build and benchmarking logs to disk

R=rsc
CC=golang-dev
https://golang.org/cl/2637041
diff --git a/misc/dashboard/builder/exec.go b/misc/dashboard/builder/exec.go
index 009c769..91e991e 100644
--- a/misc/dashboard/builder/exec.go
+++ b/misc/dashboard/builder/exec.go
@@ -3,6 +3,7 @@
 import (
 	"bytes"
 	"exec"
+	"io"
 	"os"
 	"strings"
 )
@@ -21,8 +22,9 @@
 	return p.Close()
 }
 
-// runLog runs a process and returns the combined stdout/stderr
-func runLog(envv []string, dir string, argv ...string) (output string, exitStatus int, err os.Error) {
+// 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
@@ -34,15 +36,24 @@
 	}
 	defer p.Close()
 	b := new(bytes.Buffer)
-	_, err = b.ReadFrom(p.Stdout)
+	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
 	}
-	w, err := p.Wait(0)
+	wait, err := p.Wait(0)
 	if err != nil {
 		return
 	}
-	return b.String(), w.WaitStatus.ExitStatus(), nil
+	return b.String(), wait.WaitStatus.ExitStatus(), nil
 }
 
 // Find bin in PATH if a relative or absolute path hasn't been specified