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