set -euo pipefail
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
compile_mode="default"
need_debug="NONE"
ascend_home_path="${ASCEND_HOME_PATH:-}"
build_dir="${ROOT_DIR}/build_cmake/ccec_build"
bin_dir="${build_dir}/tests"
catch2_src="${ROOT_DIR}/3rdparty/Catch2"
catch2_build="${ROOT_DIR}/build_cmake/_catch2_gxx_build"
catch2_install="${ROOT_DIR}/build_cmake/_catch2_gxx_install"
doxygen_src="${ROOT_DIR}/3rdparty/doxygen"
doxygen_build="${ROOT_DIR}/build_cmake/_doxygen_gxx_build"
doxygen_install="${ROOT_DIR}/build_cmake/_doxygen_gxx_install"
function clean_build() {
echo "[INFO] 清理构建目录..."
if [ -d "${ROOT_DIR}/build" ]; then
rm -rf "${ROOT_DIR}/build"
fi
if [ -d "${ROOT_DIR}/build_cmake" ]; then
rm -rf "${ROOT_DIR}/build_cmake"
fi
if [ -f "${ROOT_DIR}/kernel.o" ]; then
rm -rf "${ROOT_DIR}/kernel.o"
fi
echo "[INFO] 清理完成。"
}
function show_help() {
cat << EOF
集合测试框架构建和运行脚本
为华为昇腾平台的集合库提供一站式测试管理工具
用法: $0 [选项] [参数]
选项:
-h, --help 显示此帮助信息
-m, --mode MODE 设置编译模式: default 或 kernel (默认: default)
-d, --debug 启用调试模式
-c, --clean 清理构建目录
-b, --build 构建整个项目(包含清理)
-r, --run [PATTERN] 运行所有测试
-a, --all 清理、构建并运行所有测试(完整流程)
-p, --performance 构建性能测试
-rp, --run-performance 运行性能测试
-doc, --document 生成项目文档
--ascend-home PATH 设置 ASCEND_HOME_PATH 路径
--test-name NAME 指定要运行的测试可执行文件名或关键字
--test-pattern PATTERN 指定 Catch2 测试标签过滤模式
示例:
$0 -c # 清理构建目录
$0 -b # 构建整个项目
$0 -r # 运行所有测试
$0 -a # 完整流程:清理、构建、运行测试
$0 -b -d # 调试模式构建
$0 -b -m kernel # 使用 kernel 模式构建
$0 -r --test-name static_map # 运行所有 static_map 相关测试
$0 -r --test-pattern "[insert]" # 运行所有 insert 标签的测试
$0 -p # 构建性能测试
$0 -rp # 运行性能测试
$0 -doc # 生成项目文档
$0 -b --ascend-home /usr/local/Ascend/ascend-toolkit/latest
EOF
}
function set_compile_config() {
case "$1" in
"default"|"kernel")
compile_mode="$1"
echo "[INFO] 设置编译模式为: $compile_mode"
;;
"--debug")
need_debug="--debug"
echo "[INFO] 启用调试模式"
;;
*)
echo "错误: 参数 '$1' 无效。支持的参数: default, kernel, --debug"
exit 1
;;
esac
}
function compile_testframework() {
echo "[INFO] 开始构建测试框架..."
mkdir -p "${ROOT_DIR}/build"
if [ "$compile_mode" == "kernel" ]; then
echo "[INFO] kernel 模式,跳过普通构建。"
return 0
fi
if [ "$need_debug" == "--debug" ]; then
debug_flag="DEBUG"
else
debug_flag=""
fi
echo "[INFO] 构建完成。"
}
function build_catch2() {
echo "[INFO] 构建 Catch2..."
mkdir -p "${catch2_src}"
if [ ! -d "${catch2_src}/.git" ]; then
echo "[3rdparty] 克隆 Catch2 到 ${catch2_src}"
git clone --depth 1 --branch v3.5.4 https://github.com/catchorg/Catch2.git "${catch2_src}"
fi
mkdir -p "${catch2_build}" "${catch2_install}"
echo "[Catch2] 使用 g++ 构建/安装 -> ${catch2_install}"
cmake -S "${catch2_src}" -B "${catch2_build}" \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_CXX_COMPILER=g++ \
-DCMAKE_INSTALL_PREFIX="${catch2_install}" \
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
cmake --build "${catch2_build}" -j"$(nproc)"
cmake --install "${catch2_build}"
if [ -d "${catch2_install}/lib/cmake/Catch2" ]; then
catch2_dir="${catch2_install}/lib/cmake/Catch2"
elif [ -d "${catch2_install}/lib64/cmake/Catch2" ]; then
catch2_dir="${catch2_install}/lib64/cmake/Catch2"
else
echo "错误: 在 ${catch2_install} 中找不到 Catch2 cmake 包目录"
exit 1
fi
echo "[INFO] Catch2 构建完成。"
}
function build_main_project() {
echo "[INFO] 构建主项目..."
if [ -z "${ascend_home_path}" ]; then
echo "错误: ASCEND_HOME_PATH 未设置。"
echo "示例:"
echo " export ASCEND_HOME_PATH=/usr/local/Ascend/ascend-toolkit/latest"
echo "或使用: $0 --ascend-home /path/to/ascend"
exit 1
fi
mkdir -p "${build_dir}"
echo "[Main] 使用 ccec 配置 (Catch2_DIR=${catch2_dir})"
cmake -S "${ROOT_DIR}" -B "${build_dir}" \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_CXX_COMPILER=ccec \
-DCatch2_DIR="${catch2_dir}" \
-DASCEND_HOME_PATH="${ascend_home_path}" \
-DBUILD_TESTS=ON
echo "[Main] 使用 ccec 构建"
cmake --build "${build_dir}" -j"$(nproc)"
echo "[INFO] 主项目构建完成。"
}
function build_performance_project() {
echo "[INFO] 构建性能测试..."
if [ -z "${ascend_home_path}" ]; then
echo "错误: ASCEND_HOME_PATH 未设置。"
echo "示例:"
echo " export ASCEND_HOME_PATH=/usr/local/Ascend/ascend-toolkit/latest"
echo "或使用: $0 --ascend-home /path/to/ascend"
exit 1
fi
perf_build_dir="${ROOT_DIR}/build/performance"
mkdir -p "${perf_build_dir}"
echo "[Performance] 使用 ccec 配置"
cmake -S "${ROOT_DIR}" -B "${perf_build_dir}" \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_CXX_COMPILER=ccec \
-DASCEND_HOME_PATH="${ascend_home_path}" \
-DBUILD_PERFORMANCE=ON \
-DBUILD_TESTS=OFF
echo "[Performance] 使用 ccec 构建"
cmake --build "${perf_build_dir}" -j"$(nproc)"
echo "[INFO] 性能测试构建完成,可执行文件在 build/performance/ 下。"
}
function run_performance_test() {
local perf_bin_dir="${ROOT_DIR}/build/performance"
local test_count=0
if [ ! -d "${perf_bin_dir}" ]; then
echo "ERROR: ${perf_bin_dir} not found. 先运行构建: $0 -p"
exit 1
fi
local bins=()
while IFS= read -r -d '' file; do
bins+=("$file")
done < <(find "${perf_bin_dir}" -type f -name "*_perf_*" -print0)
if [ "${#bins[@]}" -eq 0 ]; then
echo "ERROR: no performance test binaries found in ${perf_bin_dir}"
exit 1
fi
echo "[INFO] 找到 ${#bins[@]} 个性能测试文件"
for b in "${bins[@]}"; do
if [ ! -x "${b}" ]; then
continue
fi
local test_name="${b#${perf_bin_dir}/}"
echo "==> RUN: ${test_name}"
"${b}"
test_count=$((test_count + 1))
done
echo ""
echo "==> 完成 ${test_count} 个性能测试用例"
}
function run_tests() {
local SEL="${1:-}"
local PATTERN="${2:-}"
if [ ! -d "${bin_dir}" ]; then
echo "ERROR: ${bin_dir} not found. 先运行构建: $0 build"
exit 1
fi
shopt -s nullglob
bins=()
if [ -z "${SEL}" ]; then
bins=( "${bin_dir}/collection_tests_"* )
else
if [ -x "${bin_dir}/${SEL}" ]; then
bins=( "${bin_dir}/${SEL}" )
else
bins=( "${bin_dir}/collection_tests_"*"${SEL}"* )
fi
fi
if [ "${#bins[@]}" -eq 0 ]; then
echo "ERROR: no test binaries matched."
echo " BIN_DIR=${bin_dir}"
echo " SEL=${SEL}"
echo " 可执行示例:collection_tests_static_map_insert_test"
exit 1
fi
local passed=0
local failed=0
local skipped=0
local failed_names=()
local skipped_names=()
for b in "${bins[@]}"; do
if [ ! -x "${b}" ]; then
continue
fi
if [ -n "${PATTERN}" ]; then
set +e
test_output=$("${b}" "${PATTERN}" 2>&1)
rc=$?
set -e
if echo "${test_output}" | grep -qE "No tests ran"; then
echo " [SKIP] $(basename "${b}") — no matching tests"
skipped=$((skipped + 1))
skipped_names+=("$(basename "${b}")")
continue
fi
echo ""
echo "==> RUN: $(basename "${b}") ${PATTERN}"
printf '%s\n' "${test_output}"
if [ "${rc}" -eq 0 ]; then
echo "==> PASS: $(basename "${b}")"
passed=$((passed + 1))
else
echo "==> FAIL: $(basename "${b}") (exit code: ${rc})"
failed=$((failed + 1))
failed_names+=("$(basename "${b}")")
fi
else
echo ""
echo "==> RUN: $(basename "${b}")"
set +e
"${b}"
rc=$?
set -e
if [ "${rc}" -eq 0 ]; then
echo "==> PASS: $(basename "${b}")"
passed=$((passed + 1))
else
echo "==> FAIL: $(basename "${b}") (exit code: ${rc})"
failed=$((failed + 1))
failed_names+=("$(basename "${b}")")
fi
fi
done
echo ""
echo "=============================================="
echo " 测试汇总:共 $((passed + failed)) 个,通过 ${passed} 个,失败 ${failed} 个,跳过 ${skipped} 个"
if [ "${failed}" -gt 0 ]; then
echo " 失败列表:"
for fn in "${failed_names[@]}"; do
echo " - ${fn}"
done
fi
if [ "${skipped}" -gt 0 ]; then
echo " 跳过列表:"
for sn in "${skipped_names[@]}"; do
echo " - ${sn}"
done
fi
echo "=============================================="
if [ "${failed}" -gt 0 ]; then
exit 1
fi
if [ "${passed}" -eq 0 ] && [ "${failed}" -eq 0 ] && [ "${skipped}" -gt 0 ]; then
echo "ERROR: 所有测试都被跳过,未匹配到任何测试用例。请检查 --test-pattern 拼写。"
exit 1
fi
}
function build_doxygen()
{
if [ -d "$doxygen_install/bin" ]; then
return 0
fi
sys_info=$(awk -F= '/^NAME/{print $2}' /etc/os-release)
if [[ "$sys_info" == *"EulerOS"* ]]; then
yum -y install flex bison
elif [[ "$sys_info" == *"Ubuntu"* ]]; then
apt-get -y install flex bison
fi
mkdir -p "$doxygen_build" "$doxygen_install"
if [ ! -d "$doxygen_src/.git" ]; then
git clone --depth 1 --branch "Release_1_9_6" https://github.com/doxygen/doxygen.git "${doxygen_src}"
fi
cd "$doxygen_src" || return 1
rm -rf build && mkdir build && cd build
cmake .. -DCMAKE_INSTALL_PREFIX=$doxygen_install
cmake --build . --parallel $(nproc)
cmake --install . > /dev/null
}
function build_documentation() {
echo "[INFO] 开始生成 Doxygen文档..."
if ! command -v doxygen &> /dev/null; then
build_doxygen
export PATH="$doxygen_install/bin:$PATH"
if ! command -v doxygen &> /dev/null; then
echo "[ERROR] doxygen 安装后仍无法使用,请检查安装过程。"
return 1
fi
fi
if [ ! -f "${ROOT_DIR}/doxygen/Doxyfile" ]; then
echo "[ERROR] Doxygen 文档生成失败, ${ROOT_DIR}/doxygen/Doxyfile 配置文件不存在"
return 1
fi
mkdir -p "${ROOT_DIR}/docs/"
cd "${ROOT_DIR}"
echo "[INFO] 正在执行 doxygen 生成文档..."
if doxygen "${ROOT_DIR}/doxygen/Doxyfile"; then
echo "[INFO] Doxygen 文档生成成功,输出到 ${ROOT_DIR}/docs/html"
return 0
else
echo "[ERROR] Doxygen 文档生成失败,请检查配置文件和doxygen安装。"
return 1
fi
}
function main() {
local action=""
local test_name=""
local test_pattern=""
while [[ $# -gt 0 ]]; do
case "$1" in
-h|--help)
show_help
exit 0
;;
-m|--mode)
if [[ -z "$2" ]] || [[ "$2" =~ ^- ]]; then
echo "错误: --mode 需要一个参数"
exit 1
fi
set_compile_config "$2"
shift 2
;;
-d|--debug)
set_compile_config "--debug"
shift
;;
-b|--build)
action="build"
shift
;;
-r|--run)
action="run"
shift
if [[ $# -ge 1 ]] && [[ ! "$1" =~ ^- ]]; then
if [[ "$1" =~ ^\[ ]]; then
test_pattern="$1"
shift
else
test_name="$1"
shift
if [[ $# -ge 1 ]] && [[ ! "$1" =~ ^- ]] && [[ "$1" =~ ^\[ ]]; then
test_pattern="$1"
shift
fi
fi
fi
;;
-a|--all)
action="all"
shift
;;
-c|--clean)
action="clean"
shift
;;
--ascend-home)
if [[ -z "$2" ]]; then
echo "错误: --ascend-home 需要一个参数"
exit 1
fi
ascend_home_path="$2"
export ASCEND_HOME_PATH="$ascend_home_path"
shift 2
;;
--test-name)
if [[ -z "$2" ]]; then
echo "错误: --test-name 需要一个参数"
exit 1
fi
test_name="$2"
shift 2
;;
--test-pattern)
if [[ -z "$2" ]]; then
echo "错误: --test-pattern 需要一个参数"
exit 1
fi
test_pattern="$2"
shift 2
;;
-p|--performance)
action="performance"
shift
;;
-rp|--run-performance)
action="run_performance"
shift
;;
-doc|--document)
action="document"
shift
;;
*)
echo "错误: 未知选项 '$1'"
show_help
exit 1
;;
esac
done
if [ -z "$action" ]; then
show_help
exit 0
fi
case "$action" in
"clean")
clean_build
;;
"build")
clean_build
compile_testframework
build_catch2
build_main_project
;;
"run")
run_tests "$test_name" "$test_pattern"
;;
"all")
clean_build
compile_testframework
build_catch2
build_main_project
run_tests "" ""
;;
"performance")
build_performance_project
;;
"run_performance")
run_performance_test
;;
"document")
build_documentation
;;
esac
}
main "$@"