#!/usr/bin/env bash # Lab2 自动化构建 + IR 验证测评脚本 # 用法: # bash scripts/lab2_build_test.sh [--save-ir] [测试目录...] # # 选项: # --save-ir 保存每个测试用例生成的 IR 到 output/lab2/ 目录 # 默认只进行通过/失败统计,不保存 IR # # 退出码: # 0 全部用例验证通过 # 1 存在验证失败用例 set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" COMPILER="$REPO_ROOT/build/bin/compiler" ANTLR_JAR="$REPO_ROOT/third_party/antlr-4.13.2-complete.jar" VERIFY_SCRIPT="$REPO_ROOT/scripts/verify_ir.sh" # 输出目录 OUTPUT_DIR="$REPO_ROOT/output/lab2" LOG_DIR="$REPO_ROOT/output/logs/lab2" # 颜色输出 RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' # 默认不保存 IR SAVE_IR=false # 解析命令行参数 TEST_DIRS=() while [[ $# -gt 0 ]]; do case "$1" in --save-ir) SAVE_IR=true shift ;; *) TEST_DIRS+=("$1") shift ;; esac done # 如果没有指定测试目录,使用默认 if [[ ${#TEST_DIRS[@]} -eq 0 ]]; then TEST_DIRS=( "$REPO_ROOT/test/test_case/functional" "$REPO_ROOT/test/test_case/performance" ) fi # 检查必要文件是否存在 if [ ! -f "$VERIFY_SCRIPT" ]; then echo -e "${RED}错误: 验证脚本 $VERIFY_SCRIPT 不存在${NC}" exit 1 fi # 创建输出目录 mkdir -p "$OUTPUT_DIR" "$LOG_DIR" # ─── Step 1:生成 ANTLR Lexer/Parser ──────────────────────────────────────────────── echo "==> [1/3] 生成 ANTLR Lexer/Parser ..." mkdir -p "$REPO_ROOT/build/generated/antlr4" java -jar "$ANTLR_JAR" \ -Dlanguage=Cpp \ -visitor -no-listener \ -Xexact-output-dir \ -o "$REPO_ROOT/build/generated/antlr4" \ "$REPO_ROOT/src/antlr4/SysY.g4" echo " Lexer/Parser 生成完毕" # ─── Step 2:CMake 完整构建(不启用 PARSE_ONLY)──────────────────────────────────── echo "==> [2/3] CMake 构建完整编译器..." cmake -S "$REPO_ROOT" -B "$REPO_ROOT/build" \ -DCMAKE_BUILD_TYPE=Release \ > /dev/null cmake --build "$REPO_ROOT/build" -j "$(nproc)" 2>&1 | grep -E "error:|warning:|Built target|Linking" || true echo " 构建完毕:$COMPILER" # ─── Step 3:批量验证 IR 生成与运行 ───────────────────────────────────────────────── echo "==> [3/3] 批量验证 IR 生成与运行 ..." PASS=0 FAIL=0 FAIL_LIST=() # 定义测试单个文件的函数,便于统一错误处理 test_one() { local sy_file="$1" local rel="$2" local basename="$(basename "$sy_file" .sy)" local safe_name="${rel//\//_}" # 将路径中的 '/' 替换为 '_' local result_dir="$OUTPUT_DIR/$basename" local log_file="$LOG_DIR/${safe_name}.log" # 创建结果目录 mkdir -p "$result_dir" if $SAVE_IR; then # 生成 IR 文件到指定目录 local ir_file="$OUTPUT_DIR/${safe_name}.ir" mkdir -p "$(dirname "$ir_file")" # 尝试生成 IR if ! "$COMPILER" --emit-ir "$sy_file" > "$ir_file" 2>&1; then # 编译失败,记录日志 { echo "IR generation failed for $rel" echo "Command: $COMPILER --emit-ir $sy_file" echo "--- Output ---" cat "$ir_file" } > "$log_file" 2>&1 return 1 fi # 运行验证脚本(不再重复生成 IR,直接使用生成的 IR?但 verify_ir.sh 内部会重新编译,所以我们仍调用它验证运行) # 这里我们希望验证脚本将中间文件放到 result_dir 中 if ! "$VERIFY_SCRIPT" "$sy_file" "$result_dir" --run > /dev/null 2>&1; then # 验证失败,记录日志 { echo "Verification failed for $rel" echo "Command: $VERIFY_SCRIPT $sy_file $result_dir --run" # 可能还需要捕获验证脚本的输出,但 verify_ir.sh 已经将错误打印到 stderr,我们无法直接捕获 # 这里我们只能记录一些基本信息,并建议查看 result_dir 中的输出 echo "Check result directory: $result_dir" # 如果 result_dir 中有输出文件,可以尝试附加 if [ -f "$result_dir/out" ]; then echo "--- Program output ---" cat "$result_dir/out" fi if [ -f "$result_dir/err" ]; then echo "--- Program error ---" cat "$result_dir/err" fi } > "$log_file" 2>&1 return 1 fi else # 不保存 IR,直接运行验证脚本 if ! "$VERIFY_SCRIPT" "$sy_file" "$result_dir" --run > /dev/null 2>&1; then { echo "Verification failed for $rel" echo "Command: $VERIFY_SCRIPT $sy_file $result_dir --run" echo "Check result directory: $result_dir" if [ -f "$result_dir/out" ]; then echo "--- Program output ---" cat "$result_dir/out" fi if [ -f "$result_dir/err" ]; then echo "--- Program error ---" cat "$result_dir/err" fi } > "$log_file" 2>&1 return 1 fi fi return 0 } for TEST_DIR in "${TEST_DIRS[@]}"; do if [[ ! -d "$TEST_DIR" ]]; then echo -e " ${YELLOW}警告:目录不存在,跳过:$TEST_DIR${NC}" continue fi while IFS= read -r -d '' sy_file; do rel="$(realpath --relative-to="$REPO_ROOT" "$sy_file")" echo -n "测试 $rel ... " if test_one "$sy_file" "$rel"; then echo -e "${GREEN}PASS${NC}" ((PASS++)) || true else echo -e "${RED}FAIL${NC}" FAIL_LIST+=("$rel") ((FAIL++)) || true fi done < <(find "$TEST_DIR" -name "*.sy" -print0 | sort -z) done # ─── 汇总 ───────────────────────────────────────────────────────────────────── echo "" echo "──────────────────────────────────────────" echo -e " 测试结果:${GREEN}${PASS} PASS${NC} / ${RED}${FAIL} FAIL${NC} / 总计 $((PASS + FAIL))" if [[ ${#FAIL_LIST[@]} -gt 0 ]]; then echo "" echo " 失败用例:" for f in "${FAIL_LIST[@]}"; do echo -e " ${RED}- $f${NC}" echo " 日志文件: $LOG_DIR/${f//\//_}.log" done fi echo "──────────────────────────────────────────" [[ $FAIL -eq 0 ]]