|
|
#!/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 ]] |