Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 package main | |
| 2 | |
| 3 import ( | |
| 4 "bytes" | |
| 5 "crypto/md5" | |
| 6 "encoding/base64" | |
| 7 "encoding/json" | |
| 8 "flag" | |
| 9 "fmt" | |
| 10 "io/ioutil" | |
| 11 "log" | |
| 12 "net/http" | |
| 13 "os" | |
| 14 "os/exec" | |
| 15 "path/filepath" | |
| 16 "strings" | |
| 17 "text/template" | |
| 18 ) | |
| 19 | |
| 20 const ( | |
| 21 RESULT_COMPILE = `c++ -DSK_GAMMA_SRGB -DSK_GAMMA_APPLY_TO_A8 -DSK_SCALAR _TO_FLOAT_EXCLUDED -DSK_ALLOW_STATIC_GLOBAL_INITIALIZERS=1 -DSK_SUPPORT_GPU=0 -D SK_SUPPORT_OPENCL=0 -DSK_FORCE_DISTANCEFIELD_FONTS=0 -DSK_SCALAR_IS_FLOAT -DSK_C AN_USE_FLOAT -DSK_SAMPLES_FOR_X -DSK_BUILD_FOR_UNIX -DSK_USE_POSIX_THREADS -DSK_ SYSTEM_ZLIB=1 -DSK_DEBUG -DSK_DEVELOPER=1 -I../../src/core -I../../src/images -I ../../tools/flags -I../../include/config -I../../include/core -I../../include/pa thops -I../../include/pipe -I../../include/effects -I../../include/ports -I../.. /src/sfnt -I../../include/utils -I../../src/utils -I../../include/images -g -fno -exceptions -fstrict-aliasing -Wall -Wextra -Winit-self -Wpointer-arith -Wno-unu sed-parameter -Wno-c++11-extensions -Werror -m64 -fno-rtti -Wnon-virtual-dtor -c ../../../cache/%s.cpp -o ../../../cache/%s.o` | |
| 22 LINK = `c++ -m64 -lstdc++ -lm -o ../../../inout/%s -Wl,--start -group ../../../cache/%s.o obj/experimental/webtry/webtry.main.o obj/experimenta l/webtry/webtry.syscall_reporter.o obj/gyp/libflags.a libskia_images.a libskia_c ore.a libskia_effects.a obj/gyp/libjpeg.a obj/gyp/libwebp_dec.a obj/gyp/libwebp_ demux.a obj/gyp/libwebp_dsp.a obj/gyp/libwebp_enc.a obj/gyp/libwebp_utils.a libs kia_utils.a libskia_opts.a libskia_opts_ssse3.a libskia_ports.a libskia_sfnt.a - Wl,--end-group -lpng -lz -lgif -lpthread -lfontconfig -ldl -lfreetype` | |
| 23 ) | |
| 24 | |
| 25 var ( | |
| 26 // codeTemplate is the cpp code template the user's code is copied into. | |
| 27 codeTemplate *template.Template = nil | |
| 28 | |
| 29 // index is the main index.html page we serve. | |
| 30 index []byte | |
| 31 ) | |
| 32 | |
| 33 // flags | |
| 34 var ( | |
| 35 useChroot = flag.Bool("use_chroot", false, "Run the compiled code in the schroot jail.") | |
|
mtklein
2014/04/08 19:00:23
My paranoia wants this to default to true now that
jcgregorio
2014/04/09 14:10:31
Not just local testing, but anyone can run this lo
| |
| 36 ) | |
| 37 | |
| 38 // lineNumbers adds #line numbering to the user's code. | |
| 39 func LineNumbers(c string) string { | |
| 40 lines := strings.Split(c, "\n") | |
| 41 ret := []string{} | |
| 42 for i, line := range lines { | |
| 43 ret = append(ret, fmt.Sprintf("#line %d", i+1)) | |
| 44 ret = append(ret, line) | |
| 45 } | |
| 46 return strings.Join(ret, "\n") | |
| 47 } | |
| 48 | |
| 49 func init() { | |
| 50 // Change the current working directory to be where the directory of the executable. | |
|
mtklein
2014/04/08 19:00:23
Funky wording?
jcgregorio
2014/04/09 14:10:31
Done.
| |
| 51 var err error | |
| 52 cwd, err := filepath.Abs(filepath.Dir(os.Args[0])) | |
| 53 if err != nil { | |
| 54 log.Fatal(err) | |
| 55 } | |
| 56 os.Chdir(cwd) | |
| 57 | |
| 58 codeTemplate, err = template.ParseFiles(filepath.Join(cwd, "templates/te mplate.cpp")) | |
| 59 if err != nil { | |
| 60 panic(err) | |
| 61 } | |
| 62 index, err = ioutil.ReadFile(filepath.Join(cwd, "templates/index.html")) | |
| 63 if err != nil { | |
| 64 panic(err) | |
| 65 } | |
| 66 } | |
| 67 | |
| 68 // userCode is used in template expansion. | |
| 69 type userCode struct { | |
|
mtklein
2014/04/08 19:00:23
I've got no problem with the struct if you like it
jcgregorio
2014/04/09 14:10:31
I think that would make the template for expansion
mtklein
2014/04/09 15:03:00
Yeah, it would and it does work. I think it's eve
| |
| 70 UserCode string | |
| 71 } | |
| 72 | |
| 73 // expandToFile expands the template and writes the result to the file. | |
| 74 func expandToFile(filename string, c userCode, t *template.Template) error { | |
| 75 f, err := os.Create(filename) | |
| 76 if err != nil { | |
| 77 return err | |
| 78 } | |
| 79 defer f.Close() | |
| 80 | |
| 81 return t.Execute(f, c) | |
| 82 } | |
| 83 | |
| 84 // ExpandCode expands the template into a file and calculate the MD5 hash. | |
| 85 func ExpandCode(code string) (string, error) { | |
|
mtklein
2014/04/08 19:00:23
expandCode?
jcgregorio
2014/04/09 14:10:31
Done.
| |
| 86 h := md5.New() | |
| 87 h.Write([]byte(code)) | |
| 88 hash := fmt.Sprintf("%x", h.Sum(nil)) | |
| 89 content := userCode{ | |
| 90 UserCode: code, | |
| 91 } | |
| 92 err := expandToFile(fmt.Sprintf("../../../cache/%s.cpp", hash), content, codeTemplate) | |
|
mtklein
2014/04/08 19:00:23
Could be helpful to remind the reader where we are
jcgregorio
2014/04/09 14:10:31
Done. Also added a TODO to make into flags.
| |
| 93 return hash, err | |
| 94 } | |
| 95 | |
| 96 // Response is serialed to JSON as a response to POSTs. | |
| 97 type Response struct { | |
|
mtklein
2014/04/08 19:00:23
response?
jcgregorio
2014/04/09 14:10:31
Done.
| |
| 98 Message string `json:"message"` | |
| 99 Img string `json:"img"` | |
| 100 } | |
| 101 | |
| 102 // doCmd executes the given command line string in either the out/Debug director y or the inout directory. | |
| 103 func doCmd(commandLine string, moveToDebug bool) (string, error) { | |
|
mtklein
2014/04/08 19:00:23
Not clear here what it returns? can this be (stdo
jcgregorio
2014/04/09 14:10:31
Done.
| |
| 104 log.Printf("Command: %q\n", commandLine) | |
| 105 programAndArgs := strings.SplitN(commandLine, " ", 2) | |
| 106 program := programAndArgs[0] | |
| 107 args := []string{} | |
| 108 if len(programAndArgs) > 1 { | |
| 109 args = strings.Split(programAndArgs[1], " ") | |
| 110 } | |
| 111 cmd := exec.Command(program, args...) | |
| 112 abs, err := filepath.Abs("../../out/Debug") | |
| 113 if err != nil { | |
| 114 return "", fmt.Errorf("Failed to find absolute path to Debug dir ectory.") | |
| 115 } | |
| 116 if moveToDebug { | |
| 117 cmd.Dir = abs | |
| 118 } else if *useChroot { | |
|
mtklein
2014/04/08 19:00:23
?
if moveToDebug {
} else if !*useChroot {
}
?
jcgregorio
2014/04/09 14:10:31
Done.
| |
| 119 } else { | |
| 120 abs, err := filepath.Abs("../../../inout") | |
|
mtklein
2014/04/08 19:00:23
Might help readability to make some of these liter
jcgregorio
2014/04/09 14:10:31
Added as TODO above.
| |
| 121 if err != nil { | |
| 122 return "", fmt.Errorf("Failed to find absolute path to i nout directory.") | |
| 123 } | |
| 124 cmd.Dir = abs | |
| 125 } | |
| 126 log.Printf("Run in directory: %q\n", cmd.Dir) | |
| 127 var stdOut bytes.Buffer | |
| 128 cmd.Stdout = &stdOut | |
| 129 var stdErr bytes.Buffer | |
| 130 cmd.Stderr = &stdErr | |
| 131 cmd.Start() | |
| 132 err = cmd.Wait() | |
| 133 message := stdOut.String() | |
| 134 log.Printf("StdOut: %s\n", message) | |
| 135 if err != nil { | |
| 136 log.Printf("Exit status: %s\n", err.Error()) | |
| 137 log.Printf("StdErr: %s\n", stdErr.String()) | |
| 138 message += stdErr.String() | |
| 139 return message, fmt.Errorf("Failed to run command.") | |
| 140 } | |
| 141 return message, nil | |
| 142 } | |
| 143 | |
| 144 // reportError formats an HTTP error response and also logs the detailed error m essage. | |
| 145 func reportError(w http.ResponseWriter, r *http.Request, err error, message stri ng) { | |
| 146 m := Response{ | |
| 147 Message: message, | |
| 148 } | |
| 149 log.Printf("Error: %s\n%s", message, err.Error()) | |
| 150 resp, err := json.Marshal(m) | |
| 151 if err != nil { | |
| 152 http.Error(w, "Failed to serialize a response", 500) | |
| 153 return | |
| 154 } | |
| 155 w.Write(resp) | |
| 156 } | |
| 157 | |
| 158 // mainHandler handles the GET and POST of the main page. | |
| 159 func mainHandler(w http.ResponseWriter, r *http.Request) { | |
| 160 if r.Method == "GET" { | |
| 161 w.Write(index) | |
| 162 } else if r.Method == "POST" { | |
| 163 w.Header().Set("Content-Type", "application/json") | |
| 164 b, err := ioutil.ReadAll(r.Body) | |
| 165 if err != nil { | |
| 166 reportError(w, r, err, "Failed to read a request body.") | |
| 167 return | |
| 168 } | |
| 169 hash, err := ExpandCode(LineNumbers(string(b))) | |
| 170 if err != nil { | |
| 171 reportError(w, r, err, "Failed to write the code to comp ile.") | |
| 172 return | |
| 173 } | |
| 174 message, err := doCmd(fmt.Sprintf(RESULT_COMPILE, hash, hash), t rue) | |
| 175 if err != nil { | |
| 176 reportError(w, r, err, "Failed to compile the code.") | |
| 177 return | |
| 178 } | |
| 179 linkMessage, err := doCmd(fmt.Sprintf(LINK, hash, hash), true) | |
| 180 if err != nil { | |
| 181 reportError(w, r, err, "Failed to link the code.") | |
| 182 return | |
| 183 } | |
| 184 message += linkMessage | |
| 185 cmd := hash + " --out " + hash + ".png" | |
| 186 if *useChroot { | |
| 187 cmd = "schroot -c webtry --directory=/inout -- /inout/" + cmd | |
| 188 } else { | |
| 189 abs, err := filepath.Abs("../../../inout") | |
| 190 if err != nil { | |
| 191 reportError(w, r, err, "Failed to find executabl e directory.") | |
| 192 return | |
| 193 } | |
| 194 cmd = abs + "/" + cmd | |
| 195 } | |
| 196 | |
| 197 execMessage, err := doCmd(cmd, false) | |
| 198 if err != nil { | |
| 199 reportError(w, r, err, "Failed to run the code: "+execMe ssage) | |
| 200 return | |
| 201 } | |
| 202 png, err := ioutil.ReadFile("../../../inout/" + hash + ".png") | |
| 203 if err != nil { | |
| 204 reportError(w, r, err, "Failed to open the generated PNG .") | |
| 205 return | |
| 206 } | |
| 207 | |
| 208 m := Response{ | |
| 209 Message: message, | |
| 210 Img: base64.StdEncoding.EncodeToString([]byte(png)), | |
| 211 } | |
| 212 resp, err := json.Marshal(m) | |
| 213 if err != nil { | |
| 214 reportError(w, r, err, "Failed to serialize a response." ) | |
| 215 return | |
| 216 } | |
| 217 w.Write(resp) | |
| 218 } | |
| 219 } | |
| 220 | |
| 221 func main() { | |
| 222 flag.Parse() | |
| 223 | |
| 224 http.HandleFunc("/", mainHandler) | |
| 225 log.Fatal(http.ListenAndServe(":8000", nil)) | |
| 226 } | |
| OLD | NEW |