diff --git a/resource/runner/.gitignore b/resource/runner/.gitignore new file mode 100644 index 0000000..0cf8d7b --- /dev/null +++ b/resource/runner/.gitignore @@ -0,0 +1,2 @@ +.mark.* +*.zip diff --git a/resource/runner/framework/scripts/.gitignore b/resource/runner/framework/scripts/.gitignore new file mode 100644 index 0000000..bf3d440 --- /dev/null +++ b/resource/runner/framework/scripts/.gitignore @@ -0,0 +1,2 @@ +/libwoj_sandbox.so +/woj_launcher diff --git a/resource/runner/framework/scripts/setup.sh b/resource/runner/framework/scripts/setup.sh new file mode 100755 index 0000000..5b60d8b --- /dev/null +++ b/resource/runner/framework/scripts/setup.sh @@ -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 diff --git a/resource/runner/framework/template/.gitignore b/resource/runner/framework/template/.gitignore new file mode 100644 index 0000000..39fbd8f --- /dev/null +++ b/resource/runner/framework/template/.gitignore @@ -0,0 +1 @@ +/testlib \ No newline at end of file diff --git a/resource/runner/framework/template/Judger.mk b/resource/runner/framework/template/Judger.mk new file mode 100644 index 0000000..724c865 --- /dev/null +++ b/resource/runner/framework/template/Judger.mk @@ -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 diff --git a/resource/runner/framework/template/c.mk b/resource/runner/framework/template/c.mk new file mode 100644 index 0000000..756b5be --- /dev/null +++ b/resource/runner/framework/template/c.mk @@ -0,0 +1,2 @@ +CC = gcc +CFLAGS = -O2 -pipe -Wall -lm -std=c99 -DONLINE_JUDGE \ No newline at end of file diff --git a/resource/runner/framework/template/cpp.mk b/resource/runner/framework/template/cpp.mk new file mode 100644 index 0000000..6febe43 --- /dev/null +++ b/resource/runner/framework/template/cpp.mk @@ -0,0 +1,2 @@ +CXX = g++ +CFLAGS = -O2 -pipe -Wall -lm -std=c++20 -DONLINE_JUDGE \ No newline at end of file diff --git a/resource/runner/framework/template/default/c.Makefile b/resource/runner/framework/template/default/c.Makefile new file mode 100644 index 0000000..4f22696 --- /dev/null +++ b/resource/runner/framework/template/default/c.Makefile @@ -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 diff --git a/resource/runner/framework/template/default/cpp.Makefile b/resource/runner/framework/template/default/cpp.Makefile new file mode 100644 index 0000000..bcb89be --- /dev/null +++ b/resource/runner/framework/template/default/cpp.Makefile @@ -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 diff --git a/resource/runner/framework/template/setup.sh b/resource/runner/framework/template/setup.sh new file mode 100755 index 0000000..5576343 --- /dev/null +++ b/resource/runner/framework/template/setup.sh @@ -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 diff --git a/resource/runner/problem/example/.gitignore b/resource/runner/problem/example/.gitignore new file mode 100644 index 0000000..cb3d7fa --- /dev/null +++ b/resource/runner/problem/example/.gitignore @@ -0,0 +1,5 @@ +/judge/*.out +/data/input/2.* +/data/input/4.* +/data/output/2.* +/data/output/4.* diff --git a/resource/runner/problem/example/README.md b/resource/runner/problem/example/README.md new file mode 100644 index 0000000..ba9485d --- /dev/null +++ b/resource/runner/problem/example/README.md @@ -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} + ] +} +``` + +### 评测脚本 + +见注释 + + + + + + + + + + + + + + + diff --git a/resource/runner/problem/example/config.json b/resource/runner/problem/example/config.json new file mode 100644 index 0000000..54dbaff --- /dev/null +++ b/resource/runner/problem/example/config.json @@ -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} + ] +} \ No newline at end of file diff --git a/resource/runner/problem/example/data/input/1.input b/resource/runner/problem/example/data/input/1.input new file mode 100644 index 0000000..8d04f96 --- /dev/null +++ b/resource/runner/problem/example/data/input/1.input @@ -0,0 +1 @@ +1 2 diff --git a/resource/runner/problem/example/data/input/3.input b/resource/runner/problem/example/data/input/3.input new file mode 100644 index 0000000..6fa0970 --- /dev/null +++ b/resource/runner/problem/example/data/input/3.input @@ -0,0 +1 @@ +-1 -2 \ No newline at end of file diff --git a/resource/runner/problem/example/data/output/1.output b/resource/runner/problem/example/data/output/1.output new file mode 100644 index 0000000..00750ed --- /dev/null +++ b/resource/runner/problem/example/data/output/1.output @@ -0,0 +1 @@ +3 diff --git a/resource/runner/problem/example/data/output/3.output b/resource/runner/problem/example/data/output/3.output new file mode 100644 index 0000000..7d105a7 --- /dev/null +++ b/resource/runner/problem/example/data/output/3.output @@ -0,0 +1 @@ +-3 \ No newline at end of file diff --git a/resource/runner/problem/example/judge/XYZ.Makefile b/resource/runner/problem/example/judge/XYZ.Makefile new file mode 100644 index 0000000..32b8f1c --- /dev/null +++ b/resource/runner/problem/example/judge/XYZ.Makefile @@ -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 diff --git a/resource/runner/problem/example/judge/gadget.c b/resource/runner/problem/example/judge/gadget.c new file mode 100644 index 0000000..bc08434 --- /dev/null +++ b/resource/runner/problem/example/judge/gadget.c @@ -0,0 +1,3 @@ +#include +void construct() __attribute__((constructor(101))); +void construct() { puts("Greetings from gadgets..."); } diff --git a/resource/runner/problem/example/judge/gen.cpp b/resource/runner/problem/example/judge/gen.cpp new file mode 100644 index 0000000..55b3e38 --- /dev/null +++ b/resource/runner/problem/example/judge/gen.cpp @@ -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)); +} diff --git a/resource/runner/problem/example/judge/prebuild.Makefile b/resource/runner/problem/example/judge/prebuild.Makefile new file mode 100644 index 0000000..39daf6f --- /dev/null +++ b/resource/runner/problem/example/judge/prebuild.Makefile @@ -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 diff --git a/resource/runner/scripts/common.sh b/resource/runner/scripts/common.sh new file mode 100755 index 0000000..cd875a6 --- /dev/null +++ b/resource/runner/scripts/common.sh @@ -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; } diff --git a/resource/runner/scripts/prepare_container.sh b/resource/runner/scripts/prepare_container.sh new file mode 100755 index 0000000..af10035 --- /dev/null +++ b/resource/runner/scripts/prepare_container.sh @@ -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 <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 <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 diff --git a/resource/runner/scripts/problem.sh b/resource/runner/scripts/problem.sh new file mode 100755 index 0000000..ef407a2 --- /dev/null +++ b/resource/runner/scripts/problem.sh @@ -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") +} diff --git a/resource/runner/scripts/problem_compile.sh b/resource/runner/scripts/problem_compile.sh new file mode 100755 index 0000000..874dbb8 --- /dev/null +++ b/resource/runner/scripts/problem_compile.sh @@ -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 " + 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" diff --git a/resource/runner/scripts/problem_judge.sh b/resource/runner/scripts/problem_judge.sh new file mode 100755 index 0000000..9a743aa --- /dev/null +++ b/resource/runner/scripts/problem_judge.sh @@ -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 " + 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 diff --git a/resource/runner/scripts/problem_prebuild.sh b/resource/runner/scripts/problem_prebuild.sh new file mode 100755 index 0000000..de3b45d --- /dev/null +++ b/resource/runner/scripts/problem_prebuild.sh @@ -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 " + 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 diff --git a/resource/runner/scripts/problem_run.sh b/resource/runner/scripts/problem_run.sh new file mode 100755 index 0000000..8168fd4 --- /dev/null +++ b/resource/runner/scripts/problem_run.sh @@ -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 " + 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 diff --git a/resource/runner/scripts/run_timeout.sh b/resource/runner/scripts/run_timeout.sh new file mode 100755 index 0000000..0cafef9 --- /dev/null +++ b/resource/runner/scripts/run_timeout.sh @@ -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 $$ +} diff --git a/resource/runner/tmp/.gitkeep b/resource/runner/tmp/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/resource/runner/user/.gitignore b/resource/runner/user/.gitignore new file mode 100644 index 0000000..2699330 --- /dev/null +++ b/resource/runner/user/.gitignore @@ -0,0 +1 @@ +/test_user \ No newline at end of file