feat: merge woj-runner resources

This commit is contained in:
Paul Pan 2022-10-20 18:21:11 +08:00
parent b77c968409
commit 062c5ef964
31 changed files with 518 additions and 0 deletions

2
resource/runner/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
.mark.*
*.zip

View File

@ -0,0 +1,2 @@
/libwoj_sandbox.so
/woj_launcher

View File

@ -0,0 +1,15 @@
#!/usr/bin/env bash
set -x
rm -rf woj-sandbox
git clone https://github.com/WHUPRJ/woj-sandbox.git >/dev/null 2>&1 || exit 1
cd woj-sandbox && ./build_libseccomp.sh || exit 1
mkdir -p build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release || exit 1
make -j || exit 1
cd ../..
cp woj-sandbox/build/libwoj_sandbox.so . || exit 1
cp woj-sandbox/build/woj_launcher . || exit 1
rm -rf woj-sandbox

View File

@ -0,0 +1 @@
/testlib

View File

@ -0,0 +1,10 @@
FCMP = $(TESTLIB)/checkers/fcmp
HCMP = $(TESTLIB)/checkers/hcmp
LCMP = $(TESTLIB)/checkers/lcmp
NCMP = $(TESTLIB)/checkers/ncmp
NYESNO = $(TESTLIB)/checkers/nyesno
RCMP4 = $(TESTLIB)/checkers/rcmp4
RCMP6 = $(TESTLIB)/checkers/rcmp6
RCMP9 = $(TESTLIB)/checkers/rcmp9
WCMP = $(TESTLIB)/checkers/wcmp
YESNO = $(TESTLIB)/checkers/yesno

View File

@ -0,0 +1,2 @@
CC = gcc
CFLAGS = -O2 -pipe -Wall -lm -std=c99 -DONLINE_JUDGE

View File

@ -0,0 +1,2 @@
CXX = g++
CFLAGS = -O2 -pipe -Wall -lm -std=c++20 -DONLINE_JUDGE

View File

@ -0,0 +1,7 @@
include ${TEMPLATE}/c.mk ${TEMPLATE}/Judger.mk
compile:
$(CC) $(CFLAGS) -o $(PREFIX)/user/$(USER_PROG).out $(PREFIX)/user/$(USER_PROG).$(LANG)
judge:
$($(CMP)) $(PREFIX)/data/input/$(TEST_NUM).input $(PREFIX)/user/$(TEST_NUM).out.usr $(PREFIX)/data/output/$(TEST_NUM).output > $(PREFIX)/user/$(TEST_NUM).judge 2>&1

View File

@ -0,0 +1,7 @@
include ${TEMPLATE}/cpp.mk ${TEMPLATE}/Judger.mk
compile:
$(CXX) $(CFLAGS) -o $(PREFIX)/user/$(USER_PROG).out $(PREFIX)/user/$(USER_PROG).$(LANG)
judge:
$($(CMP)) $(PREFIX)/data/input/$(TEST_NUM).input $(PREFIX)/user/$(TEST_NUM).out.usr $(PREFIX)/data/output/$(TEST_NUM).output > $(PREFIX)/user/$(TEST_NUM).judge 2>&1

View File

@ -0,0 +1,8 @@
#!/usr/bin/env bash
set -x
rm -rf testlib
git clone https://github.com/MikeMirzayanov/testlib.git >/dev/null 2>&1 || exit 1
rm -rf testlib/.git
rm -rf testlib/tests
cd testlib/checkers || exit 1
parallel clang++ -Ofast -march=native -Wall -pipe -I.. {}.cpp -o {} ::: fcmp hcmp lcmp ncmp nyesno rcmp4 rcmp6 rcmp9 wcmp yesno

View File

@ -0,0 +1,5 @@
/judge/*.out
/data/input/2.*
/data/input/4.*
/data/output/2.*
/data/output/4.*

View File

@ -0,0 +1,64 @@
# 示例题目
## 文件构成
```
.
├── config.json # 题目配置信息
├── data # 数据目录
│ ├── input # 输入数据
│ │ ├── (x).input # 第 x 组输入数据
│ │ └── ...
│ └── output # 输出数据
│ ├── (x).output # 第 x 组输出数据
│ └── ...
└── judge # 评测脚本目录
├── c.Makefile # (optional) 自定义评测脚本
├── prebuild.Makefile # (optional) 题目初始化脚本
└── ...
```
## 详细说明
### 题目配置信息
```json5
{
"Runtime": {
// 运行时配置
"TimeLimit": 1000, // 时间限制 (ms)
"MemoryLimit": 16, // 内存限制 (MB)
"NProcLimit": 1 // 进(线)程 限制
},
"Languages": [
{"Lang": "c", "Type": "custom", "Script": "XYZ.Makefile", "Cmp": ""},
{"Lang": "cpp", "Type": "default", "Script": "", "Cmp": "NCMP"}
], // 支持的语言
"Tasks": [
// 评测点信息
{"Id": 1, "Points": 25}, // 第一个评测点,分值 25 分,使用 ./data/1.? 为测试数据
{"Id": 2, "Points": 25},
{"Id": 3, "Points": 25},
{"Id": 4, "Points": 25}
]
}
```
### 评测脚本
见注释

View File

@ -0,0 +1,17 @@
{
"Runtime": {
"TimeLimit": 1000,
"MemoryLimit": 16,
"NProcLimit": 1
},
"Languages": [
{"Lang": "c", "Type": "custom", "Script": "XYZ.Makefile", "Cmp": ""},
{"Lang": "cpp", "Type": "default", "Script": "", "Cmp": "NCMP"}
],
"Tasks": [
{"Id": 1, "Points": 25},
{"Id": 2, "Points": 25},
{"Id": 3, "Points": 25},
{"Id": 4, "Points": 25}
]
}

View File

@ -0,0 +1 @@
1 2

View File

@ -0,0 +1 @@
-1 -2

View File

@ -0,0 +1 @@
3

View File

@ -0,0 +1 @@
-3

View File

@ -0,0 +1,22 @@
# 默认环境变量有 PREFIX, TEMPLATE, TESTLIB
include ${TEMPLATE}/c.mk ${TEMPLATE}/Judger.mk
# 评测分四个阶段
# 1. prebuild: 用于提前生成测试数据、评测器、spj等工具runner 只执行一次
# 只有 ./data, ./judge 目录可见
# 2. compile: 用于编译用户提交的程序
# 只有 ./user/$(USER_PROG).$(LANG) 和 ./judge 目录可见
# 3. run: 运行用户程序
# 只有 ./data/input/*.input 和 ./user/$(USER_PROG).out 可见
# 用户输出存放于 ./user/?.out.usr
# 使用 woj-sandbox 运行,等效于 $(PREFIX)/user/$(USER_PROG).out < $(PREFIX)/data/input/$(TEST_NUM).input > $(PREFIX)/user/$(TEST_NUM).out.usr
# 4. judge: 用于判定输出结果 环境变量 TEST_NUM 表示当前测试点编号
# 所有目录 ./data ./judge ./user 可见
compile:
$(CC) $(CFLAGS) -o $(PREFIX)/user/$(USER_PROG).out $(PREFIX)/user/$(USER_PROG).$(LANG) $(PREFIX)/judge/gadget.c
judge:
# Rename on *.out.usr or *.judge is not allowed
sed '/gadgets/d' $(PREFIX)/user/$(TEST_NUM).out.usr > $(PREFIX)/user/$(TEST_NUM).out.usr1
$(NCMP) $(PREFIX)/data/input/$(TEST_NUM).input $(PREFIX)/user/$(TEST_NUM).out.usr1 $(PREFIX)/data/output/$(TEST_NUM).output > $(PREFIX)/user/$(TEST_NUM).judge 2>&1

View File

@ -0,0 +1,3 @@
#include<stdio.h>
void construct() __attribute__((constructor(101)));
void construct() { puts("Greetings from gadgets..."); }

View File

@ -0,0 +1,9 @@
#include "testlib.h"
using namespace std;
int main(int argc, char *argv[]) {
registerGen(argc, argv, 1);
println(rnd.next(-1000000000, 1000000000), rnd.next(-1000000000, 1000000000));
}

View File

@ -0,0 +1,12 @@
include ${TEMPLATE}/c.mk ${TEMPLATE}/Judger.mk
prebuild:
clang++ -I$(TESTLIB) -Ofast -o $(PREFIX)/judge/gen.out $(PREFIX)/judge/gen.cpp
@if [ ! -f $(PREFIX)/data/input/2.input ]; then \
$(PREFIX)/judge/gen.out > $(PREFIX)/data/input/2.input; \
python3 -c "print(sum(map(int, input().split())))" < $(PREFIX)/data/input/2.input > $(PREFIX)/data/output/2.output; \
fi
@if [ ! -f $(PREFIX)/data/input/4.input ]; then \
$(PREFIX)/judge/gen.out > $(PREFIX)/data/input/4.input; \
python3 -c "print(sum(map(int, input().split())))" < $(PREFIX)/data/input/4.input > $(PREFIX)/data/output/4.output; \
fi

View File

@ -0,0 +1,10 @@
#!/usr/bin/env bash
COLOR_RED="\e[0;31m"
COLOR_GREEN="\e[0;32m"
COLOR_YELLOW="\e[0;33m"
COLOR_NONE="\e[0m"
function log_info() { echo -e "${COLOR_GREEN}$*${COLOR_NONE}" 1>&2; }
function log_warn() { echo -e "${COLOR_YELLOW}$*${COLOR_NONE}" 1>&2; }
function log_error() { echo -e "${COLOR_RED}$*${COLOR_NONE}" 1>&2; }

View File

@ -0,0 +1,51 @@
#!/usr/bin/env bash
. common.sh
cd "$(dirname "$0")"/../ || exit 1
if [ -f ./.mark.docker ]; then
log_warn "Docker containers already prepared"
log_warn "If you want to re-prepare the containers, please remove the file `pwd`/.mark.docker"
exit 1
fi
# Full
cat <<EOF >ubuntu-full.Dockerfile
FROM ubuntu:22.04
WORKDIR /woj/
# Install dependencies
RUN apt-get update && apt-get install -y gcc g++ clang make cmake autoconf m4 libtool gperf git parallel python3 && apt-get clean && rm -rf /var/lib/apt/lists
# Copy source code
RUN mkdir -p /woj/framework && mkdir -p /woj/problem
COPY framework /woj/framework
# Build
RUN cd /woj/framework/template && ./setup.sh
RUN cd /woj/framework/scripts && ./setup.sh
# Environment
ENV WOJ_LAUNCHER=/woj/framework/scripts/woj_launcher
ENV WOJ_SANDBOX=/woj/framework/scripts/libwoj_sandbox.so
ENV TEMPLATE=/woj/framework/template
ENV TESTLIB=/woj/framework/template/testlib
ENV PREFIX=/woj/problem
EOF
docker build -t woj/ubuntu-full -f ubuntu-full.Dockerfile . || exit 1
rm ubuntu-full.Dockerfile
# Tiny
cat <<EOF >ubuntu-run.Dockerfile
FROM woj/ubuntu-full:latest AS builder
FROM ubuntu:22.04
WORKDIR /woj/problem
RUN mkdir -p /woj/framework/scripts
COPY --from=builder /woj/framework/scripts/libwoj_sandbox.so /woj/framework/scripts/
COPY --from=builder /woj/framework/scripts/woj_launcher /woj/framework/scripts/
EOF
docker build -t woj/ubuntu-run -f ubuntu-run.Dockerfile . || exit 1
rm ubuntu-run.Dockerfile
touch ./.mark.docker

View File

@ -0,0 +1,75 @@
#!/usr/bin/env bash
. common.sh
# get_problem_info
# extract language info and limits
# $1: workspace
# $2: problem name
# $3: language
# exports: Info_Script, Info_Cmp, Info_Num, Info_Limit_Time, Info_Limit_Memory, Info_Limit_NProc
function get_problem_info() {
local err
if [ ! -f "$1/problem/$2/config.json" ]; then
log_error "problem $2 not found"
return 1
fi
parse_language_info "$1" "$2" "$3"
err=$?
if [ "$err" -ne 0 ]; then
return "$err"
fi
parse_limits "$1" "$2"
err=$?
if [ "$err" -ne 0 ]; then
return "$err"
fi
}
function parse_language_info() {
export Info_Script
export Info_Cmp
local lang_config
local lang_type
local lang_script
lang_config=$(jq ".Languages[] | select(.Lang == \"$3\")" "$1/problem/$2/config.json")
if [ -z "$lang_config" ]; then
log_error "language $3 is not supported"
return 1
fi
Info_Cmp=$(echo "$lang_config" | jq -r ".Cmp")
lang_type=$(echo "$lang_config" | jq -r ".Type")
lang_script=$(echo "$lang_config" | jq -r ".Script")
if [ "$lang_type" == "custom" ]; then
Info_Script="/woj/problem/judge/$lang_script"
elif [ "$lang_type" == "default" ]; then
Info_Script="/woj/framework/template/default/$3.Makefile"
else
log_warn "Config file might be corrupted!"
log_error "Unknown language type: $lang_type"
return 1
fi
}
function parse_limits() {
export Info_Limit_Time
export Info_Limit_Memory
export Info_Limit_NProc
export Info_Num
local cfg
cfg="$1/problem/$2/config.json"
Info_Limit_Time=$(jq ".Runtime.TimeLimit" "$cfg")
Info_Limit_Memory=$(jq ".Runtime.MemoryLimit" "$cfg")
Info_Limit_NProc=$(jq ".Runtime.NProcLimit" "$cfg")
Info_Num=$(jq ".Tasks | length" "$1/problem/$2/config.json")
}

View File

@ -0,0 +1,30 @@
#!/usr/bin/env bash
WORKSPACE=$(cd "$(dirname "$0")"/.. && pwd)
. "$WORKSPACE"/scripts/run_timeout.sh
. "$WORKSPACE"/scripts/common.sh
. "$WORKSPACE"/scripts/problem.sh
if [ "$1" == "" ] || [ ! -d "$WORKSPACE/problem/$1" ] || [ "$2" == "" ] || [ ! -d "$WORKSPACE/user/$2" ] || [ -z "$3" ]; then
log_warn "Usage: $0 <problem> <user_dir> <language> <timeout>"
exit 1
fi
get_problem_info "$WORKSPACE" "$1" "$3"
SRC_FILE="$WORKSPACE"/user/"$2"/"$2"."$3"
EXE_FILE="$WORKSPACE"/user/"$2"/"$2".out
LOG_FILE="$WORKSPACE"/user/"$2"/"$2".compile.log
rm -f "$EXE_FILE" && touch "$EXE_FILE"
export TIMEOUT=${4:-60}
docker_run \
-v "$WORKSPACE"/problem/"$1"/judge:/woj/problem/judge:ro \
-v "$SRC_FILE":/woj/problem/user/"$2"."$3":ro \
-v "$EXE_FILE":/woj/problem/user/"$2".out \
-e USER_PROG="$2" \
-e LANG="$3" \
woj/ubuntu-full \
sh -c \
"cd /woj/problem/user && make -f $Info_Script compile"

View File

@ -0,0 +1,40 @@
#!/usr/bin/env bash
WORKSPACE=$(cd "$(dirname "$0")"/.. && pwd)
. "$WORKSPACE"/scripts/run_timeout.sh
. "$WORKSPACE"/scripts/common.sh
. "$WORKSPACE"/scripts/problem.sh
if [ "$1" == "" ] || [ ! -d "$WORKSPACE/problem/$1" ] || [ "$2" == "" ] || [ ! -d "$WORKSPACE/user/$2" ] || [ -z "$3" ]; then
log_warn "Usage: $0 <problem> <user_dir> <language> <timeout>"
exit 1
fi
get_problem_info "$WORKSPACE" "$1" "$3"
TIMEOUT=${4:-60}
for test_num in $(seq "$Info_Num"); do
std_file="$WORKSPACE/problem/$1/data/output/$test_num.output"
ans_file="$WORKSPACE/user/$2/$test_num.out.usr"
jdg_file="$WORKSPACE/user/$2/$test_num.judge"
if [ ! -f "$std_file" ] || [ ! -f "$ans_file" ]; then
log_error "Missing test case $test_num"
exit 1
fi
log_info "Judging test case $test_num"
touch "$jdg_file"
docker_run \
-v "$WORKSPACE"/problem/"$1"/judge:/woj/problem/judge:ro \
-v "$WORKSPACE"/problem/"$1"/data:/woj/problem/data:ro \
-v "$ans_file":/woj/problem/user/"$test_num".out.usr \
-v "$jdg_file":/woj/problem/user/"$test_num".judge \
-e TEST_NUM="$test_num" \
-e CMP="$Info_Cmp" \
woj/ubuntu-full \
sh -c \
"cd /woj/problem/user && make -f $Info_Script judge"
done

View File

@ -0,0 +1,32 @@
#!/usr/bin/env bash
WORKSPACE=$(cd "$(dirname "$0")"/.. && pwd)
. "$WORKSPACE"/scripts/run_timeout.sh
. "$WORKSPACE"/scripts/common.sh
if [ "$1" == "" ] || [ ! -d "$WORKSPACE/problem/$1" ]; then
log_warn "Usage: $0 <problem> <timeout>"
exit 1
fi
if [ -f "$WORKSPACE/problem/$1/.mark.prebuild" ]; then
log_warn "Problem $1 already prebuilt"
log_warn "If you want to re-prebuild the problem, please remove the file $WORKSPACE/problem/$1/.mark.prebuild"
exit 0
fi
if [ ! -f "$WORKSPACE/problem/$1/judge/prebuild.Makefile" ]; then
log_warn "Problem $1 does not have prebuild scripts"
log_warn "$WORKSPACE/problem/$1/.mark.prebuild"
exit 0
fi
TIMEOUT=${2:-300}
docker_run \
-v "$WORKSPACE/problem/$1/data":/woj/problem/data \
-v "$WORKSPACE/problem/$1/judge":/woj/problem/judge \
-e PREFIX=/woj/problem \
woj/ubuntu-full \
sh -c "cd /woj/problem/judge && make -f prebuild.Makefile prebuild && touch .mark.prebuild"
mv "$WORKSPACE/problem/$1/judge/.mark.prebuild" "$WORKSPACE/problem/$1/.mark.prebuild" || exit 1

View File

@ -0,0 +1,71 @@
#!/usr/bin/env bash
WORKSPACE=$(cd "$(dirname "$0")"/.. && pwd)
. "$WORKSPACE"/scripts/run_timeout.sh
. "$WORKSPACE"/scripts/common.sh
. "$WORKSPACE"/scripts/problem.sh
if [ "$1" == "" ] || [ ! -d "$WORKSPACE/problem/$1" ] || [ "$2" == "" ] || [ ! -d "$WORKSPACE/user/$2" ] || [ -z "$3" ]; then
log_warn "Usage: $0 <problem> <user_dir> <language>"
exit 1
fi
if [ ! -f "$WORKSPACE/problem/$1/.mark.prebuild" ]; then
log_warn "Problem $1 has not been prebuilt"
log_warn "Please run 'problem_prebuild.sh $1' first"
exit 1
fi
if [ ! -f "$WORKSPACE/user/$2/$2.out" ]; then
log_warn "User $2 has not been compiled"
log_warn "Please run 'problem_compile.sh ...' first"
exit 1
fi
parse_limits "$WORKSPACE" "$1"
log_info "Running problem $1 for user $2"
log_info "TimeLimit: $Info_Limit_Time"
log_info "MemoryLimit: $Info_Limit_Memory"
log_info "NProcLimit: $Info_Limit_NProc"
# launcher will add 2 more seconds
# here add 3 more seconds
TIMEOUT=$(((LIMIT_TIME + 1000) / 1000 + 3))
log_info "Timeout: $TIMEOUT"
for test_num in $(seq "$Info_Num"); do
test_case="$WORKSPACE/problem/$1/data/input/$test_num.input"
exe_file="$WORKSPACE/user/$2/$2.out"
ans_file="$WORKSPACE/user/$2/$test_num.out.usr"
ifo_file="$WORKSPACE/user/$2/$test_num.info"
if [ ! -f "$test_case" ]; then
log_error "Test case $test_num does not exist"
exit 1
fi
log_info "Running test case $test_num"
rm -f "$ans_file" && touch "$ans_file"
rm -f "$ifo_file" && touch "$ifo_file"
docker_run \
--cpus 1 \
--network none \
-v "$test_case":/woj/problem/data/input/"$test_num".input:ro \
-v "$exe_file":/woj/user/"$2".out:ro \
-v "$ans_file":/woj/user/"$test_num".out.usr \
-v "$ifo_file":/woj/user/"$test_num".info \
woj/ubuntu-run \
sh -c \
"cd /woj/user && /woj/framework/scripts/woj_launcher \
--memory_limit=$Info_Limit_Memory \
--nproc_limit=$Info_Limit_NProc \
--time_limit=$Info_Limit_Time \
--sandbox_path=/woj/framework/scripts/libwoj_sandbox.so \
--sandbox_template=$3 \
--sandbox_action=nothing \
--file_input=/woj/problem/data/input/$test_num.input \
--file_output=/woj/user/$test_num.out.usr \
--file_info=/woj/user/$test_num.info \
--program=/woj/user/$2.out"
done

View File

@ -0,0 +1,16 @@
#!/usr/bin/env bash
. common.sh
function docker_run() {
local timeout=${TIMEOUT:-10}
local log_file=${LOG_FILE:-/dev/stderr}
log_info "Docker run with timeout $timeout"
CONTAINER_NAME=$(uuidgen)
(
sleep "$timeout"
docker kill "$CONTAINER_NAME"
) &
docker run --rm --name "$CONTAINER_NAME" "$@" > "$log_file" 2>&1
pkill -P $$
}

View File

1
resource/runner/user/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/test_user