mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 01:38:19 +08:00
judge: pass envirent vars to checker
This commit is contained in:
1 parent
9158550139
commit
abec5fd3ec
11 files changed
+54
-47
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.12.17",
|
"version": "2.13.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",
|
||||||
|
|||||||
@@ -3,36 +3,16 @@ import { findFileSync } from '@hydrooj/utils/lib/utils';
|
|||||||
import checkers from './checkers';
|
import checkers from './checkers';
|
||||||
import compile from './compile';
|
import compile from './compile';
|
||||||
import { SystemError } from './error';
|
import { SystemError } from './error';
|
||||||
import { Execute } from './interface';
|
import { CheckConfig, Execute } from './interface';
|
||||||
import { CopyInFile } from './sandbox/interface';
|
|
||||||
import { parseFilename } from './utils';
|
import { parseFilename } from './utils';
|
||||||
|
|
||||||
const testlibSrc = findFileSync('@hydrooj/hydrojudge/vendor/testlib/testlib.h');
|
const testlibSrc = findFileSync('@hydrooj/hydrojudge/vendor/testlib/testlib.h');
|
||||||
|
|
||||||
interface CheckConfig {
|
|
||||||
checker_type: string;
|
|
||||||
stdin: CopyInFile,
|
|
||||||
stdout: CopyInFile,
|
|
||||||
user_stdout: CopyInFile,
|
|
||||||
user_stderr: CopyInFile,
|
|
||||||
copyIn?: Record<string, CopyInFile>,
|
|
||||||
score: number,
|
|
||||||
detail: boolean,
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function check(config: CheckConfig): Promise<[number, number, string]> {
|
export async function check(config: CheckConfig): Promise<[number, number, string]> {
|
||||||
if (!checkers[config.checker_type]) throw new SystemError('Unknown checker type {0}', [config.checker_type]);
|
if (!checkers[config.checker_type]) throw new SystemError('Unknown checker type {0}', [config.checker_type]);
|
||||||
const {
|
const {
|
||||||
code, status, score, message,
|
code, status, score, message,
|
||||||
} = await checkers[config.checker_type]({
|
} = await checkers[config.checker_type](config);
|
||||||
input: config.stdin,
|
|
||||||
output: config.stdout,
|
|
||||||
user_stdout: config.user_stdout,
|
|
||||||
user_stderr: config.user_stderr,
|
|
||||||
score: config.score,
|
|
||||||
copyIn: config.copyIn || {},
|
|
||||||
detail: config.detail,
|
|
||||||
});
|
|
||||||
if (code) throw new SystemError('Checker returned {0}.', [code]);
|
if (code) throw new SystemError('Checker returned {0}.', [code]);
|
||||||
return [status, score, message];
|
return [status, score, message];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +1,10 @@
|
|||||||
/* eslint-disable no-template-curly-in-string */
|
/* eslint-disable no-template-curly-in-string */
|
||||||
import { STATUS } from '@hydrooj/utils/lib/status';
|
import { STATUS } from '@hydrooj/utils/lib/status';
|
||||||
import { SystemError } from './error';
|
import { SystemError } from './error';
|
||||||
|
import { CheckConfig, CheckResult } from './interface';
|
||||||
import { run } from './sandbox';
|
import { run } from './sandbox';
|
||||||
import { CopyInFile } from './sandbox/interface';
|
|
||||||
import { parse } from './testlib';
|
import { parse } from './testlib';
|
||||||
|
|
||||||
interface CheckConfig {
|
|
||||||
input: CopyInFile,
|
|
||||||
output: CopyInFile,
|
|
||||||
user_stdout: CopyInFile,
|
|
||||||
user_stderr: CopyInFile,
|
|
||||||
copyIn: Record<string, CopyInFile>,
|
|
||||||
score: number,
|
|
||||||
detail: boolean,
|
|
||||||
}
|
|
||||||
|
|
||||||
interface CheckResult {
|
|
||||||
status: number,
|
|
||||||
score: number,
|
|
||||||
message: string,
|
|
||||||
code?: number,
|
|
||||||
}
|
|
||||||
|
|
||||||
type Checker = (config: CheckConfig) => Promise<CheckResult>;
|
type Checker = (config: CheckConfig) => Promise<CheckResult>;
|
||||||
|
|
||||||
const checkers: Record<string, Checker> = {
|
const checkers: Record<string, Checker> = {
|
||||||
@@ -129,6 +112,7 @@ const checkers: Record<string, Checker> = {
|
|||||||
...config.copyIn,
|
...config.copyIn,
|
||||||
},
|
},
|
||||||
copyOut: ['score', 'message'],
|
copyOut: ['score', 'message'],
|
||||||
|
env: config.env,
|
||||||
});
|
});
|
||||||
const { message } = files;
|
const { message } = files;
|
||||||
const score = parseInt(files.score, 10);
|
const score = parseInt(files.score, 10);
|
||||||
@@ -196,6 +180,7 @@ const checkers: Record<string, Checker> = {
|
|||||||
answer: config.output,
|
answer: config.output,
|
||||||
...config.copyIn,
|
...config.copyIn,
|
||||||
},
|
},
|
||||||
|
env: config.env,
|
||||||
});
|
});
|
||||||
if (status === STATUS.STATUS_SYSTEM_ERROR) {
|
if (status === STATUS.STATUS_SYSTEM_ERROR) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ class JudgeTask {
|
|||||||
data: any[];
|
data: any[];
|
||||||
folder: string;
|
folder: string;
|
||||||
config: any;
|
config: any;
|
||||||
|
env: Record<string, string>;
|
||||||
getLang: (name: string) => LangConfig;
|
getLang: (name: string) => LangConfig;
|
||||||
|
|
||||||
constructor(session: Hydro, request, ws: WebSocket) {
|
constructor(session: Hydro, request, ws: WebSocket) {
|
||||||
@@ -63,6 +64,13 @@ class JudgeTask {
|
|||||||
this.source = this.request.source;
|
this.source = this.request.source;
|
||||||
this.tmpdir = path.resolve(getConfig('tmp_dir'), this.host, this.rid);
|
this.tmpdir = path.resolve(getConfig('tmp_dir'), this.host, this.rid);
|
||||||
this.clean = [];
|
this.clean = [];
|
||||||
|
this.env = {
|
||||||
|
HYDRO_DOMAIN: this.request.domainId.toString(),
|
||||||
|
HYDRO_RECORD: this.rid,
|
||||||
|
HYDRO_LANG: this.lang,
|
||||||
|
HYDRO_USER: this.request.uid.toString(),
|
||||||
|
HYDRO_CONTEST: this.request.tid?.toString() || '',
|
||||||
|
};
|
||||||
await Lock.acquire(`${this.host}/${this.source}/${this.rid}`);
|
await Lock.acquire(`${this.host}/${this.source}/${this.rid}`);
|
||||||
fs.ensureDirSync(this.tmpdir);
|
fs.ensureDirSync(this.tmpdir);
|
||||||
tmpfs.mount(this.tmpdir, getConfig('tmpfs_size'));
|
tmpfs.mount(this.tmpdir, getConfig('tmpfs_size'));
|
||||||
|
|||||||
@@ -1,12 +1,33 @@
|
|||||||
import { CopyInFile } from './sandbox/interface';
|
import { CopyInFile } from './sandbox/interface';
|
||||||
|
|
||||||
export interface Execute {
|
export interface Execute {
|
||||||
execute: string,
|
execute: string,
|
||||||
clean: Function,
|
clean: Function,
|
||||||
copyIn: Record<string, CopyInFile>,
|
copyIn: Record<string, CopyInFile>,
|
||||||
time?: number,
|
time?: number,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CompileErrorInfo {
|
export interface CompileErrorInfo {
|
||||||
stdout?: string,
|
stdout?: string,
|
||||||
stderr?: string,
|
stderr?: string,
|
||||||
status?: number
|
status?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface CheckConfig {
|
||||||
|
checker_type: string;
|
||||||
|
input: CopyInFile;
|
||||||
|
output: CopyInFile;
|
||||||
|
user_stdout: CopyInFile;
|
||||||
|
user_stderr: CopyInFile;
|
||||||
|
copyIn: Record<string, CopyInFile>;
|
||||||
|
score: number;
|
||||||
|
detail: boolean;
|
||||||
|
env?: Record<string, string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CheckResult {
|
||||||
|
status: number,
|
||||||
|
score: number,
|
||||||
|
message: string,
|
||||||
|
code?: number,
|
||||||
|
}
|
||||||
@@ -67,14 +67,15 @@ 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({
|
||||||
copyIn: ctx.checker.copyIn,
|
copyIn: ctx.checker.copyIn || {},
|
||||||
stdin: { src: c.input },
|
input: { src: c.input },
|
||||||
stdout: { src: c.output },
|
output: { src: c.output },
|
||||||
user_stdout: stdout,
|
user_stdout: stdout,
|
||||||
user_stderr: stderr,
|
user_stderr: stderr,
|
||||||
checker_type: ctx.config.checker_type,
|
checker_type: ctx.config.checker_type,
|
||||||
score: ctxSubtask.subtask.score,
|
score: ctxSubtask.subtask.score,
|
||||||
detail: ctx.config.detail ?? true,
|
detail: ctx.config.detail ?? true,
|
||||||
|
env: ctx.env,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if (status === STATUS.STATUS_RUNTIME_ERROR && code) {
|
} else if (status === STATUS.STATUS_RUNTIME_ERROR && code) {
|
||||||
|
|||||||
@@ -97,8 +97,8 @@ export const judge = async (ctx: Context) => {
|
|||||||
} else {
|
} else {
|
||||||
[status, , message] = await check({
|
[status, , message] = await check({
|
||||||
copyIn: copyInDir(path.resolve(ctx.tmpdir, 'checker')),
|
copyIn: copyInDir(path.resolve(ctx.tmpdir, 'checker')),
|
||||||
stdin: { src: input },
|
input: { src: input },
|
||||||
stdout: { src: stdout },
|
output: { src: stdout },
|
||||||
user_stdout: { src: stdout },
|
user_stdout: { src: stdout },
|
||||||
user_stderr: { src: stderr },
|
user_stderr: { src: stderr },
|
||||||
checker_type: ctx.config.checker_type,
|
checker_type: ctx.config.checker_type,
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ function judgeCase(c: Case) {
|
|||||||
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,
|
||||||
copyOut: ['/w/tout?'],
|
copyOut: ['/w/tout?'],
|
||||||
|
env: ctx.env,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
// TODO handle tout (maybe pass to checker?)
|
// TODO handle tout (maybe pass to checker?)
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ export interface JudgeTaskInterface {
|
|||||||
input?: string;
|
input?: string;
|
||||||
clean: Function[];
|
clean: Function[];
|
||||||
config: Config;
|
config: Config;
|
||||||
|
env?: Record<string, string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RuntimeContext {
|
export interface RuntimeContext {
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ interface Parameter {
|
|||||||
copyOut?: string[];
|
copyOut?: string[];
|
||||||
copyOutCached?: string[];
|
copyOutCached?: string[];
|
||||||
cacheStdoutAndStderr?: boolean;
|
cacheStdoutAndStderr?: boolean;
|
||||||
|
env?: Record<string, string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SandboxAdaptedResult {
|
interface SandboxAdaptedResult {
|
||||||
@@ -72,6 +73,7 @@ function proc({
|
|||||||
processLimit = getConfig('processLimit'),
|
processLimit = getConfig('processLimit'),
|
||||||
stdin = '', copyIn = {}, copyOut = [], copyOutCached = [],
|
stdin = '', copyIn = {}, copyOut = [], copyOutCached = [],
|
||||||
cacheStdoutAndStderr = false,
|
cacheStdoutAndStderr = false,
|
||||||
|
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 as string[]).map((i) => (i.endsWith('?') ? i.substr(0, i.length - 1) : i));
|
||||||
@@ -84,7 +86,7 @@ function proc({
|
|||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
args: parseArgs(execute),
|
args: parseArgs(execute),
|
||||||
env: getConfig('env').split('\n'),
|
env: [...getConfig('env').split('\n'), ...Object.keys(env).map((i) => `${i}=${env[i].replace(/=/g, '\\=')}`)],
|
||||||
files: [
|
files: [
|
||||||
stdin ? { src: stdin } : { content: '' },
|
stdin ? { src: stdin } : { content: '' },
|
||||||
{ name: 'stdout', max: Math.floor(1024 * 1024 * size) },
|
{ name: 'stdout', max: Math.floor(1024 * 1024 * size) },
|
||||||
|
|||||||
@@ -150,6 +150,7 @@ async function postInit() {
|
|||||||
code: string;
|
code: string;
|
||||||
data: any[];
|
data: any[];
|
||||||
config: any;
|
config: any;
|
||||||
|
env: any;
|
||||||
input?: string;
|
input?: string;
|
||||||
next: (data: any, id?: number) => void;
|
next: (data: any, id?: number) => void;
|
||||||
end: (data: any) => void;
|
end: (data: any) => void;
|
||||||
@@ -176,6 +177,13 @@ async function postInit() {
|
|||||||
this.source = this.request.source;
|
this.source = this.request.source;
|
||||||
this.config = this.request.config;
|
this.config = this.request.config;
|
||||||
this.input = this.request.input;
|
this.input = this.request.input;
|
||||||
|
this.env = {
|
||||||
|
HYDRO_DOMAIN: this.request.domainId.toString(),
|
||||||
|
HYDRO_RECORD: this.rid,
|
||||||
|
HYDRO_LANG: this.lang,
|
||||||
|
HYDRO_USER: this.request.uid.toString(),
|
||||||
|
HYDRO_CONTEST: this.request.tid?.toString() || '',
|
||||||
|
};
|
||||||
this.next = getNext(this);
|
this.next = getNext(this);
|
||||||
this.end = getEnd(this.rid);
|
this.end = getEnd(this.rid);
|
||||||
this.tmpdir = path.resolve(getConfig('tmp_dir'), this.rid);
|
this.tmpdir = path.resolve(getConfig('tmp_dir'), this.rid);
|
||||||
|
|||||||
Reference in new issue
Block a user