You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

201 lines
6.9 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#!/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 2CMake 完整构建(不启用 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 ]]