119 lines
3.4 KiB
Go
119 lines
3.4 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) Compile(meta *JudgeMeta) (*JudgeStatus, e.Status) {
|
|
// 0. maybe we can skip compile
|
|
if v, ok := AllowedLanguages[meta.Cfg.Lang.Lang]; !ok || v.Interpreter != "" {
|
|
return &JudgeStatus{}, e.Success
|
|
}
|
|
|
|
// 1. prepare judge environment
|
|
workDir := filepath.Join(UserDir, meta.Run.User)
|
|
judgeDir := filepath.Join(ProblemDir, fmt.Sprintf("%d", meta.Run.Version), "judge")
|
|
|
|
sourceFile := filepath.Join(workDir, fmt.Sprintf("%s.%s", meta.Run.User, meta.Run.Lang))
|
|
targetFile := filepath.Join(workDir, fmt.Sprintf("%s.out", meta.Run.User))
|
|
|
|
logFile := filepath.Join(workDir, fmt.Sprintf("%s.compile.log", meta.Run.User))
|
|
log, err := os.Create(logFile)
|
|
if err != nil {
|
|
s.log.Error("[compile] create log file failed", zap.Error(err))
|
|
return &JudgeStatus{
|
|
Message: "System Error: Unable to create log file",
|
|
CompileMessage: "",
|
|
Tasks: []TaskStatus{{Verdict: VerdictSystemError, Message: "System Error"}},
|
|
}, e.RunnerUserCompileFailed
|
|
}
|
|
defer func(log *os.File) {
|
|
_ = log.Close()
|
|
}(log)
|
|
|
|
// 2. compile
|
|
err = utils.NewMust().
|
|
DoAny(func() error { return os.Remove(targetFile) }).
|
|
Do(func() error { return file.TouchErr(targetFile) }).
|
|
Do(func() error {
|
|
script := meta.Cfg.Lang.JudgeScript()
|
|
|
|
args := &RunArgs{
|
|
Program: ProgramArgs{
|
|
Args: []string{"/bin/sh", "-c", fmt.Sprintf("cd /woj/user && make -f %s compile", script)},
|
|
Env: []string{fmt.Sprintf("USER_PROG=%s", meta.Run.User), fmt.Sprintf("LANG=%s", meta.Run.Lang)},
|
|
},
|
|
Runtime: RuntimeArgs{
|
|
Rootfs: RootfsFullDir,
|
|
Pid: int64(meta.Cfg.Lang.Runtime.Compile.NProcLimit + 2), // bash + make
|
|
Memory: uint64(meta.Cfg.Lang.Runtime.Compile.MemoryLimit * 1024 * 1024),
|
|
Timeout: time.Duration((meta.Cfg.Lang.Runtime.Compile.TimeLimit+1000)/1000) * time.Second,
|
|
},
|
|
IO: IOArgs{
|
|
Output: log,
|
|
Limit: 4 * 1024, // 4 KB
|
|
},
|
|
}
|
|
args.Runtime.Mount = []MountInfo{
|
|
{
|
|
Source: judgeDir,
|
|
Destination: "/woj/problem/judge",
|
|
Readonly: true,
|
|
},
|
|
{
|
|
// Rust will write intermediate files into source directory instead of /tmp
|
|
Source: "",
|
|
Destination: "/woj/user",
|
|
},
|
|
{
|
|
Source: sourceFile,
|
|
Destination: fmt.Sprintf("/woj/user/%s.%s", meta.Run.User, meta.Run.Lang),
|
|
Readonly: true,
|
|
},
|
|
{
|
|
Source: targetFile,
|
|
Destination: fmt.Sprintf("/woj/user/%s.out", meta.Run.User),
|
|
Readonly: false,
|
|
},
|
|
}
|
|
|
|
id := s.JailRunPool(args)
|
|
ret := s.pool.WaitForTask(id)
|
|
return ret.Error
|
|
}).
|
|
Done()
|
|
|
|
status := e.Success
|
|
if err != nil {
|
|
s.log.Info("[compile] compile failed", zap.Error(err), zap.Any("meta", *meta))
|
|
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",
|
|
CompileMessage: msgText,
|
|
Tasks: []TaskStatus{{Verdict: VerdictCompileError, Message: msgText}}},
|
|
utils.If(status == e.Success, e.RunnerUserCompileFailed, status)
|
|
}
|
|
|
|
// 5. grant permission
|
|
_ = os.Chmod(targetFile, 0755)
|
|
|
|
return &JudgeStatus{CompileMessage: msgText}, e.Success
|
|
}
|