Files
mysysy/test_script/runit.sh
2025-06-25 14:37:46 +08:00

226 lines
11 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/bash
# runit.sh - 用于编译和测试 SysY 程序的脚本
# 此脚本位于 mysysy/test_script/
# 定义相对于脚本位置的目录
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd)"
TESTDATA_DIR="${SCRIPT_DIR}/../testdata"
BUILD_BIN_DIR="${SCRIPT_DIR}/../build/bin"
LIB_DIR="${SCRIPT_DIR}/../lib"
TMP_DIR="/home/ladev987/paraComp/debug/share_folder"
# 定义编译器和模拟器
SYSYC="${BUILD_BIN_DIR}/sysyc"
GCC_RISCV64="riscv64-linux-gnu-gcc"
QEMU_RISCV64="qemu-riscv64"
# 标志,用于确定是否应该生成和运行可执行文件
EXECUTE_MODE=false
# 显示帮助信息的函数
show_help() {
echo "用法: $0 [选项]"
echo "此脚本用于编译 .sy 文件,并可选择性地运行它们进行测试。"
echo ""
echo "选项:"
echo " -e, --executable 编译为可执行文件,运行可执行文件,并比较输出(如果存在 .in/.out 文件)。"
echo " 如果 .out 文件的最后一行是整数,则将其视为期望的返回值进行比较,其余内容视为期望的标准输出。"
echo " 如果 .out 文件的最后一行不是整数,则将整个 .out 文件视为期望的标准输出进行比较。"
echo " 如果不存在 .in/.out 文件,则打印返回码。"
echo " -c, --clean 清理 'tmp' 目录下的所有生成文件。"
echo " -h, --help 显示此帮助信息并退出。"
echo ""
echo "编译步骤:"
echo "1. 调用 sysyc 将 .sy 编译为 .s (RISC-V 汇编)。"
echo "2. 调用 riscv64-linux-gnu-gcc 将 .s 编译为可执行文件,并链接 -L../lib/ -lsysy_riscv -static。"
echo "3. 调用 qemu-riscv64 执行编译后的文件。"
echo "4. 根据 .out 文件内容(最后一行是否为整数)决定是进行返回值比较、标准输出比较,或两者都进行。"
echo "5. 如果没有 .in/.out 文件,则打印可执行文件的返回值。"
}
# 清理临时文件的函数
clean_tmp() {
echo "正在清理临时目录: ${TMP_DIR}"
rm -rf "${TMP_DIR}"/*
# 如果需要,也可以根据 clean.sh 示例清理其他特定文件
# rm -rf "${SCRIPT_DIR}"/*.s "${SCRIPT_DIR}"/*.ll "${SCRIPT_DIR}"/*clang "${SCRIPT_DIR}"/*sysyc
# rm -rf "${SCRIPT_DIR}"/*_riscv64
}
# 如果临时目录不存在,则创建它
mkdir -p "${TMP_DIR}"
# 解析命令行参数
while [[ "$#" -gt 0 ]]; do
case "$1" in
-e|--executable)
EXECUTE_MODE=true
shift
;;
-c|--clean)
clean_tmp
exit 0
;;
-h|--help)
show_help
exit 0
;;
*)
echo "未知选项: $1"
show_help
exit 1
;;
esac
done
echo "SysY 测试运行器启动..."
echo "输入目录: ${TESTDATA_DIR}"
echo "临时目录: ${TMP_DIR}"
echo "执行模式已启用: ${EXECUTE_MODE}"
echo ""
# 查找 testdata 目录及其子目录中的所有 .sy 文件
# 遍历找到的每个 .sy 文件
find "${TESTDATA_DIR}" -name "*.sy" | while read sy_file; do
# 获取 .sy 文件的基本名称例如21_if_test2
# 这也处理了文件位于子目录中的情况例如functional/21_if_test2.sy
relative_path_no_ext=$(realpath --relative-to="${TESTDATA_DIR}" "${sy_file%.*}")
# 将斜杠替换为下划线,用于输出文件名,以避免冲突并保持结构
output_base_name=$(echo "${relative_path_no_ext}" | tr '/' '_')
# 定义汇编文件、可执行文件、输入文件和输出文件的路径
assembly_file="${TMP_DIR}/${output_base_name}_sysyc_riscv64.s"
executable_file="${TMP_DIR}/${output_base_name}_sysyc_riscv64"
input_file="${sy_file%.*}.in"
output_reference_file="${sy_file%.*}.out"
output_actual_file="${TMP_DIR}/${output_base_name}_sysyc_riscv64.actual_out"
echo "正在处理: $(basename "$sy_file")"
echo " SY 文件: ${sy_file}"
# 步骤 1: 使用 sysyc 编译 .sy 到 .s
echo " 使用 sysyc 编译: ${SYSYC} -s asm \"${sy_file}\" > \"${assembly_file}\""
"${SYSYC}" -s asm "${sy_file}" > "${assembly_file}"
if [ $? -ne 0 ]; then
echo -e "\e[31m错误: SysY 编译 ${sy_file} 失败\e[0m"
continue
fi
echo " 生成的汇编文件: ${assembly_file}"
# 只有当 EXECUTE_MODE 为 true 时才继续生成和执行可执行文件
if ${EXECUTE_MODE}; then
# 步骤 2: 使用 riscv64-linux-gnu-gcc 编译 .s 到可执行文件
echo " 使用 gcc 编译: ${GCC_RISCV64} \"${assembly_file}\" -o \"${executable_file}\" -L\"${LIB_DIR}\" -lsysy_riscv -static"
"${GCC_RISCV64}" "${assembly_file}" -o "${executable_file}" -L"${LIB_DIR}" -lsysy_riscv -static
if [ $? -ne 0 ]; then
echo -e "\e[31m错误: GCC 编译 ${assembly_file} 失败\e[0m"
continue
fi
echo " 生成的可执行文件: ${executable_file}"
# 步骤 3, 4, 5: 执行编译后的文件并比较/报告结果
echo " 正在执行: ${QEMU_RISCV64} \"${executable_file}\""
# 检查是否存在 .out 文件
if [ -f "${output_reference_file}" ]; then
# 尝试从 .out 文件中提取期望的返回码和期望的标准输出
# 获取 .out 文件的最后一行,去除空白字符
LAST_LINE_TRIMMED=$(tail -n 1 "${output_reference_file}" | tr -d '[:space:]')
# 检查最后一行是否为纯整数 (允许正负号)
if [[ "$LAST_LINE_TRIMMED" =~ ^[-+]?[0-9]+$ ]]; then
# 假设最后一行是期望的返回码
EXPECTED_RETURN_CODE="$LAST_LINE_TRIMMED"
# 创建一个只包含期望标准输出的临时文件 (所有行除了最后一行)
EXPECTED_STDOUT_FILE="${TMP_DIR}/${output_base_name}_sysyc_riscv64.expected_stdout"
# 使用 head -n -1 来获取除了最后一行之外的所有行。如果文件只有一行,则生成一个空文件。
head -n -1 "${output_reference_file}" > "${EXPECTED_STDOUT_FILE}"
echo " 检测到 .out 文件同时包含标准输出和期望的返回码。"
echo " 期望返回码: ${EXPECTED_RETURN_CODE}"
if [ -s "${EXPECTED_STDOUT_FILE}" ]; then # -s 检查文件是否非空
echo " 期望标准输出文件: ${EXPECTED_STDOUT_FILE}"
else
echo " 期望标准输出为空。"
fi
# 执行程序,捕获实际返回码和实际标准输出
if [ -f "${input_file}" ]; then
echo " 使用输入文件: ${input_file}"
"${QEMU_RISCV64}" "${executable_file}" < "${input_file}" > "${output_actual_file}"
else
"${QEMU_RISCV64}" "${executable_file}" > "${output_actual_file}"
fi
ACTUAL_RETURN_CODE=$? # 捕获执行状态
# 比较实际返回码与期望返回码
if [ "$ACTUAL_RETURN_CODE" -eq "$EXPECTED_RETURN_CODE" ]; then
echo -e "\e[32m 返回码测试成功: ${sy_file} 的返回码 (${ACTUAL_RETURN_CODE}) 与期望值 (${EXPECTED_RETURN_CODE}) 匹配\e[0m"
else
echo -e "\e[31m 返回码测试失败: ${sy_file} 的返回码不匹配。期望: ${EXPECTED_RETURN_CODE}, 实际: ${ACTUAL_RETURN_CODE}\e[0m"
fi
# 比较实际标准输出与期望标准输出
# 注意实际输出文件可能包含一个额外的换行符如果程序没有putch(10)
# 确保比较时,如果期望输出为空,且实际输出也为空或仅包含空白字符,则视为匹配
# 这里我们假设 output_actual_file 已经包含程序的所有标准输出
diff -q "${output_actual_file}" "${EXPECTED_STDOUT_FILE}" >/dev/null 2>&1
if [ $? -eq 0 ]; then
echo -e "\e[32m 标准输出测试成功: 输出与 ${sy_file} 的参考输出匹配\e[0m"
else
echo -e "\e[31m 标准输出测试失败: ${sy_file} 的输出不匹配\e[0m"
echo " 差异:"
diff "${output_actual_file}" "${EXPECTED_STDOUT_FILE}"
fi
else
# 最后一行不是纯整数,将整个 .out 文件视为纯标准输出
echo " 检测到 .out 文件为纯标准输出参考。正在与输出文件比较: ${output_reference_file}"
# 使用输入文件(如果存在)运行可执行文件,并将输出重定向到临时文件
if [ -f "${input_file}" ]; then
echo " 使用输入文件: ${input_file}"
"${QEMU_RISCV64}" "${executable_file}" < "${input_file}" > "${output_actual_file}"
else
"${QEMU_RISCV64}" "${executable_file}" > "${output_actual_file}"
fi
EXEC_STATUS=$? # 捕获执行状态
if [ $EXEC_STATUS -ne 0 ]; then
echo -e "\e[33m警告: 可执行文件 ${sy_file} 以非零状态 ${EXEC_STATUS} 退出 (纯输出比较模式)。请检查程序逻辑或其是否应返回此状态。\e[0m"
fi
# 比较实际输出与参考输出
diff -q "${output_actual_file}" "${output_reference_file}" >/dev/null 2>&1
if [ $? -eq 0 ]; then
echo -e "\e[32m 成功: 输出与 ${sy_file} 的参考输出匹配\e[0m"
else
echo -e "\e[31m 失败: ${sy_file} 的输出不匹配\e[0m"
echo " 差异:"
diff "${output_actual_file}" "${output_reference_file}"
fi
fi
elif [ -f "${input_file}" ]; then
# 只有 .in 文件存在,使用输入运行并报告退出码(无参考输出)
echo " 使用输入文件: ${input_file}"
echo " 没有 .out 文件进行比较。正在运行并报告返回码。"
"${QEMU_RISCV64}" "${executable_file}" < "${input_file}"
EXEC_STATUS=$?
echo " ${sy_file} 的返回码: ${EXEC_STATUS}"
else
# .in 和 .out 文件都不存在,只运行并报告退出码
echo " 未找到 .in 或 .out 文件。正在运行并报告返回码。"
"${QEMU_RISCV64}" "${executable_file}"
EXEC_STATUS=$?
echo " ${sy_file} 的返回码: ${EXEC_STATUS}"
fi
else
echo " 跳过执行模式。仅生成汇编文件。"
fi
echo "" # 为测试用例之间添加一个空行,以提高可读性
done
echo "脚本完成。"