mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 01:38:19 +08:00
core: update judge config
This commit is contained in:
1 parent
d13876a5bc
commit
a2004056ac
22 files changed
+100
-245
No files matched your search
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@hydrooj/hydrojudge",
|
"name": "@hydrooj/hydrojudge",
|
||||||
"bin": "bin/hydrojudge.js",
|
"bin": "bin/hydrojudge.js",
|
||||||
"version": "2.13.8",
|
"version": "2.14.0",
|
||||||
"main": "package.json",
|
"main": "package.json",
|
||||||
"author": "undefined <i@undefined.moe>",
|
"author": "undefined <i@undefined.moe>",
|
||||||
"repository": "https://github.com/hydro-dev/Hydro.git",
|
"repository": "https://github.com/hydro-dev/Hydro.git",
|
||||||
|
|||||||
@@ -155,7 +155,7 @@ async function read1(folder: string, files: string[], checkFile, cfg, rst) {
|
|||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function readAutoCases(folder, { next }, cfg, rst) {
|
async function readAutoCases(folder: string, { next }, cfg, rst) {
|
||||||
const config = {
|
const config = {
|
||||||
checker_type: 'default',
|
checker_type: 'default',
|
||||||
count: 0,
|
count: 0,
|
||||||
@@ -177,7 +177,7 @@ async function readAutoCases(folder, { next }, cfg, rst) {
|
|||||||
let result = await read0(folder, files, checkFile, cfg);
|
let result = await read0(folder, files, checkFile, cfg);
|
||||||
if (!result.count) result = await read1(folder, files, checkFile, cfg, rst);
|
if (!result.count) result = await read1(folder, files, checkFile, cfg, rst);
|
||||||
Object.assign(config, result);
|
Object.assign(config, result);
|
||||||
next({ message: { message: 'Found {0} testcases.', params: [config.count] } });
|
if (cfg.isSelfSubmission) next({ message: { message: 'Found {0} testcases.', params: [config.count] } });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new SystemError('Cannot parse testdata.', [e.message, ...e.params]);
|
throw new SystemError('Cannot parse testdata.', [e.message, ...e.params]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import checkers from './checkers';
|
|||||||
import compile from './compile';
|
import compile from './compile';
|
||||||
import { SystemError } from './error';
|
import { SystemError } from './error';
|
||||||
import { CheckConfig, Execute } from './interface';
|
import { CheckConfig, Execute } from './interface';
|
||||||
import { parseFilename } from './utils';
|
|
||||||
|
|
||||||
const testlibSrc = findFileSync('@hydrooj/hydrojudge/vendor/testlib/testlib.h');
|
const testlibSrc = findFileSync('@hydrooj/hydrojudge/vendor/testlib/testlib.h');
|
||||||
|
|
||||||
@@ -21,6 +20,15 @@ export async function compileChecker(getLang: Function, checkerType: string, che
|
|||||||
if (!checkers[checkerType]) throw new SystemError('Unknown checker type {0}.', [checkerType]);
|
if (!checkers[checkerType]) throw new SystemError('Unknown checker type {0}.', [checkerType]);
|
||||||
if (checkerType === 'testlib') copyIn['testlib.h'] = { src: testlibSrc };
|
if (checkerType === 'testlib') copyIn['testlib.h'] = { src: testlibSrc };
|
||||||
const file = await fs.readFile(checker);
|
const file = await fs.readFile(checker);
|
||||||
|
const s = checker.replace('@', '.').split('.');
|
||||||
|
let lang;
|
||||||
|
let langId = s.pop();
|
||||||
|
while (s.length) {
|
||||||
|
lang = getLang(langId, false);
|
||||||
|
if (lang) break;
|
||||||
|
langId = `${s.pop()}.${langId}`;
|
||||||
|
}
|
||||||
|
if (!lang) throw new SystemError('Unknown checker language.');
|
||||||
// TODO cache compiled checker
|
// TODO cache compiled checker
|
||||||
return await compile(getLang(parseFilename(checker).split('.')[1].replace('@', '.')), file.toString(), 'checker', copyIn);
|
return await compile(lang, file.toString(), copyIn);
|
||||||
}
|
}
|
||||||
@@ -79,7 +79,7 @@ const checkers: Record<string, Checker> = {
|
|||||||
* exit code:返回判断结果
|
* exit code:返回判断结果
|
||||||
*/
|
*/
|
||||||
async hustoj(config) {
|
async hustoj(config) {
|
||||||
const { code, stdout } = await run('${dir}/checker input answer usrout', {
|
const { code, stdout } = await run(`${config.execute} input answer usrout`, {
|
||||||
copyIn: {
|
copyIn: {
|
||||||
usrout: config.user_stdout,
|
usrout: config.user_stdout,
|
||||||
answer: config.output,
|
answer: config.output,
|
||||||
@@ -104,7 +104,7 @@ const checkers: Record<string, Checker> = {
|
|||||||
* argv[6]:输出错误报告的文件
|
* argv[6]:输出错误报告的文件
|
||||||
*/
|
*/
|
||||||
async lemon(config) {
|
async lemon(config) {
|
||||||
const { files } = await run(`\${dir}/checker input usrout answer ${config.score} score message`, {
|
const { files } = await run(`${config.execute} input usrout answer ${config.score} score message`, {
|
||||||
copyIn: {
|
copyIn: {
|
||||||
usrout: config.user_stdout,
|
usrout: config.user_stdout,
|
||||||
answer: config.output,
|
answer: config.output,
|
||||||
@@ -131,7 +131,7 @@ const checkers: Record<string, Checker> = {
|
|||||||
* exit code:返回判断结果
|
* exit code:返回判断结果
|
||||||
*/
|
*/
|
||||||
async qduoj(config) {
|
async qduoj(config) {
|
||||||
const { status, stdout } = await run('${dir}/checker input usrout', {
|
const { status, stdout } = await run(`${config.execute} input usrout`, {
|
||||||
copyIn: {
|
copyIn: {
|
||||||
usrout: config.user_stdout,
|
usrout: config.user_stdout,
|
||||||
input: config.input,
|
input: config.input,
|
||||||
@@ -157,7 +157,7 @@ const checkers: Record<string, Checker> = {
|
|||||||
*/
|
*/
|
||||||
async syzoj(config) {
|
async syzoj(config) {
|
||||||
// eslint-disable-next-line prefer-const
|
// eslint-disable-next-line prefer-const
|
||||||
let { status, stdout, stderr } = await run('${dir}/checker', {
|
let { status, stdout, stderr } = await run(config.execute, {
|
||||||
copyIn: {
|
copyIn: {
|
||||||
input: config.input,
|
input: config.input,
|
||||||
user_out: config.user_stdout,
|
user_out: config.user_stdout,
|
||||||
@@ -173,7 +173,7 @@ const checkers: Record<string, Checker> = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async testlib(config) {
|
async testlib(config) {
|
||||||
const { stderr, status } = await run('${dir}/checker ${dir}/in ${dir}/user_out ${dir}/answer', {
|
const { stderr, status } = await run(`${config.execute} /w/in /w/user_out /w/answer`, {
|
||||||
copyIn: {
|
copyIn: {
|
||||||
in: config.input,
|
in: config.input,
|
||||||
user_out: config.user_stdout,
|
user_out: config.user_stdout,
|
||||||
|
|||||||
@@ -7,15 +7,15 @@ import { CopyInFile } from './sandbox/interface';
|
|||||||
import { compilerText } from './utils';
|
import { compilerText } from './utils';
|
||||||
|
|
||||||
export = async function compile(
|
export = async function compile(
|
||||||
lang: LangConfig, code: string, target: string, copyIn: Record<string, CopyInFile> = {}, next?: Function,
|
lang: LangConfig, code: string, copyIn: Record<string, CopyInFile> = {}, next?: Function,
|
||||||
): Promise<Execute> {
|
): Promise<Execute> {
|
||||||
target = lang.target || target;
|
const target = lang.target || 'foo';
|
||||||
copyIn[lang.code_file] = { content: code };
|
copyIn[lang.code_file] = { content: code };
|
||||||
if (lang.compile) {
|
if (lang.compile) {
|
||||||
const {
|
const {
|
||||||
status, stdout, stderr, fileIds,
|
status, stdout, stderr, fileIds,
|
||||||
} = await run(
|
} = await run(
|
||||||
lang.compile.replace(/\$\{name\}/g, target),
|
lang.compile,
|
||||||
{ copyIn, copyOutCached: [target] },
|
{ copyIn, copyOutCached: [target] },
|
||||||
);
|
);
|
||||||
if (status !== STATUS.STATUS_ACCEPTED) throw new CompileError({ status, stdout, stderr });
|
if (status !== STATUS.STATUS_ACCEPTED) throw new CompileError({ status, stdout, stderr });
|
||||||
|
|||||||
@@ -112,7 +112,11 @@ class JudgeTask {
|
|||||||
this.stat.read_cases = new Date();
|
this.stat.read_cases = new Date();
|
||||||
this.config = await readCases(
|
this.config = await readCases(
|
||||||
this.folder,
|
this.folder,
|
||||||
{ detail: this.session.config.detail, ...this.config },
|
{
|
||||||
|
detail: this.session.config.detail,
|
||||||
|
isSelfSubmission: this.config.problemOwner === this.request.uid,
|
||||||
|
...this.config,
|
||||||
|
},
|
||||||
{ next: this.next, key: md5(`${this.source}/${getConfig('secret')}`) },
|
{ next: this.next, key: md5(`${this.source}/${getConfig('secret')}`) },
|
||||||
);
|
);
|
||||||
this.stat.judge = new Date();
|
this.stat.judge = new Date();
|
||||||
@@ -243,10 +247,11 @@ export default class Hydro {
|
|||||||
return filePath;
|
return filePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
getLang(name: string) {
|
getLang(name: string, doThrow = true) {
|
||||||
if (this.language[name]) return this.language[name];
|
if (this.language[name]) return this.language[name];
|
||||||
if (name === 'cpp' && this.language.cc) return this.language.cc;
|
if (name === 'cpp' && this.language.cc) return this.language.cc;
|
||||||
throw new SystemError('Unsupported language {0}', [name]);
|
if (doThrow) throw new SystemError('Unsupported language {0}', [name]);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async consume(queue: Queue<any>) {
|
async consume(queue: Queue<any>) {
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
export { default as vj4, default } from './vj4';
|
export { default as vj4 } from './vj4';
|
||||||
export { default as hydro } from './hydro';
|
export { default as hydro, default } from './hydro';
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
export default class VJ4 {
|
export default class VJ4 {
|
||||||
constructor() {
|
constructor() {
|
||||||
console.log('新版本不再支持 VJ4。请降级至 hydrojudge@2.5.21 或迁移到 Hydro。');
|
console.log('新版本不再支持 VJ4。请降级至 hydrojudge@2.5.21 或迁移到 Hydro。');
|
||||||
|
console.log('This version of HydroJudge nolonger supports VJ4. Please downgrade to');
|
||||||
|
console.log('hydrojudge@2.5.21 or migrate to Hydro.');
|
||||||
console.log('详细文档: https://hydro.js.org/ ');
|
console.log('详细文档: https://hydro.js.org/ ');
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ export interface CompileErrorInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface CheckConfig {
|
export interface CheckConfig {
|
||||||
|
execute: string;
|
||||||
checker_type: string;
|
checker_type: string;
|
||||||
input: CopyInFile;
|
input: CopyInFile;
|
||||||
output: CopyInFile;
|
output: CopyInFile;
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ function judgeCase(c: Case, sid: string) {
|
|||||||
const copyOutCached = filename ? [`${filename}.out?`] : [];
|
const copyOutCached = filename ? [`${filename}.out?`] : [];
|
||||||
const stdin = filename ? null : c.input;
|
const stdin = filename ? null : c.input;
|
||||||
const res = await run(
|
const res = await run(
|
||||||
ctx.execute.execute.replace(/\$\{name\}/g, 'code'),
|
ctx.execute.execute,
|
||||||
{
|
{
|
||||||
stdin,
|
stdin,
|
||||||
copyIn,
|
copyIn,
|
||||||
@@ -67,6 +67,7 @@ function judgeCase(c: Case, sid: string) {
|
|||||||
status = STATUS.STATUS_MEMORY_LIMIT_EXCEEDED;
|
status = STATUS.STATUS_MEMORY_LIMIT_EXCEEDED;
|
||||||
} else {
|
} else {
|
||||||
[status, score, message] = await check({
|
[status, score, message] = await check({
|
||||||
|
execute: ctx.checker.execute,
|
||||||
copyIn: ctx.checker.copyIn || {},
|
copyIn: ctx.checker.copyIn || {},
|
||||||
input: { src: c.input },
|
input: { src: c.input },
|
||||||
output: { src: c.output },
|
output: { src: c.output },
|
||||||
@@ -142,15 +143,15 @@ export const judge = async (ctx: Context) => {
|
|||||||
} else throw new CompileError('Language not supported by provided templates');
|
} else throw new CompileError('Language not supported by provided templates');
|
||||||
}
|
}
|
||||||
[ctx.execute, ctx.checker] = await Promise.all([
|
[ctx.execute, ctx.checker] = await Promise.all([
|
||||||
|
compile(
|
||||||
|
ctx.getLang(ctx.lang), ctx.code,
|
||||||
|
Object.fromEntries(
|
||||||
|
(ctx.config.user_extra_files || []).map((i) => [i.split('/').pop(), { src: i }]),
|
||||||
|
),
|
||||||
|
ctx.next,
|
||||||
|
),
|
||||||
(() => {
|
(() => {
|
||||||
const copyIn = {};
|
if (['default', 'strict'].includes(ctx.config.checker_type || 'default')) {
|
||||||
for (const file of ctx.config.user_extra_files) {
|
|
||||||
copyIn[parseFilename(file)] = { src: file };
|
|
||||||
}
|
|
||||||
return compile(ctx.getLang(ctx.lang), ctx.code, 'code', copyIn, ctx.next);
|
|
||||||
})(),
|
|
||||||
(() => {
|
|
||||||
if (!ctx.config.checker_type || ['default', 'strict'].includes(ctx.config.checker_type)) {
|
|
||||||
return { execute: '', copyIn: {}, clean: () => Promise.resolve(null) };
|
return { execute: '', copyIn: {}, clean: () => Promise.resolve(null) };
|
||||||
}
|
}
|
||||||
const copyIn = {};
|
const copyIn = {};
|
||||||
|
|||||||
@@ -1,132 +0,0 @@
|
|||||||
// Not implemented
|
|
||||||
import path from 'path';
|
|
||||||
import fs from 'fs-extra';
|
|
||||||
import { STATUS } from '@hydrooj/utils/lib/status';
|
|
||||||
import { check, compileChecker } from '../check';
|
|
||||||
import compile from '../compile';
|
|
||||||
import { CompileError } from '../error';
|
|
||||||
import { run } from '../sandbox';
|
|
||||||
import signals from '../signals';
|
|
||||||
import { copyInDir, parseFilename } from '../utils';
|
|
||||||
import { Context } from './interface';
|
|
||||||
|
|
||||||
export const judge = async (ctx: Context) => {
|
|
||||||
if (ctx.config.template) {
|
|
||||||
if (ctx.config.template[ctx.lang]) {
|
|
||||||
const tpl = ctx.config.template[ctx.lang];
|
|
||||||
ctx.code = tpl[0] + ctx.code + tpl[1];
|
|
||||||
} else throw new CompileError('Language not supported by provided templates');
|
|
||||||
}
|
|
||||||
ctx.next({ status: STATUS.STATUS_COMPILING });
|
|
||||||
if (!ctx.config.validator || !ctx.config.std) throw new CompileError('config.validator or config.std is missing.');
|
|
||||||
const [execute, executeValidator, executeStd, executeChecker] = await Promise.all([
|
|
||||||
// UserProgram
|
|
||||||
(() => {
|
|
||||||
const copyIn = {};
|
|
||||||
for (const file of ctx.config.user_extra_files) {
|
|
||||||
copyIn[parseFilename(file)] = { src: file };
|
|
||||||
}
|
|
||||||
return compile(ctx.getLang(ctx.lang), ctx.code, 'code', copyIn, ctx.next);
|
|
||||||
})(),
|
|
||||||
// Validator
|
|
||||||
(async () => {
|
|
||||||
const copyIn = {};
|
|
||||||
for (const file of ctx.config.judge_extra_files) {
|
|
||||||
copyIn[parseFilename(file)] = { src: file };
|
|
||||||
}
|
|
||||||
const file = await fs.readFile(ctx.config.validator);
|
|
||||||
return await compile(ctx.getLang(parseFilename(ctx.config.validator).split('.')[1]), file.toString(), 'validator', copyIn);
|
|
||||||
})(),
|
|
||||||
// Std
|
|
||||||
(async () => {
|
|
||||||
const copyIn = {};
|
|
||||||
for (const file of ctx.config.user_extra_files) {
|
|
||||||
copyIn[parseFilename(file)] = { src: file };
|
|
||||||
}
|
|
||||||
const file = await fs.readFile(ctx.config.std);
|
|
||||||
return await compile(ctx.getLang(parseFilename(ctx.config.std).split('.')[1]), file.toString(), 'std', copyIn);
|
|
||||||
})(),
|
|
||||||
// Checker
|
|
||||||
(() => {
|
|
||||||
if (!ctx.config.checker_type || ctx.config.checker_type === 'default') return null;
|
|
||||||
const copyIn = {};
|
|
||||||
for (const file of ctx.config.judge_extra_files) {
|
|
||||||
copyIn[parseFilename(file)] = { src: file };
|
|
||||||
}
|
|
||||||
return compileChecker(
|
|
||||||
ctx.getLang,
|
|
||||||
ctx.config.checker_type,
|
|
||||||
ctx.config.checker,
|
|
||||||
copyIn,
|
|
||||||
);
|
|
||||||
})(),
|
|
||||||
]);
|
|
||||||
ctx.clean.push(execute.clean, executeValidator.clean, executeStd.clean, executeChecker.clean);
|
|
||||||
ctx.next({ status: STATUS.STATUS_JUDGING, progress: 0 });
|
|
||||||
const { filename } = ctx.config;
|
|
||||||
const input = path.resolve(ctx.tmpdir, 'input');
|
|
||||||
await fs.writeFile(input, ctx.config.hack);
|
|
||||||
if (filename) execute.copyIn[`${filename}.in`] = { src: input };
|
|
||||||
const copyOut = filename ? [`${filename}.out`] : [];
|
|
||||||
const stdin = filename ? null : input;
|
|
||||||
const stdout = path.resolve(ctx.tmpdir, 'user.out');
|
|
||||||
const stderr = path.resolve(ctx.tmpdir, 'user.err');
|
|
||||||
const res = await run(
|
|
||||||
ctx.execute.execute.replace(/\$\{name\}/g, 'code'),
|
|
||||||
{
|
|
||||||
stdin,
|
|
||||||
stdout: filename ? null : stdout,
|
|
||||||
stderr,
|
|
||||||
copyIn: execute.copyIn,
|
|
||||||
copyOut,
|
|
||||||
time: 1000,
|
|
||||||
memory: 512,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
const { code, time_usage_ms, memory_usage_kb } = res;
|
|
||||||
let { status } = res;
|
|
||||||
if (res.files[`${filename}.out`] || !fs.existsSync(stdout)) {
|
|
||||||
fs.writeFileSync(stdout, res.files[`${filename}.out`] || '');
|
|
||||||
}
|
|
||||||
let message = '';
|
|
||||||
if (status === STATUS.STATUS_ACCEPTED) {
|
|
||||||
if (time_usage_ms > 1000) {
|
|
||||||
status = STATUS.STATUS_TIME_LIMIT_EXCEEDED;
|
|
||||||
} else if (memory_usage_kb > 512 * 1024) {
|
|
||||||
status = STATUS.STATUS_MEMORY_LIMIT_EXCEEDED;
|
|
||||||
} else {
|
|
||||||
[status, , message] = await check({
|
|
||||||
copyIn: copyInDir(path.resolve(ctx.tmpdir, 'checker')),
|
|
||||||
input: { src: input },
|
|
||||||
output: { src: stdout },
|
|
||||||
user_stdout: { src: stdout },
|
|
||||||
user_stderr: { src: stderr },
|
|
||||||
checker_type: ctx.config.checker_type,
|
|
||||||
score: 100,
|
|
||||||
detail: ctx.config.detail,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else if (status === STATUS.STATUS_RUNTIME_ERROR && code) {
|
|
||||||
if (code < 32) message = signals[code];
|
|
||||||
else message = `您的程序返回了 ${code}.`;
|
|
||||||
}
|
|
||||||
ctx.next({
|
|
||||||
status: STATUS.STATUS_JUDGING,
|
|
||||||
case: {
|
|
||||||
status,
|
|
||||||
score: 0,
|
|
||||||
time_ms: time_usage_ms,
|
|
||||||
memory_kb: memory_usage_kb,
|
|
||||||
message,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
ctx.stat.done = new Date();
|
|
||||||
ctx.next({ message: JSON.stringify(ctx.stat) });
|
|
||||||
ctx.end({
|
|
||||||
status,
|
|
||||||
score: ctx.total_score,
|
|
||||||
time_ms: Math.floor(ctx.total_time_usage_ms * 1000000) / 1000000,
|
|
||||||
memory_kb: ctx.total_memory_usage_kb,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
@@ -1,10 +1,9 @@
|
|||||||
import * as def from './default';
|
import * as def from './default';
|
||||||
import * as hack from './hack';
|
|
||||||
import * as interactive from './interactive';
|
import * as interactive from './interactive';
|
||||||
import { Context } from './interface';
|
import { Context } from './interface';
|
||||||
import * as run from './run';
|
import * as run from './run';
|
||||||
import * as submit_answer from './submit_answer';
|
import * as submit_answer from './submit_answer';
|
||||||
|
|
||||||
export = {
|
export = {
|
||||||
default: def, interactive, run, submit_answer, hack, objective: submit_answer,
|
default: def, interactive, run, submit_answer, objective: submit_answer,
|
||||||
} as Record<string, { judge(ctx: Context): Promise<void> }>;
|
} as Record<string, { judge(ctx: Context): Promise<void> }>;
|
||||||
@@ -24,13 +24,13 @@ function judgeCase(c: Case) {
|
|||||||
ctx.executeInteractor.copyIn.out = c.output ? { src: c.output } : { content: '' };
|
ctx.executeInteractor.copyIn.out = c.output ? { src: c.output } : { content: '' };
|
||||||
const [{ code, time_usage_ms, memory_usage_kb }, resInteractor] = await runPiped(
|
const [{ code, time_usage_ms, memory_usage_kb }, resInteractor] = await runPiped(
|
||||||
{
|
{
|
||||||
execute: ctx.executeUser.execute.replace(/\$\{name\}/g, 'code'),
|
execute: ctx.executeUser.execute,
|
||||||
copyIn: ctx.executeUser.copyIn,
|
copyIn: ctx.executeUser.copyIn,
|
||||||
time: ctxSubtask.subtask.time * ctx.executeUser.time,
|
time: ctxSubtask.subtask.time * ctx.executeUser.time,
|
||||||
memory: ctxSubtask.subtask.memory,
|
memory: ctxSubtask.subtask.memory,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
execute: `${ctx.executeInteractor.execute.replace(/\$\{name\}/g, 'interactor')} /w/in /w/tout /w/out`,
|
execute: `${ctx.executeInteractor.execute} /w/in /w/tout /w/out`,
|
||||||
copyIn: ctx.executeInteractor.copyIn,
|
copyIn: ctx.executeInteractor.copyIn,
|
||||||
time: ctxSubtask.subtask.time * 2 * ctx.executeInteractor.time,
|
time: ctxSubtask.subtask.time * 2 * ctx.executeInteractor.time,
|
||||||
memory: ctxSubtask.subtask.memory * 2,
|
memory: ctxSubtask.subtask.memory * 2,
|
||||||
@@ -103,7 +103,7 @@ export const judge = async (ctx: Context) => {
|
|||||||
for (const file of ctx.config.user_extra_files) {
|
for (const file of ctx.config.user_extra_files) {
|
||||||
copyIn[parseFilename(file)] = { src: file };
|
copyIn[parseFilename(file)] = { src: file };
|
||||||
}
|
}
|
||||||
return compile(ctx.getLang(ctx.lang), ctx.code, 'code', copyIn, ctx.next);
|
return compile(ctx.getLang(ctx.lang), ctx.code, copyIn, ctx.next);
|
||||||
})(),
|
})(),
|
||||||
(() => {
|
(() => {
|
||||||
const copyIn = { 'testlib.h': { src: testlibSrc } };
|
const copyIn = { 'testlib.h': { src: testlibSrc } };
|
||||||
@@ -113,7 +113,6 @@ export const judge = async (ctx: Context) => {
|
|||||||
return compile(
|
return compile(
|
||||||
ctx.getLang(parseFilename(ctx.config.interactor).split('.')[1].replace('@', '.')),
|
ctx.getLang(parseFilename(ctx.config.interactor).split('.')[1].replace('@', '.')),
|
||||||
fs.readFileSync(ctx.config.interactor).toString(),
|
fs.readFileSync(ctx.config.interactor).toString(),
|
||||||
'interactor',
|
|
||||||
copyIn,
|
copyIn,
|
||||||
);
|
);
|
||||||
})(),
|
})(),
|
||||||
|
|||||||
@@ -8,46 +8,32 @@ import signals from '../signals';
|
|||||||
import { compilerText, parseMemoryMB, parseTimeMS } from '../utils';
|
import { compilerText, parseMemoryMB, parseTimeMS } from '../utils';
|
||||||
import { Context } from './interface';
|
import { Context } from './interface';
|
||||||
|
|
||||||
|
const failure = (status: number, message?: string) => ({
|
||||||
|
status,
|
||||||
|
score: 0,
|
||||||
|
time_ms: 0,
|
||||||
|
memory_kb: 0,
|
||||||
|
message,
|
||||||
|
});
|
||||||
|
|
||||||
export const judge = async (ctx: Context) => {
|
export const judge = async (ctx: Context) => {
|
||||||
ctx.stat.judge = new Date();
|
ctx.stat.judge = new Date();
|
||||||
ctx.next({ status: STATUS.STATUS_COMPILING });
|
ctx.next({ status: STATUS.STATUS_COMPILING });
|
||||||
try {
|
try {
|
||||||
ctx.execute = await compile(ctx.getLang(ctx.lang), ctx.code, 'code', {}, ctx.next);
|
ctx.execute = await compile(ctx.getLang(ctx.lang), ctx.code, {}, ctx.next);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof CompileError) {
|
if (e instanceof CompileError) {
|
||||||
ctx.next({
|
ctx.next({
|
||||||
status: STATUS.STATUS_COMPILE_ERROR,
|
status: STATUS.STATUS_COMPILE_ERROR,
|
||||||
case: {
|
case: failure(STATUS.STATUS_COMPILE_ERROR, compilerText(e.stdout, e.stderr)),
|
||||||
status: STATUS.STATUS_COMPILE_ERROR,
|
|
||||||
score: 0,
|
|
||||||
time_ms: 0,
|
|
||||||
memory_kb: 0,
|
|
||||||
message: compilerText(e.stdout, e.stderr),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
ctx.end({
|
|
||||||
status: STATUS.STATUS_COMPILE_ERROR,
|
|
||||||
score: 0,
|
|
||||||
time_ms: 0,
|
|
||||||
memory_kb: 0,
|
|
||||||
});
|
});
|
||||||
|
ctx.end(failure(STATUS.STATUS_COMPILE_ERROR));
|
||||||
} else {
|
} else {
|
||||||
ctx.next({
|
ctx.next({
|
||||||
status: STATUS.STATUS_SYSTEM_ERROR,
|
status: STATUS.STATUS_SYSTEM_ERROR,
|
||||||
case: {
|
case: failure(STATUS.STATUS_SYSTEM_ERROR, `${e.message}\n${JSON.stringify(e.params)}`),
|
||||||
status: STATUS.STATUS_SYSTEM_ERROR,
|
|
||||||
score: 0,
|
|
||||||
time_ms: 0,
|
|
||||||
memory_kb: 0,
|
|
||||||
message: `${e.message}\n${JSON.stringify(e.params)}`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
ctx.end({
|
|
||||||
status: STATUS.STATUS_COMPILE_ERROR,
|
|
||||||
score: 0,
|
|
||||||
time_ms: 0,
|
|
||||||
memory_kb: 0,
|
|
||||||
});
|
});
|
||||||
|
ctx.end(failure(STATUS.STATUS_SYSTEM_ERROR));
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -62,7 +48,7 @@ export const judge = async (ctx: Context) => {
|
|||||||
const stdout = path.resolve(ctx.tmpdir, '0.out');
|
const stdout = path.resolve(ctx.tmpdir, '0.out');
|
||||||
const stderr = path.resolve(ctx.tmpdir, '0.err');
|
const stderr = path.resolve(ctx.tmpdir, '0.err');
|
||||||
const res = await run(
|
const res = await run(
|
||||||
ctx.execute.execute.replace(/\$\{name\}/g, 'code'),
|
ctx.execute.execute,
|
||||||
{
|
{
|
||||||
stdin: filename ? null : stdin,
|
stdin: filename ? null : stdin,
|
||||||
stdout: filename ? null : stdout,
|
stdout: filename ? null : stdout,
|
||||||
@@ -96,7 +82,7 @@ export const judge = async (ctx: Context) => {
|
|||||||
status,
|
status,
|
||||||
time_ms: time_usage_ms,
|
time_ms: time_usage_ms,
|
||||||
memory_kb: memory_usage_kb,
|
memory_kb: memory_usage_kb,
|
||||||
message: message.join('\n').substr(0, 102400),
|
message: message.join('\n').substring(0, 102400),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
ctx.stat.done = new Date();
|
ctx.stat.done = new Date();
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ function checkStringArray(args: ParseEntry[]): args is string[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function parseArgs(execute: string): string[] {
|
function parseArgs(execute: string): string[] {
|
||||||
const args = cmd(execute.replace(/\$\{dir\}/g, '/w'));
|
const args = cmd(execute);
|
||||||
if (!checkStringArray(args)) {
|
if (!checkStringArray(args)) {
|
||||||
throw new SystemError(`${execute} contains invalid operator`);
|
throw new SystemError(`${execute} contains invalid operator`);
|
||||||
}
|
}
|
||||||
@@ -76,7 +76,7 @@ function proc({
|
|||||||
env = {},
|
env = {},
|
||||||
}: Parameter = {}): Cmd {
|
}: Parameter = {}): Cmd {
|
||||||
if (!supportOptional) {
|
if (!supportOptional) {
|
||||||
copyOut = (copyOut as string[]).map((i) => (i.endsWith('?') ? i.substr(0, i.length - 1) : i));
|
copyOut = copyOut.map((i) => (i.endsWith('?') ? i.substring(0, i.length - 1) : i));
|
||||||
}
|
}
|
||||||
const size = parseMemoryMB(getConfig('stdio_size'));
|
const size = parseMemoryMB(getConfig('stdio_size'));
|
||||||
const rate = getConfig('rate');
|
const rate = getConfig('rate');
|
||||||
|
|||||||
@@ -134,10 +134,11 @@ async function postInit() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function getLang(lang: string) {
|
function getLang(lang: string, doThrow = true) {
|
||||||
if (setting.langs[lang]) return setting.langs[lang];
|
if (setting.langs[lang]) return setting.langs[lang];
|
||||||
if (lang === 'cpp' && setting.langs['cc']) return setting.langs['cc'];
|
if (lang === 'cpp' && setting.langs['cc']) return setting.langs['cc'];
|
||||||
throw new SystemError('Unsupported language {0}.', [lang]);
|
if (doThrow) throw new SystemError('Unsupported language {0}.', [lang]);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
class JudgeTask {
|
class JudgeTask {
|
||||||
|
|||||||
@@ -35,10 +35,10 @@ int main(){
|
|||||||
},
|
},
|
||||||
end: () => { },
|
end: () => { },
|
||||||
getLang: () => ({
|
getLang: () => ({
|
||||||
compile: '/usr/bin/g++ -Wall -std=c++14 -o foo.cc foo.cc -lm',
|
compile: '/usr/bin/g++ -Wall -std=c++14 -o foo foo.cc -lm',
|
||||||
execute: 'foo',
|
execute: '/w/foo',
|
||||||
code_file: 'foo.cc',
|
code_file: 'foo.cc',
|
||||||
highlight: 'cpp astyle-c',
|
highlight: 'cpp',
|
||||||
monaco: 'cpp',
|
monaco: 'cpp',
|
||||||
display: 'C++',
|
display: 'C++',
|
||||||
time_limit_rate: 1,
|
time_limit_rate: 1,
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ const encrypt = (algorithm: string, content: crypto.BinaryLike) => {
|
|||||||
return hash.digest('hex');
|
return hash.digest('hex');
|
||||||
};
|
};
|
||||||
|
|
||||||
export const sha1 = (content: string) => encrypt('sha1', content);
|
|
||||||
export const md5 = (content: string) => encrypt('md5', content);
|
export const md5 = (content: string) => encrypt('md5', content);
|
||||||
|
|
||||||
export class Queue<T> extends EventEmitter {
|
export class Queue<T> extends EventEmitter {
|
||||||
@@ -69,26 +68,11 @@ export namespace Lock {
|
|||||||
|
|
||||||
export function compilerText(stdout: string, stderr: string) {
|
export function compilerText(stdout: string, stderr: string) {
|
||||||
const ret = [];
|
const ret = [];
|
||||||
if (!EMPTY_STR.test(stdout)) ret.push(stdout.substr(0, 1024 * 1024));
|
if (!EMPTY_STR.test(stdout)) ret.push(stdout.substring(0, 1024 * 1024));
|
||||||
if (!EMPTY_STR.test(stderr)) ret.push(stderr.substr(0, 1024 * 1024));
|
if (!EMPTY_STR.test(stderr)) ret.push(stderr.substring(0, 1024 * 1024));
|
||||||
return ret.join('\n');
|
return ret.join('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function copyInDir(dir: string) {
|
|
||||||
const files = {};
|
|
||||||
if (fs.existsSync(dir)) {
|
|
||||||
fs.readdirSync(dir).forEach((f1) => {
|
|
||||||
const p1 = `${dir}/${f1}`;
|
|
||||||
if (fs.statSync(p1).isDirectory()) {
|
|
||||||
fs.readdirSync(p1).forEach((f2) => {
|
|
||||||
files[`${f1}/${f2}`] = { src: `${dir}/${f1}/${f2}` };
|
|
||||||
});
|
|
||||||
} else files[f1] = { src: `${dir}/${f1}` };
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return files;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function restrictFile(p: string) {
|
export function restrictFile(p: string) {
|
||||||
if (!p) return '/';
|
if (!p) return '/';
|
||||||
if (p[0] === '/') p = '';
|
if (p[0] === '/') p = '';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "hydrooj",
|
"name": "hydrooj",
|
||||||
"version": "3.4.31",
|
"version": "3.5.0",
|
||||||
"bin": "bin/hydrooj.js",
|
"bin": "bin/hydrooj.js",
|
||||||
"main": "src/loader",
|
"main": "src/loader",
|
||||||
"module": "src/loader",
|
"module": "src/loader",
|
||||||
|
|||||||
@@ -32,33 +32,33 @@ langs:
|
|||||||
target: Compiler output file
|
target: Compiler output file
|
||||||
display: Display name
|
display: Display name
|
||||||
c:
|
c:
|
||||||
compile: /usr/bin/gcc -Wall --std=c99 -o ${name} foo.c -lm
|
compile: /usr/bin/gcc -Wall --std=c99 -o foo foo.c -lm
|
||||||
highlight: c astyle-c
|
highlight: c
|
||||||
monaco: cpp
|
monaco: cpp
|
||||||
display: C
|
display: C
|
||||||
cc:
|
cc:
|
||||||
compile: /usr/bin/g++ -Wall -std=c++14 -o ${name} foo.cc -lm
|
compile: /usr/bin/g++ -Wall -std=c++14 -o foo foo.cc -lm
|
||||||
code_file: foo.cc
|
code_file: foo.cc
|
||||||
highlight: cpp astyle-c
|
highlight: cpp
|
||||||
monaco: cpp
|
monaco: cpp
|
||||||
display: C++
|
display: C++
|
||||||
cc.std98:
|
cc.std98:
|
||||||
compile: /usr/bin/g++ -Wall -std=c++98 -o ${name} foo.cc -lm
|
compile: /usr/bin/g++ -Wall -std=c++98 -o foo foo.cc -lm
|
||||||
display: C++ 98
|
display: C++ 98
|
||||||
cc.std11:
|
cc.std11:
|
||||||
compile: /usr/bin/g++ -Wall -std=c++11 -o ${name} foo.cc -lm
|
compile: /usr/bin/g++ -Wall -std=c++11 -o foo foo.cc -lm
|
||||||
display: C++ 11
|
display: C++ 11
|
||||||
cc.std14:
|
cc.std14:
|
||||||
display: C++ 14
|
display: C++ 14
|
||||||
cc.std17:
|
cc.std17:
|
||||||
compile: /usr/bin/g++ -Wall -std=c++17 -o ${name} foo.cc -lm
|
compile: /usr/bin/g++ -Wall -std=c++17 -o foo foo.cc -lm
|
||||||
display: C++ 17
|
display: C++ 17
|
||||||
pas:
|
pas:
|
||||||
compile: /usr/bin/fpc -O2 -o${dir}/${name} foo.pas
|
compile: /usr/bin/fpc -O2 -o/w/foo foo.pas
|
||||||
highlight: pascal
|
highlight: pascal
|
||||||
display: Pascal
|
display: Pascal
|
||||||
java:
|
java:
|
||||||
compile: /usr/bin/bash -c "javac -d ${dir} -encoding utf8 ./Main.java && jar cvf Main.jar *.class >/dev/null"
|
compile: /usr/bin/bash -c "javac -d /w -encoding utf8 ./Main.java && jar cvf Main.jar *.class >/dev/null"
|
||||||
code_file: Main.java
|
code_file: Main.java
|
||||||
target: Main.jar
|
target: Main.jar
|
||||||
execute: /usr/bin/java -cp Main.jar Main
|
execute: /usr/bin/java -cp Main.jar Main
|
||||||
@@ -67,46 +67,42 @@ langs:
|
|||||||
monaco: java
|
monaco: java
|
||||||
display: Java
|
display: Java
|
||||||
py:
|
py:
|
||||||
compile: /usr/bin/python -c "import py_compile; py_compile.compile('${dir}/foo.py', '${dir}/${name}', doraise=True)"
|
compile: /usr/bin/python -c "import py_compile; py_compile.compile('/w/foo.py', '/w/foo', doraise=True)"
|
||||||
code_file: foo.py
|
code_file: foo.py
|
||||||
execute: /usr/bin/python ${name}
|
execute: /usr/bin/python foo
|
||||||
highlight: python
|
highlight: python
|
||||||
display: Python
|
display: Python
|
||||||
py.py2:
|
py.py2:
|
||||||
display: Python 2
|
display: Python 2
|
||||||
py.py3:
|
py.py3:
|
||||||
compile: /usr/bin/python3 -c "import py_compile; py_compile.compile('${dir}/foo.py', '${dir}/${name}', doraise=True)"
|
compile: /usr/bin/python3 -c "import py_compile; py_compile.compile('/w/foo.py', '/w/foo', doraise=True)"
|
||||||
execute: /usr/bin/python3 ${name}
|
execute: /usr/bin/python3 foo
|
||||||
display: Python 3
|
display: Python 3
|
||||||
php:
|
php:
|
||||||
execute: /usr/bin/php foo.php
|
execute: /usr/bin/php foo.php
|
||||||
display: PHP
|
display: PHP
|
||||||
rs:
|
rs:
|
||||||
compile: /usr/bin/rustc -O -o ${dir}/${name} ${dir}/foo.rs
|
compile: /usr/bin/rustc -O -o /w/foo /w/foo.rs
|
||||||
highlight: rust
|
highlight: rust
|
||||||
display: Rust
|
display: Rust
|
||||||
hs:
|
hs:
|
||||||
compile: /usr/bin/ghc -O -outputdir /tmp -o ${name} foo.hs
|
compile: /usr/bin/ghc -O -outputdir /tmp -o foo foo.hs
|
||||||
highlight: haskell
|
highlight: haskell
|
||||||
display: Haskell
|
display: Haskell
|
||||||
js:
|
js:
|
||||||
execute: /usr/bin/jsc ${dir}/foo.js
|
execute: /usr/bin/jsc /w/foo.js
|
||||||
highlight: javascript
|
highlight: javascript
|
||||||
display: Javascript (JSC)
|
display: Javascript (JSC)
|
||||||
go:
|
go:
|
||||||
compile: /usr/bin/go build -o ${name} foo.go
|
compile: /usr/bin/go build -o foo foo.go
|
||||||
display: Golang
|
display: Golang
|
||||||
rb:
|
rb:
|
||||||
execute: /usr/bin/ruby foo.rb
|
execute: /usr/bin/ruby foo.rb
|
||||||
highlight: ruby
|
highlight: ruby
|
||||||
display: Ruby
|
display: Ruby
|
||||||
cs:
|
cs:
|
||||||
compile: /usr/bin/mcs -optimize+ -out:${dir}/${name} ${dir}/foo.cs
|
compile: /usr/bin/mcs -optimize+ -out:/w/foo /w/foo.cs
|
||||||
execute: /usr/bin/mono ${name}
|
execute: /usr/bin/mono foo
|
||||||
highlight: csharp astyle-cs
|
highlight: csharp
|
||||||
monaco: csharp
|
monaco: csharp
|
||||||
display: Csharp
|
display: 'C#'
|
||||||
jl:
|
|
||||||
execute: /usr/bin/julia ${dir}/foo.jl
|
|
||||||
highlight: julia
|
|
||||||
display: Julia
|
|
||||||
@@ -72,6 +72,7 @@ class RecordModel {
|
|||||||
pdoc = await problem.get(pdoc.reference.domainId, pdoc.reference.pid);
|
pdoc = await problem.get(pdoc.reference.domainId, pdoc.reference.pid);
|
||||||
if (!pdoc) throw new ProblemNotFoundError(rdoc.domainId, rdoc.pid);
|
if (!pdoc) throw new ProblemNotFoundError(rdoc.domainId, rdoc.pid);
|
||||||
}
|
}
|
||||||
|
(config as any).problemOwner = pdoc.owner;
|
||||||
source = `${pdoc.domainId}/${pdoc.docId}`;
|
source = `${pdoc.domainId}/${pdoc.docId}`;
|
||||||
data = pdoc.data;
|
data = pdoc.data;
|
||||||
if (typeof pdoc.config === 'string') throw new Error(pdoc.config);
|
if (typeof pdoc.config === 'string') throw new Error(pdoc.config);
|
||||||
|
|||||||
@@ -683,6 +683,10 @@ const scripts: UpgradeScript[] = [
|
|||||||
await TaskModel.deleteMany({ type: 'schedule', subType: 'contest.problemHide' });
|
await TaskModel.deleteMany({ type: 'schedule', subType: 'contest.problemHide' });
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
async function _59_60() {
|
||||||
|
const langs = await system.get('hydrooj.langs');
|
||||||
|
await system.set('hydrooj.langs', langs.replace(/\$\{dir\}/g, '/w').replace(/\$\{name\}/g, 'foo'));
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export default scripts;
|
export default scripts;
|
||||||
Reference in new issue
Block a user