Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 package main | 1 package main |
| 2 | 2 |
| 3 import ( | 3 import ( |
| 4 "bytes" | 4 "bytes" |
| 5 "crypto/md5" | 5 "crypto/md5" |
| 6 "database/sql" | 6 "database/sql" |
| 7 "encoding/base64" | 7 "encoding/base64" |
| 8 "encoding/binary" | 8 "encoding/binary" |
| 9 "encoding/json" | 9 "encoding/json" |
| 10 "flag" | 10 "flag" |
| 11 "fmt" | 11 "fmt" |
| 12 htemplate "html/template" | 12 htemplate "html/template" |
| 13 "image" | 13 "image" |
| 14 _ "image/gif" | 14 _ "image/gif" |
| 15 _ "image/jpeg" | 15 _ "image/jpeg" |
| 16 "image/png" | 16 "image/png" |
| 17 "io/ioutil" | 17 "io/ioutil" |
| 18 "log" | 18 "log" |
| 19 "math/rand" | 19 "math/rand" |
| 20 "net" | 20 "net" |
| 21 "net/http" | 21 "net/http" |
| 22 "os" | 22 "os" |
| 23 "os/exec" | 23 "os/exec" |
| 24 "path/filepath" | 24 "path/filepath" |
| 25 "regexp" | 25 "regexp" |
| 26 "strconv" | |
| 26 "strings" | 27 "strings" |
| 27 "text/template" | 28 "text/template" |
| 28 "time" | 29 "time" |
| 29 ) | 30 ) |
| 30 | 31 |
| 31 import ( | 32 import ( |
| 32 "github.com/fiorix/go-web/autogzip" | 33 "github.com/fiorix/go-web/autogzip" |
| 33 _ "github.com/go-sql-driver/mysql" | 34 _ "github.com/go-sql-driver/mysql" |
| 34 _ "github.com/mattn/go-sqlite3" | 35 _ "github.com/mattn/go-sqlite3" |
| 35 "github.com/rcrowley/go-metrics" | 36 "github.com/rcrowley/go-metrics" |
| (...skipping 361 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 397 return hash, err | 398 return hash, err |
| 398 } | 399 } |
| 399 | 400 |
| 400 // expandGyp produces the GYP file needed to build the code | 401 // expandGyp produces the GYP file needed to build the code |
| 401 func expandGyp(hash string) error { | 402 func expandGyp(hash string) error { |
| 402 return writeTemplate(fmt.Sprintf("../../../cache/%s.gyp", hash), gypTemp late, struct{ Hash string }{hash}) | 403 return writeTemplate(fmt.Sprintf("../../../cache/%s.gyp", hash), gypTemp late, struct{ Hash string }{hash}) |
| 403 } | 404 } |
| 404 | 405 |
| 405 // response is serialized to JSON as a response to POSTs. | 406 // response is serialized to JSON as a response to POSTs. |
| 406 type response struct { | 407 type response struct { |
| 407 » Message string `json:"message"` | 408 » Message string `json:"message"` |
| 408 » StdOut string `json:"stdout"` | 409 » CompileErrors []compileError `json:"compileErrors"` |
| 409 » Img string `json:"img"` | 410 » Img string `json:"img"` |
| 410 » Hash string `json:"hash"` | 411 » Hash string `json:"hash"` |
| 411 } | 412 } |
| 412 | 413 |
| 413 // doCmd executes the given command line string; the command being | 414 // doCmd executes the given command line string; the command being |
| 414 // run is expected to not care what its current working directory is. | 415 // run is expected to not care what its current working directory is. |
| 415 // Returns the stdout and stderr. | 416 // Returns the stdout and stderr. |
| 416 func doCmd(commandLine string) (string, error) { | 417 func doCmd(commandLine string) (string, error) { |
| 417 log.Printf("Command: %q\n", commandLine) | 418 log.Printf("Command: %q\n", commandLine) |
| 418 programAndArgs := strings.SplitN(commandLine, " ", 2) | 419 programAndArgs := strings.SplitN(commandLine, " ", 2) |
| 419 program := programAndArgs[0] | 420 program := programAndArgs[0] |
| 420 args := []string{} | 421 args := []string{} |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 439 } | 440 } |
| 440 | 441 |
| 441 // reportTryError formats an HTTP error response in JSON and also logs the detai led error message. | 442 // reportTryError formats an HTTP error response in JSON and also logs the detai led error message. |
| 442 func reportTryError(w http.ResponseWriter, r *http.Request, err error, message, hash string) { | 443 func reportTryError(w http.ResponseWriter, r *http.Request, err error, message, hash string) { |
| 443 m := response{ | 444 m := response{ |
| 444 Message: message, | 445 Message: message, |
| 445 Hash: hash, | 446 Hash: hash, |
| 446 } | 447 } |
| 447 log.Printf("Error: %s\n%s", message, err.Error()) | 448 log.Printf("Error: %s\n%s", message, err.Error()) |
| 448 resp, err := json.Marshal(m) | 449 resp, err := json.Marshal(m) |
| 450 | |
| 449 if err != nil { | 451 if err != nil { |
| 450 http.Error(w, "Failed to serialize a response", 500) | 452 http.Error(w, "Failed to serialize a response", 500) |
| 451 return | 453 return |
| 454 } | |
| 455 w.Header().Set("Content-Type", "text/plain") | |
| 456 w.Write(resp) | |
| 457 } | |
| 458 | |
| 459 func reportCompileError(w http.ResponseWriter, r *http.Request, compileErrors [] compileError, hash string) { | |
| 460 m := response{ | |
| 461 CompileErrors: compileErrors, | |
| 462 Hash: hash, | |
| 463 } | |
| 464 | |
| 465 resp, err := json.Marshal(m) | |
| 466 | |
| 467 if err != nil { | |
| 468 http.Error(w, "Failed to serialize a response", 500) | |
| 469 return | |
| 452 } | 470 } |
| 453 w.Header().Set("Content-Type", "text/plain") | 471 w.Header().Set("Content-Type", "text/plain") |
| 454 w.Write(resp) | 472 w.Write(resp) |
| 455 } | 473 } |
| 456 | 474 |
| 457 func writeToDatabase(hash string, code string, workspaceName string, source int, width, height int, gpu bool) { | 475 func writeToDatabase(hash string, code string, workspaceName string, source int, width, height int, gpu bool) { |
| 458 if db == nil { | 476 if db == nil { |
| 459 return | 477 return |
| 460 } | 478 } |
| 461 if _, err := db.Exec("INSERT INTO webtry (code, hash, width, height, gpu , source_image_id) VALUES(?, ?, ?, ?, ?, ?)", code, hash, width, height, gpu, so urce); err != nil { | 479 if _, err := db.Exec("INSERT INTO webtry (code, hash, width, height, gpu , source_image_id) VALUES(?, ?, ?, ?, ?, ?)", code, hash, width, height, gpu, so urce); err != nil { |
| (...skipping 323 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 785 w.Header().Set("Content-Type", "application/json") | 803 w.Header().Set("Content-Type", "application/json") |
| 786 w.Write(resp) | 804 w.Write(resp) |
| 787 } | 805 } |
| 788 | 806 |
| 789 func cleanCompileOutput(s, hash string) string { | 807 func cleanCompileOutput(s, hash string) string { |
| 790 old := "../../../cache/src/" + hash + ".cpp:" | 808 old := "../../../cache/src/" + hash + ".cpp:" |
| 791 log.Printf("INFO: replacing %q\n", old) | 809 log.Printf("INFO: replacing %q\n", old) |
| 792 return strings.Replace(s, old, "usercode.cpp:", -1) | 810 return strings.Replace(s, old, "usercode.cpp:", -1) |
| 793 } | 811 } |
| 794 | 812 |
| 813 type compileError struct { | |
| 814 Line int `json:"line"` | |
| 815 Column int `json:"column"` | |
| 816 Error string `json:"error"` | |
| 817 } | |
| 818 | |
| 795 // mainHandler handles the GET and POST of the main page. | 819 // mainHandler handles the GET and POST of the main page. |
| 796 func mainHandler(w http.ResponseWriter, r *http.Request) { | 820 func mainHandler(w http.ResponseWriter, r *http.Request) { |
| 797 log.Printf("Main Handler: %q\n", r.URL.Path) | 821 log.Printf("Main Handler: %q\n", r.URL.Path) |
| 798 requestsCounter.Inc(1) | 822 requestsCounter.Inc(1) |
| 799 if r.Method == "GET" { | 823 if r.Method == "GET" { |
| 800 code := DEFAULT_SAMPLE | 824 code := DEFAULT_SAMPLE |
| 801 source := 0 | 825 source := 0 |
| 802 width := 256 | 826 width := 256 |
| 803 height := 256 | 827 height := 256 |
| 804 gpu := false | 828 gpu := false |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 860 cmd += " --gpu" | 884 cmd += " --gpu" |
| 861 } | 885 } |
| 862 if *useChroot { | 886 if *useChroot { |
| 863 cmd = "schroot -c webtry --directory=/ -- /skia_build/sk ia/experimental/webtry/" + cmd | 887 cmd = "schroot -c webtry --directory=/ -- /skia_build/sk ia/experimental/webtry/" + cmd |
| 864 } | 888 } |
| 865 if request.Source > 0 { | 889 if request.Source > 0 { |
| 866 cmd += fmt.Sprintf(" --source image-%d.png", request.Sou rce) | 890 cmd += fmt.Sprintf(" --source image-%d.png", request.Sou rce) |
| 867 } | 891 } |
| 868 | 892 |
| 869 message, err := doCmd(cmd) | 893 message, err := doCmd(cmd) |
| 894 | |
| 895 outputLines := strings.Split(message, "\n") | |
| 896 errorLines := []compileError{} | |
| 897 errorRE := regexp.MustCompile(fmt.Sprintf("^.*%s.cpp:(\\d+):(\\d +):\\s*(.*)", hash)) | |
|
jcgregorio
2014/10/15 19:59:50
Move to top of file so it's only compiled once.
humper
2014/10/15 21:03:59
Done.
| |
| 898 for _, line := range outputLines { | |
| 899 match := errorRE.FindStringSubmatch(line) | |
| 900 if len(match) > 0 { | |
| 901 lineNumber, parseError := strconv.Atoi(match[1]) | |
| 902 if parseError != nil { | |
| 903 log.Printf("ERROR: Couldn't parse line n umber from %s\n", match[1]) | |
|
jcgregorio
2014/10/15 19:59:50
Add a TODO to switch from log to glog (http://godo
tfarina
2014/10/15 20:05:21
I will do that. I have a bug filed for that: https
| |
| 904 continue | |
| 905 } | |
| 906 columnNumber, parseError := strconv.Atoi(match[2 ]) | |
| 907 if parseError != nil { | |
| 908 log.Printf("ERROR: Couldn't parse column number from %s\n", match[2]) | |
| 909 continue | |
| 910 } | |
| 911 errorLines = append(errorLines, | |
| 912 compileError{ | |
| 913 Line: lineNumber, | |
| 914 Column: columnNumber, | |
| 915 Error: match[3], | |
| 916 }) | |
| 917 } | |
| 918 } | |
| 919 | |
| 870 if err != nil { | 920 if err != nil { |
| 871 » » » reportTryError(w, r, err, "Failed to run the code:\n"+me ssage, hash) | 921 » » » if len(errorLines) > 0 { |
| 922 » » » » reportCompileError(w, r, errorLines, hash) | |
| 923 » » » } else { | |
| 924 » » » » reportTryError(w, r, err, "Failed to run the cod e:\n"+message, hash) | |
| 925 » » » } | |
| 872 return | 926 return |
| 873 } | 927 } |
| 928 | |
| 874 png, err := ioutil.ReadFile("../../../inout/" + hash + ".png") | 929 png, err := ioutil.ReadFile("../../../inout/" + hash + ".png") |
| 875 if err != nil { | 930 if err != nil { |
| 876 reportTryError(w, r, err, "Failed to open the generated PNG.", hash) | 931 reportTryError(w, r, err, "Failed to open the generated PNG.", hash) |
| 877 return | 932 return |
| 878 } | 933 } |
| 879 | 934 |
| 880 m := response{ | 935 m := response{ |
| 881 Message: message, | 936 Message: message, |
| 882 Img: base64.StdEncoding.EncodeToString([]byte(png)), | 937 Img: base64.StdEncoding.EncodeToString([]byte(png)), |
| 883 Hash: hash, | 938 Hash: hash, |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 902 http.HandleFunc("/sources/", autogzip.HandleFunc(sourcesHandler)) | 957 http.HandleFunc("/sources/", autogzip.HandleFunc(sourcesHandler)) |
| 903 | 958 |
| 904 // Resources are served directly | 959 // Resources are served directly |
| 905 // TODO add support for caching/etags/gzip | 960 // TODO add support for caching/etags/gzip |
| 906 http.Handle("/res/", autogzip.Handle(http.FileServer(http.Dir("./")))) | 961 http.Handle("/res/", autogzip.Handle(http.FileServer(http.Dir("./")))) |
| 907 | 962 |
| 908 // TODO Break out /c/ as it's own handler. | 963 // TODO Break out /c/ as it's own handler. |
| 909 http.HandleFunc("/", autogzip.HandleFunc(mainHandler)) | 964 http.HandleFunc("/", autogzip.HandleFunc(mainHandler)) |
| 910 log.Fatal(http.ListenAndServe(*port, nil)) | 965 log.Fatal(http.ListenAndServe(*port, nil)) |
| 911 } | 966 } |
| OLD | NEW |