mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 01:38:19 +08:00
core&judge: probe compiler versions
This commit is contained in:
6 files changed
+94
-9
No files matched your search
@@ -0,0 +1,18 @@
|
|||||||
|
import { LangConfig } from '@hydrooj/utils/lib/lang';
|
||||||
|
import { runQueued } from './sandbox';
|
||||||
|
|
||||||
|
export async function compilerVersions(langs: Record<string, LangConfig>) {
|
||||||
|
const result = {};
|
||||||
|
for (const lang in langs) {
|
||||||
|
const version = langs[lang].version;
|
||||||
|
if (!version) continue;
|
||||||
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
const res = await runQueued(version, {
|
||||||
|
copyIn: {},
|
||||||
|
time: 10000,
|
||||||
|
memory: 256 * 1024 * 1024,
|
||||||
|
}, 5);
|
||||||
|
result[lang] = `${res.stdout}\n${res.stderr}`.trim();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
@@ -2,10 +2,13 @@
|
|||||||
/* eslint-disable no-await-in-loop */
|
/* eslint-disable no-await-in-loop */
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { fs } from '@hydrooj/utils';
|
import { fs } from '@hydrooj/utils';
|
||||||
|
import * as sysinfo from '@hydrooj/utils/lib/sysinfo';
|
||||||
import {
|
import {
|
||||||
JudgeHandler, JudgeResultBody, ObjectId, RecordModel, SettingModel,
|
db, JudgeHandler, JudgeResultBody, ObjectId, RecordModel,
|
||||||
StorageModel, SystemModel, TaskModel,
|
SettingModel, StorageModel, SystemModel, TaskModel,
|
||||||
} from 'hydrooj';
|
} from 'hydrooj';
|
||||||
|
import { langs } from 'hydrooj/src/model/setting';
|
||||||
|
import { compilerVersions } from '../compiler';
|
||||||
import { getConfig } from '../config';
|
import { getConfig } from '../config';
|
||||||
import { FormatError, SystemError } from '../error';
|
import { FormatError, SystemError } from '../error';
|
||||||
import { Context } from '../judge/interface';
|
import { Context } from '../judge/interface';
|
||||||
@@ -82,6 +85,7 @@ export async function postInit(ctx) {
|
|||||||
c.check.addChecker('Judge', (_ctx, log, warn, error) => versionCheck(warn, error));
|
c.check.addChecker('Judge', (_ctx, log, warn, error) => versionCheck(warn, error));
|
||||||
});
|
});
|
||||||
await fs.ensureDir(getConfig('tmp_dir'));
|
await fs.ensureDir(getConfig('tmp_dir'));
|
||||||
|
const info = await sysinfo.get();
|
||||||
const handle = async (t) => {
|
const handle = async (t) => {
|
||||||
const rdoc = await RecordModel.get(t.domainId, t.rid);
|
const rdoc = await RecordModel.get(t.domainId, t.rid);
|
||||||
if (!rdoc) {
|
if (!rdoc) {
|
||||||
@@ -92,9 +96,19 @@ export async function postInit(ctx) {
|
|||||||
};
|
};
|
||||||
const parallelism = Math.max(getConfig('parallelism'), 2);
|
const parallelism = Math.max(getConfig('parallelism'), 2);
|
||||||
const taskConsumer = TaskModel.consume({ type: 'judge' }, handle, true, parallelism);
|
const taskConsumer = TaskModel.consume({ type: 'judge' }, handle, true, parallelism);
|
||||||
|
function collectCompilerInfo() {
|
||||||
|
const coll = db.collection('status');
|
||||||
|
compilerVersions(langs).then((compilers) => coll.updateOne(
|
||||||
|
{ mid: info.mid, type: 'judge' },
|
||||||
|
{ $set: { compilers } },
|
||||||
|
{ upsert: true },
|
||||||
|
)).catch((e) => logger.error(e));
|
||||||
|
}
|
||||||
ctx.on('system/setting', () => {
|
ctx.on('system/setting', () => {
|
||||||
taskConsumer.setConcurrency(Math.max(getConfig('parallelism'), 2));
|
taskConsumer.setConcurrency(Math.max(getConfig('parallelism'), 2));
|
||||||
|
collectCompilerInfo();
|
||||||
});
|
});
|
||||||
|
collectCompilerInfo();
|
||||||
TaskModel.consume({ type: 'judge', priority: { $gt: -50 } }, handle);
|
TaskModel.consume({ type: 'judge', priority: { $gt: -50 } }, handle);
|
||||||
TaskModel.consume({ type: 'generate' }, handle);
|
TaskModel.consume({ type: 'generate' }, handle);
|
||||||
}
|
}
|
||||||
@@ -7,6 +7,7 @@ import { fs, pipeRequest } from '@hydrooj/utils';
|
|||||||
import { LangConfig } from '@hydrooj/utils/lib/lang';
|
import { LangConfig } from '@hydrooj/utils/lib/lang';
|
||||||
import * as sysinfo from '@hydrooj/utils/lib/sysinfo';
|
import * as sysinfo from '@hydrooj/utils/lib/sysinfo';
|
||||||
import type { JudgeResultBody } from 'hydrooj';
|
import type { JudgeResultBody } from 'hydrooj';
|
||||||
|
import { compilerVersions } from '../compiler';
|
||||||
import { getConfig } from '../config';
|
import { getConfig } from '../config';
|
||||||
import { FormatError, SystemError } from '../error';
|
import { FormatError, SystemError } from '../error';
|
||||||
import { Session } from '../interface';
|
import { Session } from '../interface';
|
||||||
@@ -173,13 +174,19 @@ export default class Hydro implements Session {
|
|||||||
const content = Object.keys(config).length
|
const content = Object.keys(config).length
|
||||||
? JSON.stringify({ key: 'config', ...config })
|
? JSON.stringify({ key: 'config', ...config })
|
||||||
: '{"key":"ping"}';
|
: '{"key":"ping"}';
|
||||||
|
let compilers = {};
|
||||||
this.ws.on('message', (data) => {
|
this.ws.on('message', (data) => {
|
||||||
if (data.toString() === 'ping') {
|
if (data.toString() === 'ping') {
|
||||||
this.ws.send('pong');
|
this.ws.send('pong');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const request = JSON.parse(data.toString());
|
const request = JSON.parse(data.toString());
|
||||||
if (request.language) this.language = request.language;
|
if (request.language) {
|
||||||
|
this.language = request.language;
|
||||||
|
compilerVersions(this.language).then((res) => {
|
||||||
|
compilers = res;
|
||||||
|
});
|
||||||
|
}
|
||||||
if (request.task) queue.add(() => new JudgeTask(this, request.task).handle().catch((e) => log.error(e)));
|
if (request.task) queue.add(() => new JudgeTask(this, request.task).handle().catch((e) => log.error(e)));
|
||||||
});
|
});
|
||||||
this.ws.on('close', (data, reason) => {
|
this.ws.on('close', (data, reason) => {
|
||||||
@@ -196,10 +203,10 @@ export default class Hydro implements Session {
|
|||||||
this.ws.send('{"key":"start"}');
|
this.ws.send('{"key":"start"}');
|
||||||
if (!this.config.noStatus) {
|
if (!this.config.noStatus) {
|
||||||
const info = await sysinfo.get();
|
const info = await sysinfo.get();
|
||||||
this.ws.send(JSON.stringify({ key: 'status', info }));
|
this.ws.send(JSON.stringify({ key: 'status', info: { ...info, compilers } }));
|
||||||
setInterval(async () => {
|
setInterval(async () => {
|
||||||
const [mid, inf] = await sysinfo.update();
|
const [mid, inf] = await sysinfo.update();
|
||||||
this.ws.send(JSON.stringify({ key: 'status', info: { mid, ...inf } }));
|
this.ws.send(JSON.stringify({ key: 'status', info: { mid, ...inf, compilers } }));
|
||||||
}, 1200000);
|
}, 1200000);
|
||||||
}
|
}
|
||||||
resolve(null);
|
resolve(null);
|
||||||
|
|||||||
@@ -23,12 +23,12 @@ langs:
|
|||||||
- ~ All keys starting with _ will be ignored
|
- ~ All keys starting with _ will be ignored
|
||||||
- ~ Entries named like [foo], [foo].[bar] [foo].[baz]
|
- ~ Entries named like [foo], [foo].[bar] [foo].[baz]
|
||||||
- will be merged into one category.
|
- will be merged into one category.
|
||||||
- ~ A entry named [foo].[bar] will inhert all options
|
- ~ A entry named [foo].[bar] will inhert all options
|
||||||
- defined in entry [foo] if not overwritten
|
- defined in entry [foo] if not overwritten
|
||||||
- ~ code_file option defaults to foo.[name]
|
- ~ code_file option defaults to foo.[name]
|
||||||
- ~ highlight option defaults to [name]
|
- ~ highlight option defaults to [name]
|
||||||
- ~ if highlight option was set, monaco option defaults
|
- ~ if highlight option was set, monaco option defaults
|
||||||
- to [highlight], otherwise defaults to [name]
|
- to [highlight], otherwise defaults to [name]
|
||||||
- ~ Compiling be be ignored if compile option wasn't set
|
- ~ Compiling be be ignored if compile option wasn't set
|
||||||
- ~ Key marked with a ? means the option is optional
|
- ~ Key marked with a ? means the option is optional
|
||||||
- (name):
|
- (name):
|
||||||
@@ -49,11 +49,13 @@ langs:
|
|||||||
code_file: foo.sh
|
code_file: foo.sh
|
||||||
execute: /bin/bash foo.sh
|
execute: /bin/bash foo.sh
|
||||||
highlight: bash
|
highlight: bash
|
||||||
|
version: /bin/bash --version
|
||||||
c:
|
c:
|
||||||
compile: /usr/bin/gcc -Wall --std=c99 -o foo foo.c -lm
|
compile: /usr/bin/gcc -Wall --std=c99 -o foo foo.c -lm
|
||||||
highlight: c
|
highlight: c
|
||||||
monaco: c
|
monaco: c
|
||||||
display: C
|
display: C
|
||||||
|
version: /usr/bin/gcc --version
|
||||||
cc:
|
cc:
|
||||||
compile: /usr/bin/g++ -Wall -std=c++14 -o foo foo.cc -lm -I/include
|
compile: /usr/bin/g++ -Wall -std=c++14 -o foo foo.cc -lm -I/include
|
||||||
code_file: foo.cc
|
code_file: foo.cc
|
||||||
@@ -62,6 +64,7 @@ langs:
|
|||||||
display: C++
|
display: C++
|
||||||
address_space_limit: true
|
address_space_limit: true
|
||||||
process_limit: 1
|
process_limit: 1
|
||||||
|
version: /usr/bin/g++ --version
|
||||||
cc.cc98:
|
cc.cc98:
|
||||||
compile: /usr/bin/g++ -Wall -std=c++98 -o foo foo.cc -lm -I/include
|
compile: /usr/bin/g++ -Wall -std=c++98 -o foo foo.cc -lm -I/include
|
||||||
display: C++98
|
display: C++98
|
||||||
@@ -95,6 +98,7 @@ langs:
|
|||||||
compile: /usr/bin/fpc -O2 -o/w/foo foo.pas
|
compile: /usr/bin/fpc -O2 -o/w/foo foo.pas
|
||||||
highlight: pascal
|
highlight: pascal
|
||||||
display: Pascal
|
display: Pascal
|
||||||
|
version: /usr/bin/fpc -iW
|
||||||
java:
|
java:
|
||||||
compile: /usr/bin/bash -c "javac -d /w -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
|
||||||
@@ -105,6 +109,7 @@ langs:
|
|||||||
monaco: java
|
monaco: java
|
||||||
display: Java
|
display: Java
|
||||||
compile_time_limit: 30000
|
compile_time_limit: 30000
|
||||||
|
version: /usr/bin/javac -version
|
||||||
kt:
|
kt:
|
||||||
compile: echo "Invalid language selection"
|
compile: echo "Invalid language selection"
|
||||||
code_file: foo.kt
|
code_file: foo.kt
|
||||||
@@ -119,6 +124,7 @@ langs:
|
|||||||
execute: /usr/bin/java -jar foo.jar
|
execute: /usr/bin/java -jar foo.jar
|
||||||
display: Kotlin/JVM
|
display: Kotlin/JVM
|
||||||
compile_time_limit: 30000
|
compile_time_limit: 30000
|
||||||
|
version: /usr/bin/kotlinc -version
|
||||||
py:
|
py:
|
||||||
compile: /usr/bin/python -c "import py_compile; py_compile.compile('/w/foo.py', '/w/foo', 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
|
||||||
@@ -132,38 +138,47 @@ langs:
|
|||||||
compile: /usr/bin/python3 -c "import py_compile; py_compile.compile('/w/foo.py', '/w/foo', doraise=True)"
|
compile: /usr/bin/python3 -c "import py_compile; py_compile.compile('/w/foo.py', '/w/foo', doraise=True)"
|
||||||
execute: /usr/bin/python3 foo
|
execute: /usr/bin/python3 foo
|
||||||
display: Python 3
|
display: Python 3
|
||||||
|
version: /usr/bin/python3 --version
|
||||||
py.pypy3:
|
py.pypy3:
|
||||||
compile: /bin/bash -c "/usr/bin/pypy3 -c \"import py_compile; py_compile.compile('/w/foo.py', '/w/foo', doraise=True)\" && mv foo.py foo"
|
compile: /bin/bash -c "/usr/bin/pypy3 -c \"import py_compile; py_compile.compile('/w/foo.py', '/w/foo', doraise=True)\" && mv foo.py foo"
|
||||||
execute: /bin/bash -c 'mv foo foo.py && /usr/bin/pypy3 -B foo.py'
|
execute: /bin/bash -c 'mv foo foo.py && /usr/bin/pypy3 -B foo.py'
|
||||||
display: PyPy3
|
display: PyPy3
|
||||||
|
version: /usr/bin/pypy3 --version
|
||||||
php:
|
php:
|
||||||
execute: /usr/bin/php foo.php
|
execute: /usr/bin/php foo.php
|
||||||
display: PHP
|
display: PHP
|
||||||
|
version: /usr/bin/php -v
|
||||||
rs:
|
rs:
|
||||||
compile: /usr/bin/rustc -O -o /w/foo /w/foo.rs
|
compile: /usr/bin/rustc -O -o /w/foo /w/foo.rs
|
||||||
highlight: rust
|
highlight: rust
|
||||||
display: Rust
|
display: Rust
|
||||||
|
version: /usr/bin/rustc --version
|
||||||
hs:
|
hs:
|
||||||
compile: /usr/bin/ghc -O -outputdir /tmp -o foo foo.hs
|
compile: /usr/bin/ghc -O -outputdir /tmp -o foo foo.hs
|
||||||
highlight: haskell
|
highlight: haskell
|
||||||
display: Haskell
|
display: Haskell
|
||||||
|
version: /usr/bin/ghc --version
|
||||||
js:
|
js:
|
||||||
execute: /usr/bin/node /w/foo.js
|
execute: /usr/bin/node /w/foo.js
|
||||||
highlight: javascript
|
highlight: javascript
|
||||||
display: NodeJS
|
display: NodeJS
|
||||||
|
version: /usr/bin/node --version
|
||||||
go:
|
go:
|
||||||
compile: /usr/bin/go build -o foo foo.go
|
compile: /usr/bin/go build -o foo foo.go
|
||||||
display: Golang
|
display: Golang
|
||||||
|
version: /usr/bin/go version
|
||||||
rb:
|
rb:
|
||||||
execute: /usr/bin/ruby foo.rb
|
execute: /usr/bin/ruby foo.rb
|
||||||
highlight: ruby
|
highlight: ruby
|
||||||
display: Ruby
|
display: Ruby
|
||||||
|
version: /usr/bin/ruby --version
|
||||||
cs:
|
cs:
|
||||||
compile: /usr/bin/mcs -optimize+ -out:/w/foo /w/foo.cs
|
compile: /usr/bin/mcs -optimize+ -out:/w/foo /w/foo.cs
|
||||||
execute: /usr/bin/mono foo
|
execute: /usr/bin/mono foo
|
||||||
highlight: csharp
|
highlight: csharp
|
||||||
monaco: csharp
|
monaco: csharp
|
||||||
display: 'C#'
|
display: 'C#'
|
||||||
|
version: /usr/bin/mcs --version
|
||||||
r:
|
r:
|
||||||
display: R
|
display: R
|
||||||
execute: /bin/sh -c "cat /dev/stdin >stdin && /usr/bin/Rscript --vanilla foo.r"
|
execute: /bin/sh -c "cat /dev/stdin >stdin && /usr/bin/Rscript --vanilla foo.r"
|
||||||
|
|||||||
@@ -25,11 +25,41 @@ async function getStatus() {
|
|||||||
class StatusHandler extends Handler {
|
class StatusHandler extends Handler {
|
||||||
async get() {
|
async get() {
|
||||||
const stats = await getStatus();
|
const stats = await getStatus();
|
||||||
|
const compilers = {};
|
||||||
|
const warn = {};
|
||||||
|
const result: Array<{ key: string[], message: string }> = [];
|
||||||
|
// For each language, select the most common compiler message version,
|
||||||
|
// then merge languages with the same message.
|
||||||
for (const stat of stats) {
|
for (const stat of stats) {
|
||||||
if (!stat.battery.hasBattery) stat.battery = 'No battery';
|
if (!stat.battery?.hasBattery) stat.battery = 'No battery';
|
||||||
else stat.battery = `${stat.battery.type} ${stat.battery.model} ${stat.battery.percent}%${stat.battery.isCharging ? ' Charging' : ''}`;
|
else stat.battery = `${stat.battery.type} ${stat.battery.model} ${stat.battery.percent}%${stat.battery.isCharging ? ' Charging' : ''}`;
|
||||||
|
if (stat.compilers) {
|
||||||
|
for (const key in stat.compilers) {
|
||||||
|
if (!compilers[key]) compilers[key] ||= [];
|
||||||
|
const related = compilers[key].find((i) => i.message === stat.compilers[key]);
|
||||||
|
if (related) related.related.push(stat._id);
|
||||||
|
else {
|
||||||
|
compilers[key].push({
|
||||||
|
related: [stat._id],
|
||||||
|
message: stat.compilers[key],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.response.body = { stats };
|
for (const key in compilers) {
|
||||||
|
compilers[key].sort((a, b) => b.related.length - a.related.length);
|
||||||
|
const message = compilers[key][0].message;
|
||||||
|
for (let i = 1; i < compilers[key].length; i++) {
|
||||||
|
for (const id of compilers[key][i].related) {
|
||||||
|
warn[id] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const t = result.find((i) => i.message === message);
|
||||||
|
if (t) t.key.push(key);
|
||||||
|
else result.push({ key: [key], message });
|
||||||
|
}
|
||||||
|
this.response.body = { stats, compilers: result };
|
||||||
this.response.template = 'status.html';
|
this.response.template = 'status.html';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ export interface LangConfig {
|
|||||||
comment?: string | [string, string];
|
comment?: string | [string, string];
|
||||||
compile_time_limit?: number;
|
compile_time_limit?: number;
|
||||||
compile_memory_limit?: number;
|
compile_memory_limit?: number;
|
||||||
|
version?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function parseLang(config: string): Record<string, LangConfig> {
|
export function parseLang(config: string): Record<string, LangConfig> {
|
||||||
|
|||||||
Reference in new issue
Block a user