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{"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, }, { 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 }