woj-server/internal/service/runner/compile.go

126 lines
3.2 KiB
Go

package runner
import (
"fmt"
"git.0x7f.app/WOJ/woj-server/internal/e"
"git.0x7f.app/WOJ/woj-server/pkg/file"
"git.0x7f.app/WOJ/woj-server/pkg/utils"
"go.uber.org/zap"
"io"
"os"
"path/filepath"
"time"
)
func (s *service) getProblemScript(version uint, lang string) (string, e.Status) {
config, err := s.ParseConfig(version, true)
if err != nil {
return "", e.RunnerProblemParseFailed
}
var script string
for _, l := range config.Languages {
if l.Lang == lang {
if l.Type == "default" {
script = "/woj/framework/template/default/" + lang + ".Makefile"
} else {
script = "/woj/problem/judge/" + l.Script
}
break
}
}
if script == "" {
return "", e.RunnerProblemParseFailed
}
return script, e.Success
}
func (s *service) Compile(version uint, user string, lang string) (JudgeStatus, e.Status) {
// 1. ensure problem/user exists
status := s.check(version, user, lang)
if status != e.Success {
return JudgeStatus{Message: "check failed"}, status
}
// 2. prepare judge environment
workDir := filepath.Join(UserDir, user)
judgeDir := filepath.Join(ProblemDir, fmt.Sprintf("%d", version), "judge")
sourceFile := filepath.Join(workDir, fmt.Sprintf("%s.%s", user, lang))
boxSourceFile := filepath.Join("/woj/problem/user", fmt.Sprintf("%s.%s", user, lang))
targetFile := filepath.Join(workDir, fmt.Sprintf("%s.out", user))
boxTargetFile := filepath.Join("/woj/problem/user", fmt.Sprintf("%s.out", user))
logFile := filepath.Join(workDir, fmt.Sprintf("%s.compile.log", user))
log, err := os.Create(logFile)
if err != nil {
return JudgeStatus{Message: "create log file failed"}, e.RunnerUserCompileFailed
}
defer func(log *os.File) {
_ = log.Close()
}(log)
// 3. compile
err = utils.NewMust().
Do(func() error {
_ = os.Remove(targetFile)
return nil
}).
Do(func() error { return file.TouchErr(targetFile) }).
Do(func() error {
script, err := s.getProblemScript(version, lang)
if err != e.Success {
return err.AsError()
}
args := []string{
"-v", judgeDir + ":/woj/problem/judge:ro",
"-v", sourceFile + ":" + boxSourceFile + ":ro",
"-v", targetFile + ":" + boxTargetFile,
"-e", fmt.Sprintf("USER_PROG=%s", user),
"-e", fmt.Sprintf("LANG=%s", lang),
"git.0x7f.app/woj/ubuntu-full:latest",
"sh", "-c", fmt.Sprintf("cd /woj/problem/user && make -f %s compile", script),
}
runArgs := &podmanArgs{
executeArgs: executeArgs{
args: args,
timeout: 60 * time.Second,
output: log,
},
memory: "256m",
}
return s.podmanRun(runArgs)
}).
Done()
if err != nil {
s.log.Info("compile failed",
zap.Error(err),
zap.Uint("version", version),
zap.String("user", user),
zap.String("lang", lang),
)
status = e.RunnerUserCompileFailed
}
// 4. read log
_, _ = log.Seek(0, io.SeekStart)
msg, err := io.ReadAll(log)
msg = utils.If(err == nil, msg, nil)
msgText := string(msg)
if !file.Exist(targetFile) || file.Empty(targetFile) {
return JudgeStatus{
Message: "compile failed",
Tasks: []TaskStatus{{Verdict: VerdictCompileError, Message: msgText}}},
utils.If(status == e.Success, e.RunnerUserCompileFailed, status)
}
return JudgeStatus{}, e.Success
}