mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 09:46:28 +08:00
judge: bug fix
This commit is contained in:
7 files changed
+55
-101
No files matched your search
@@ -1,7 +1,6 @@
|
|||||||
import { LangConfig } from '@hydrooj/utils/lib/lang';
|
import { LangConfig } from '@hydrooj/utils/lib/lang';
|
||||||
import { STATUS } from '@hydrooj/utils/lib/status';
|
import { STATUS } from '@hydrooj/utils/lib/status';
|
||||||
import { findFileSync } from '@hydrooj/utils/lib/utils';
|
import { findFileSync } from '@hydrooj/utils/lib/utils';
|
||||||
import checkers from './checkers';
|
|
||||||
import { CompileError, FormatError } from './error';
|
import { CompileError, FormatError } from './error';
|
||||||
import { Execute } from './interface';
|
import { Execute } from './interface';
|
||||||
import {
|
import {
|
||||||
@@ -51,7 +50,10 @@ const testlibFile = {
|
|||||||
src: findFileSync('@hydrooj/hydrojudge/vendor/testlib/testlib.h'),
|
src: findFileSync('@hydrooj/hydrojudge/vendor/testlib/testlib.h'),
|
||||||
};
|
};
|
||||||
|
|
||||||
async function _compile(src: string, type: 'checker' | 'validator' | 'interactor', getLang, copyIn, withTestlib = true, next?: any) {
|
export async function compileWithTestlib(
|
||||||
|
src: string, type: 'checker' | 'validator' | 'interactor',
|
||||||
|
getLang, copyIn: CopyIn, withTestlib = true, next?: any,
|
||||||
|
) {
|
||||||
const s = src.replace('@', '.').split('.');
|
const s = src.replace('@', '.').split('.');
|
||||||
let lang;
|
let lang;
|
||||||
let langId = s.pop();
|
let langId = s.pop();
|
||||||
@@ -65,19 +67,3 @@ async function _compile(src: string, type: 'checker' | 'validator' | 'interactor
|
|||||||
// TODO cache compiled binary
|
// TODO cache compiled binary
|
||||||
return await compile(lang, { src }, copyIn, next);
|
return await compile(lang, { src }, copyIn, next);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function compileChecker(getLang: Function, checkerType: string, checker: string, copyIn: CopyIn, next?: any) {
|
|
||||||
if (['default', 'strict'].includes(checkerType)) {
|
|
||||||
return { execute: '', copyIn: {}, clean: () => Promise.resolve(null) };
|
|
||||||
}
|
|
||||||
if (!checkers[checkerType]) throw new FormatError('Unknown checker type {0}.', [checkerType]);
|
|
||||||
return _compile(checker, 'checker', getLang, copyIn, checkerType === 'testlib', next);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function compileInteractor(getLang: Function, interactor: string, copyIn: CopyIn) {
|
|
||||||
return _compile(interactor, 'interactor', getLang, copyIn);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function compileValidator(getLang: Function, validator: string, copyIn: CopyIn) {
|
|
||||||
return _compile(validator, 'validator', getLang, copyIn);
|
|
||||||
}
|
|
||||||
@@ -1,9 +1,6 @@
|
|||||||
import { basename } from 'path';
|
|
||||||
import { STATUS } from '@hydrooj/utils/lib/status';
|
import { STATUS } from '@hydrooj/utils/lib/status';
|
||||||
import checkers from '../checkers';
|
import checkers from '../checkers';
|
||||||
import compile, { compileChecker } from '../compile';
|
|
||||||
import { runFlow } from '../flow';
|
import { runFlow } from '../flow';
|
||||||
import { Execute } from '../interface';
|
|
||||||
import { Logger } from '../log';
|
import { Logger } from '../log';
|
||||||
import { del, runQueued } from '../sandbox';
|
import { del, runQueued } from '../sandbox';
|
||||||
import signals from '../signals';
|
import signals from '../signals';
|
||||||
@@ -101,30 +98,9 @@ function judgeCase(c: NormalizedCase) {
|
|||||||
|
|
||||||
export const judge = async (ctx: Context) => await runFlow(ctx, {
|
export const judge = async (ctx: Context) => await runFlow(ctx, {
|
||||||
compile: async () => {
|
compile: async () => {
|
||||||
const markCleanup = (i: Execute) => {
|
|
||||||
ctx.clean.push(i.clean);
|
|
||||||
return i;
|
|
||||||
};
|
|
||||||
[ctx.execute, ctx.checker] = await Promise.all([
|
[ctx.execute, ctx.checker] = await Promise.all([
|
||||||
compile(
|
ctx.compile(ctx.lang, ctx.code),
|
||||||
ctx.session.getLang(ctx.lang), ctx.code,
|
ctx.compileWithTestlib('checker', ctx.config.checker, ctx.config.checker_type),
|
||||||
Object.fromEntries(
|
|
||||||
(ctx.config.user_extra_files || []).map((i) => [i.split('/').pop(), { src: i }]),
|
|
||||||
),
|
|
||||||
ctx.next,
|
|
||||||
).then(markCleanup),
|
|
||||||
compileChecker(
|
|
||||||
ctx.session.getLang,
|
|
||||||
ctx.config.checker_type,
|
|
||||||
ctx.config.checker,
|
|
||||||
{
|
|
||||||
user_code: ctx.code,
|
|
||||||
...Object.fromEntries(
|
|
||||||
(ctx.config.judge_extra_files || []).map((i) => [basename(i), { src: i }]),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
ctx.next,
|
|
||||||
).then(markCleanup),
|
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
judgeCase,
|
judgeCase,
|
||||||
|
|||||||
@@ -1,29 +1,18 @@
|
|||||||
/* eslint-disable no-sequences */
|
/* eslint-disable no-sequences */
|
||||||
import { basename } from 'path';
|
|
||||||
import { fs } from '@hydrooj/utils';
|
import { fs } from '@hydrooj/utils';
|
||||||
import { STATUS } from '@hydrooj/utils/lib/status';
|
import { STATUS } from '@hydrooj/utils/lib/status';
|
||||||
import checkers from '../checkers';
|
import checkers from '../checkers';
|
||||||
import compile, { compileChecker, compileValidator } from '../compile';
|
|
||||||
import { Execute } from '../interface';
|
|
||||||
import { del, runQueued } from '../sandbox';
|
import { del, runQueued } from '../sandbox';
|
||||||
import signals from '../signals';
|
import signals from '../signals';
|
||||||
import { parseMemoryMB, parseTimeMS } from '../utils';
|
import { parseMemoryMB, parseTimeMS } from '../utils';
|
||||||
import { Context } from './interface';
|
import { Context } from './interface';
|
||||||
|
|
||||||
export async function judge(ctx: Context) {
|
export async function judge(ctx: Context) {
|
||||||
const { config, session } = ctx;
|
|
||||||
ctx.next({ status: STATUS.STATUS_COMPILING, progress: 0 });
|
ctx.next({ status: STATUS.STATUS_COMPILING, progress: 0 });
|
||||||
const userExtraFiles = Object.fromEntries(
|
|
||||||
(config.user_extra_files || []).map((i) => [basename(i), { src: i }]),
|
|
||||||
);
|
|
||||||
const judgeExtraFiles = Object.fromEntries(
|
|
||||||
(config.judge_extra_files || []).map((i) => [basename(i), { src: i }]),
|
|
||||||
);
|
|
||||||
const markCleanup = (i: Execute) => (ctx.clean.push(i.clean), i);
|
|
||||||
const [execute, checker, validator, input] = await Promise.all([
|
const [execute, checker, validator, input] = await Promise.all([
|
||||||
compile(session.getLang(ctx.lang), ctx.code, userExtraFiles, ctx.next).then(markCleanup),
|
ctx.compile(ctx.lang, ctx.code),
|
||||||
compileChecker(session.getLang, config.checker_type, config.checker, judgeExtraFiles).then(markCleanup),
|
ctx.compileWithTestlib('checker', ctx.config.checker, ctx.config.checker_type),
|
||||||
compileValidator(session.getLang, config.validator, judgeExtraFiles).then(markCleanup),
|
ctx.compileWithTestlib('validator', ctx.config.validator),
|
||||||
ctx.session.fetchFile(ctx.files.hack),
|
ctx.session.fetchFile(ctx.files.hack),
|
||||||
]);
|
]);
|
||||||
ctx.clean.push(() => fs.unlink(input));
|
ctx.clean.push(() => fs.unlink(input));
|
||||||
@@ -32,7 +21,7 @@ export async function judge(ctx: Context) {
|
|||||||
validator.execute,
|
validator.execute,
|
||||||
{
|
{
|
||||||
stdin: { src: input },
|
stdin: { src: input },
|
||||||
copyIn: { ...validator.copyIn, ...judgeExtraFiles },
|
copyIn: validator.copyIn,
|
||||||
time: parseTimeMS(ctx.config.time || '1s'),
|
time: parseTimeMS(ctx.config.time || '1s'),
|
||||||
memory: parseMemoryMB(ctx.config.memory || '256m'),
|
memory: parseMemoryMB(ctx.config.memory || '256m'),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
import { basename } from 'path';
|
|
||||||
import { STATUS } from '@hydrooj/utils/lib/status';
|
import { STATUS } from '@hydrooj/utils/lib/status';
|
||||||
import compile, { compileInteractor } from '../compile';
|
|
||||||
import { runFlow } from '../flow';
|
import { runFlow } from '../flow';
|
||||||
import { Execute } from '../interface';
|
|
||||||
import { runPiped } from '../sandbox';
|
import { runPiped } from '../sandbox';
|
||||||
import signals from '../signals';
|
import signals from '../signals';
|
||||||
import { parse } from '../testlib';
|
import { parse } from '../testlib';
|
||||||
@@ -11,8 +8,6 @@ import { Context, ContextSubTask } from './interface';
|
|||||||
|
|
||||||
function judgeCase(c: NormalizedCase) {
|
function judgeCase(c: NormalizedCase) {
|
||||||
return async (ctx: Context, ctxSubtask: ContextSubTask) => {
|
return async (ctx: Context, ctxSubtask: ContextSubTask) => {
|
||||||
ctx.executeInteractor.copyIn.in = c.input ? { src: c.input } : { content: '' };
|
|
||||||
ctx.executeInteractor.copyIn.out = c.output ? { src: c.output } : { content: '' };
|
|
||||||
const { address_space_limit, process_limit } = ctx.session.getLang(ctx.lang);
|
const { address_space_limit, process_limit } = ctx.session.getLang(ctx.lang);
|
||||||
const [{
|
const [{
|
||||||
code, signalled, time, memory,
|
code, signalled, time, memory,
|
||||||
@@ -27,7 +22,11 @@ function judgeCase(c: NormalizedCase) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
execute: `${ctx.executeInteractor.execute} /w/in /w/tout /w/out`,
|
execute: `${ctx.executeInteractor.execute} /w/in /w/tout /w/out`,
|
||||||
copyIn: ctx.executeInteractor.copyIn,
|
copyIn: {
|
||||||
|
in: c.input ? { src: c.input } : { content: '' },
|
||||||
|
out: c.output ? { src: c.output } : { content: '' },
|
||||||
|
...ctx.executeInteractor.copyIn,
|
||||||
|
},
|
||||||
time: c.time * 2,
|
time: c.time * 2,
|
||||||
memory: c.memory * 2,
|
memory: c.memory * 2,
|
||||||
copyOut: ['/w/tout?'],
|
copyOut: ['/w/tout?'],
|
||||||
@@ -67,20 +66,9 @@ function judgeCase(c: NormalizedCase) {
|
|||||||
|
|
||||||
export const judge = async (ctx: Context) => await runFlow(ctx, {
|
export const judge = async (ctx: Context) => await runFlow(ctx, {
|
||||||
compile: async () => {
|
compile: async () => {
|
||||||
const markCleanup = (i: Execute) => {
|
|
||||||
ctx.clean.push(i.clean);
|
|
||||||
return i;
|
|
||||||
};
|
|
||||||
const userExtraFiles = Object.fromEntries(
|
|
||||||
(ctx.config.user_extra_files || []).map((i) => [basename(i), { src: i }]),
|
|
||||||
);
|
|
||||||
const interactorFiles = { user_code: ctx.code };
|
|
||||||
for (const file of ctx.config.judge_extra_files) {
|
|
||||||
interactorFiles[basename(file)] = { src: file };
|
|
||||||
}
|
|
||||||
[ctx.executeUser, ctx.executeInteractor] = await Promise.all([
|
[ctx.executeUser, ctx.executeInteractor] = await Promise.all([
|
||||||
compile(ctx.session.getLang(ctx.lang), ctx.code, userExtraFiles, ctx.next).then(markCleanup),
|
ctx.compile(ctx.lang, ctx.code),
|
||||||
compileInteractor(ctx.session.getLang, ctx.config.interactor, interactorFiles).then(markCleanup),
|
ctx.compileWithTestlib('interactor', ctx.config.interactor),
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
judgeCase,
|
judgeCase,
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { STATUS } from '@hydrooj/utils/lib/status';
|
import { STATUS } from '@hydrooj/utils/lib/status';
|
||||||
import compile from '../compile';
|
|
||||||
import { CompileError } from '../error';
|
import { CompileError } from '../error';
|
||||||
|
import { Execute } from '../interface';
|
||||||
import { Logger } from '../log';
|
import { Logger } from '../log';
|
||||||
import { runQueued } from '../sandbox';
|
import { runQueued } from '../sandbox';
|
||||||
import signals from '../signals';
|
import signals from '../signals';
|
||||||
|
import { JudgeTask } from '../task';
|
||||||
import { compilerText, parseMemoryMB, parseTimeMS } from '../utils';
|
import { compilerText, parseMemoryMB, parseTimeMS } from '../utils';
|
||||||
import { Context } from './interface';
|
|
||||||
|
|
||||||
const failure = (status: number, message?: string) => ({
|
const failure = (status: number, message?: string) => ({
|
||||||
status,
|
status,
|
||||||
@@ -17,16 +17,16 @@ const failure = (status: number, message?: string) => ({
|
|||||||
|
|
||||||
const logger = new Logger('judge/run');
|
const logger = new Logger('judge/run');
|
||||||
|
|
||||||
export const judge = async (ctx: Context) => {
|
export const judge = async (ctx: JudgeTask) => {
|
||||||
ctx.stat.judge = new Date();
|
ctx.stat.judge = new Date();
|
||||||
ctx.next({ status: STATUS.STATUS_COMPILING });
|
ctx.next({ status: STATUS.STATUS_COMPILING });
|
||||||
|
let execute: Execute;
|
||||||
try {
|
try {
|
||||||
ctx.execute = await compile(
|
execute = await ctx.compile(
|
||||||
ctx.session.getLang(ctx.lang), ctx.code,
|
ctx.lang, ctx.code,
|
||||||
Object.fromEntries(
|
Object.fromEntries(
|
||||||
(ctx.config.user_extra_files || []).map((i) => [i.split('/').pop(), { src: i }]),
|
(ctx.config.user_extra_files || []).map((i) => [i.split('/').pop(), { src: i }]),
|
||||||
),
|
),
|
||||||
ctx.next,
|
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof CompileError) {
|
if (e instanceof CompileError) {
|
||||||
@@ -44,14 +44,13 @@ export const judge = async (ctx: Context) => {
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ctx.clean.push(ctx.execute.clean);
|
|
||||||
ctx.next({ status: STATUS.STATUS_JUDGING, progress: 0 });
|
ctx.next({ status: STATUS.STATUS_JUDGING, progress: 0 });
|
||||||
const { address_space_limit, process_limit } = ctx.session.getLang(ctx.lang);
|
const { address_space_limit, process_limit } = ctx.session.getLang(ctx.lang);
|
||||||
const res = await runQueued(
|
const res = await runQueued(
|
||||||
ctx.execute.execute,
|
execute.execute,
|
||||||
{
|
{
|
||||||
stdin: { content: ctx.input },
|
stdin: { content: ctx.input },
|
||||||
copyIn: ctx.execute.copyIn,
|
copyIn: execute.copyIn,
|
||||||
// Allow 2x limits for better debugging
|
// Allow 2x limits for better debugging
|
||||||
time: parseTimeMS(ctx.config.time || '1s') * 2,
|
time: parseTimeMS(ctx.config.time || '1s') * 2,
|
||||||
memory: parseMemoryMB(ctx.config.memory || '128m'),
|
memory: parseMemoryMB(ctx.config.memory || '128m'),
|
||||||
@@ -90,10 +89,9 @@ export const judge = async (ctx: Context) => {
|
|||||||
const langConfig = ctx.session.getLang(ctx.lang);
|
const langConfig = ctx.session.getLang(ctx.lang);
|
||||||
if (langConfig.analysis) {
|
if (langConfig.analysis) {
|
||||||
try {
|
try {
|
||||||
ctx.analysis = true;
|
|
||||||
const r = await runQueued(langConfig.analysis, {
|
const r = await runQueued(langConfig.analysis, {
|
||||||
copyIn: {
|
copyIn: {
|
||||||
...ctx.execute.copyIn,
|
...execute.copyIn,
|
||||||
input: { content: ctx.input },
|
input: { content: ctx.input },
|
||||||
[langConfig.code_file || 'foo']: ctx.code,
|
[langConfig.code_file || 'foo']: ctx.code,
|
||||||
compile: { content: langConfig.compile || '' },
|
compile: { content: langConfig.compile || '' },
|
||||||
|
|||||||
@@ -69,17 +69,7 @@ function judgeCase(c: NormalizedCase) {
|
|||||||
export const judge = async (ctx: Context) => {
|
export const judge = async (ctx: Context) => {
|
||||||
await runFlow(ctx, {
|
await runFlow(ctx, {
|
||||||
compile: async () => {
|
compile: async () => {
|
||||||
const copyIn = { user_code: ctx.code };
|
ctx.checker = await ctx.compileWithTestlib('checker', ctx.config.checker, ctx.config.checker_type);
|
||||||
for (const file of ctx.config.judge_extra_files) {
|
|
||||||
copyIn[basename(file)] = { src: file };
|
|
||||||
}
|
|
||||||
ctx.checker = await compileChecker(
|
|
||||||
ctx.session.getLang,
|
|
||||||
ctx.config.checker_type,
|
|
||||||
ctx.config.checker,
|
|
||||||
copyIn,
|
|
||||||
);
|
|
||||||
ctx.clean.push(ctx.checker.clean);
|
|
||||||
},
|
},
|
||||||
judgeCase,
|
judgeCase,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { basename } from 'path';
|
||||||
import { fs } from '@hydrooj/utils';
|
import { fs } from '@hydrooj/utils';
|
||||||
import { LangConfig } from '@hydrooj/utils/lib/lang';
|
import { LangConfig } from '@hydrooj/utils/lib/lang';
|
||||||
import { STATUS } from '@hydrooj/utils/lib/status';
|
import { STATUS } from '@hydrooj/utils/lib/status';
|
||||||
@@ -5,12 +6,14 @@ import type {
|
|||||||
FileInfo, JudgeMeta, JudgeRequest, JudgeResultBody, TestCase,
|
FileInfo, JudgeMeta, JudgeRequest, JudgeResultBody, TestCase,
|
||||||
} from 'hydrooj';
|
} from 'hydrooj';
|
||||||
import readCases from './cases';
|
import readCases from './cases';
|
||||||
|
import checkers from './checkers';
|
||||||
|
import compile, { compileWithTestlib } from './compile';
|
||||||
import { getConfig } from './config';
|
import { getConfig } from './config';
|
||||||
import { CompileError, FormatError } from './error';
|
import { CompileError, FormatError } from './error';
|
||||||
import { NextFunction, ParsedConfig } from './interface';
|
import { NextFunction, ParsedConfig } from './interface';
|
||||||
import judge from './judge';
|
import judge from './judge';
|
||||||
import { Logger } from './log';
|
import { Logger } from './log';
|
||||||
import { CopyInFile } from './sandbox';
|
import { CopyIn, CopyInFile } from './sandbox';
|
||||||
import { compilerText, md5 } from './utils';
|
import { compilerText, md5 } from './utils';
|
||||||
|
|
||||||
interface Session {
|
interface Session {
|
||||||
@@ -129,4 +132,28 @@ export class JudgeTask {
|
|||||||
if (!judge[type]) throw new FormatError('Unrecognized problemType: {0}', [type]);
|
if (!judge[type]) throw new FormatError('Unrecognized problemType: {0}', [type]);
|
||||||
await judge[type].judge(this);
|
await judge[type].judge(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async compile(lang: string, code: CopyInFile) {
|
||||||
|
const copyIn = Object.fromEntries(
|
||||||
|
(this.config.user_extra_files || []).map((i) => [basename(i), { src: i }]),
|
||||||
|
) as CopyIn;
|
||||||
|
const result = await compile(this.session.getLang(lang), code, copyIn, this.next);
|
||||||
|
this.clean.push(result.clean);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
async compileWithTestlib(type: 'interactor' | 'validator' | 'checker', file: string, checkerType?: string) {
|
||||||
|
if (type === 'checker' && ['default', 'strict'].includes(checkerType)) return { execute: '', copyIn: {}, clean: () => Promise.resolve(null) };
|
||||||
|
if (!checkers[checkerType]) throw new FormatError('Unknown checker type {0}.', [checkerType]);
|
||||||
|
const withTestlib = type !== 'checker' || checkerType === 'testlib';
|
||||||
|
const copyIn = {
|
||||||
|
user_code: this.code,
|
||||||
|
...Object.fromEntries(
|
||||||
|
(this.config.judge_extra_files || []).map((i) => [basename(i), { src: i }]),
|
||||||
|
),
|
||||||
|
} as CopyIn;
|
||||||
|
const result = await compileWithTestlib(file, type, this.session.getLang, copyIn, withTestlib, this.next);
|
||||||
|
this.clean.push(result.clean);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user