#!/bin/bash
set -euo pipefail
set -o errtrace
set -o errexit
SCRIPT_DIR="$(readlink -f "$0")"
trap_error() {
echo "================================================================="
echo "Error occurred in script at line: $SCRIPT_DIR:${BASH_LINENO[0]}"
echo "Error occurred in command: $BASH_COMMAND"
echo "Call stack:"
for i in "${!FUNCNAME[@]}"; do
if [[ "${BASH_LINENO[i]}" != "0" ]]; then
echo " ${FUNCNAME[i + 1]} at line ${BASH_LINENO[i]}"
fi
done
echo "================================================================="
exit 1
}
trap 'trap_error ${LINENO}' ERR
trap 'cd "${PROJECT_ROOT_DIR}"; echo "Script exited with status $?"; exit' EXIT
declare -A BUILD_DIRS=(
["Debug"]="cmake-build-debug"
["Release"]="cmake-build-release"
["RelWithDebInfo"]="cmake-build-relwithdebinfo"
["MinSizeRel"]="cmake-build-minsizerel"
)
PROJECT_ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"
output_dir="${PROJECT_ROOT_DIR}/output"
build_target="all"
build_type="Release"
build_dir="cmake-build-release"
std="17"
generator="Unix Makefiles"
enable_coverage="OFF"
enable_test="OFF"
enable_it_tests="OFF"
skip_run_tests="OFF"
force_colored_output="OFF"
deploy_version="2.0.0.B098"
enable_ub="ON"
enable_pre_commit="OFF"
enable_http_server="OFF"
enable_source_compiling="OFF"
enable_asan="OFF"
enable_lsan="OFF"
enable_tsan="OFF"
enable_ubsan="OFF"
toolchain_file=""
is_build_project="false"
enable_clean="OFF"
build_in_ci=false
if [[ "$is_build_project" == 'true' ]]; then
echo "Current build in ci env."
build_in_ci=true
fi
jobs=$(nproc)
if [[ "$build_in_ci" != true ]]; then
jobs=$((jobs > 2 ? jobs - 2 : jobs))
if command -v ninja >/dev/null 2>&1; then
generator="Ninja"
echo "Ninja is installed. Generator set to: $generator"
else
echo "Ninja is not installed."
echo "You can install Ninja using the following command:"
echo "For openEuler: sudo dnf install ninja-build"
echo "For Windows: You can download it from https://github.com/ninja-build/ninja/releases"
fi
fi
FAILURE='[\033[1;31mFAILED\033[0;39m]'
function log_info() {
LOG_FILE="${build_dir}/build.log"
if [ $# -lt 1 ]; then
return
fi
echo "$(date +"%F %T") [INFO] $*" >>"$LOG_FILE"
echo "$(date +"%F %T") [INFO] $*"
}
function echo_failure() {
echo -e "RackManager project : $FAILURE"
}
function help() {
sed -rn 's/^### ?//;T;p;' "$0"
}
function parse_args() {
trans_params=()
local trans_flag=false
while [[ $# -gt 0 ]]; do
case "$1" in
-h | --help)
help
exit
;;
-D | --debug)
build_type='Debug'
shift
;;
-T | --type)
if [[ $# -gt 1 && "$2" != "-"* ]]; then
build_type="$2"
shift 2
else
log_info "Error: Argument required after -T | --type."
exit 1
fi
;;
-t | --target)
if [[ $# -gt 1 && "$2" != "-"* ]]; then
build_target="$2"
shift 2
else
log_info "Error: Argument required after -t|--target."
exit 1
fi
;;
-C | --coverage)
enable_coverage='ON'
shift
;;
-H | --http-server)
enable_http_server='ON'
shift
;;
-c | --clean)
enable_clean='ON'
shift
;;
-S | --source-compiling)
enable_source_compiling='ON'
shift
;;
-j | --jobs)
if [[ $# -gt 1 && "$2" != "-"* ]]; then
jobs="$2"
shift 2
else
log_info "Error: Argument required after -j|--jobs."
exit 1
fi
;;
--std)
if [[ $# -gt 1 && "$2" != "-"* ]]; then
std="$2"
shift 2
else
log_info "Error: Argument required after --std, like --std 14."
exit 1
fi
;;
-v | --verbose)
export VERBOSE=1
shift
;;
-V | --deploy-version)
if [[ $# -gt 1 && "$2" != "-"* ]]; then
deploy_version="$2"
shift 2
else
log_info "Error: Argument required after --deploy-version, like --deploy-version 2.0.0.B099"
exit 1
fi
;;
--skip-run-tests)
skip_run_tests='ON'
shift
;;
--asan)
enable_asan='ON'
shift
;;
--lsan)
enable_lsan='ON'
shift
;;
--tsan)
enable_tsan='ON'
shift
;;
--ubsan)
enable_ubsan='ON'
shift
;;
--ninja)
generator='Ninja'
shift
;;
--make)
generator='Unix Makefiles'
shift
;;
--hccs)
enable_ub='OFF'
shift
;;
--ub)
enable_ub='ON'
shift
;;
--pre-commit)
enable_pre_commit='ON'
shift
;;
--)
trans_flag=true
shift
;;
*)
if $trans_flag; then
trans_params+=("$1")
else
[ "$1" != "" ] && build_target="$1"
fi
shift
;;
esac
done
}
function clean() {
local target_dirs=()
case "$1" in
"3rdparty")
target_dirs+=("${PROJECT_ROOT_DIR}/deps")
;;
"package")
target_dirs+=("${build_dir}")
target_dirs+=("${output_dir}")
;;
"package_all_in_one")
target_dirs+=("${build_dir}")
target_dirs+=("${output_dir}")
;;
*)
target_dirs+=("${build_dir}")
;;
esac
for target_dir in "${target_dirs[@]}"; do
if [ ! -d "$target_dir" ]; then
echo "Warning: Directory '$target_dir' does not exist."
else
[ -d "$target_dir" ] && sudo rm -rf "$target_dir"
echo "Directory '$target_dir' has been cleaned."
fi
done
if [[ ! -d "${build_dir}" ]]; then
mkdir -p "${build_dir}"
echo "Directory ${build_dir} has been recreated."
fi
}
function build_cmake() {
if [[ "$build_target" == 'test' || "$build_target" == 'ut' || "$build_target" =~ _ut$ || "$build_target" == 'it' || "$build_target" =~ _it$ || "$build_target" == 'ubse_it_daemon' || "$build_target" == 'pt' ]]; then
enable_test='ON'
build_type='Debug'
fi
if [[ "$build_target" == 'it' || "$build_target" =~ _it$ || "$build_target" == 'ubse_it_daemon' ]]; then
enable_it_tests='ON'
fi
build_dir="$PROJECT_ROOT_DIR/${BUILD_DIRS[$build_type]}"
if [ -z "$build_dir" ]; then
echo "Error: Invalid build type '$build_type'"
exit 1
fi
if [[ "$build_target" == 'package' || "$enable_clean" == 'ON' ]]; then
clean "$build_target"
fi
[ ! -d "${build_dir}" ] && mkdir -p "${build_dir}"
log_info "***** start build_cmake *****"
log_info "building target ${build_target}."
if [[ "$generator" == 'Ninja' ]]; then
force_colored_output='ON'
fi
if rpm -q kernel-devel >/dev/null 2>&1; then
KERNEL_VERSION=$(rpm -q kernel-devel | head -1 | awk -F 'kernel-devel-' '{print $2}')
else
KERNEL_VERSION=$(uname -r)
fi
export B_VERSION="${deploy_version}"
export TRANS_PARAMS="${trans_params[@]}"
local cmake_args=(
--no-warn-unused-cli -S . -B "${build_dir}" -G "${generator}"
-DCMAKE_BUILD_TYPE="${build_type}"
-DCMAKE_CXX_STANDARD="${std}"
-DBUILD_TESTS="${enable_test}"
-DENABLE_IT_TESTS=${enable_it_tests} \
-DENABLE_COVERAGE="${enable_coverage}"
-DSOURCE_COMPILING="${enable_source_compiling}"
-DSKIP_RUN_TESTS="${skip_run_tests}"
-DASAN_BUILD="${enable_asan}"
-DENABLE_HTTP_SERVER="${enable_http_server}"
-DFORCE_COLORED_OUTPUT="${force_colored_output}"
-DBUILD_IN_CI="${build_in_ci}"
-DB_VERSION="${deploy_version}"
-DENABLE_UB="${enable_ub}"
-DCMAKE_EXPORT_COMPILE_COMMANDS="${enable_pre_commit}"
-DKERNEL_VERSION="${KERNEL_VERSION}"
)
[[ -n "${toolchain_file}" ]] && cmake_args+=(-DCMAKE_TOOLCHAIN_FILE="${toolchain_file}")
cmake "${cmake_args[@]}"
if [[ "$enable_pre_commit" == 'ON' ]]; then
log_info "Pre-commit mode: only generating compile_commands.json, skipping build."
return 0
fi
if [[ "$enable_coverage" == 'ON' && "$build_type" == 'Debug' && "$build_target" != 'all' ]]; then
cmake --build "${build_dir}" --target all -j "${jobs}"
fi
cmake --build "${build_dir}" --target "${build_target}" -j "${jobs}"
if [[ "$enable_coverage" == 'ON' ]]; then
export COVERAGE_MODULE="$build_target"
log_info "Coverage module: ${COVERAGE_MODULE}"
if find "${build_dir}" -type f -name '*.gcda' -print -quit 2>/dev/null | grep -q .; then
cmake --build "${build_dir}" --target coverage
else
echo "No .gcda files found. Skipping coverage report generation."
fi
fi
local ret=$?
if [ "$ret" -ne 0 ]; then
log_info "build_cmake failed"
echo_failure
exit 1
fi
}
echo $(date +"[%Y-%m-%d %H:%M]"): "$0" "$@"
START_TIME=$(date +%s.%N)
cd "${PROJECT_ROOT_DIR}"
parse_args "$@"
if [[ "$build_target" == 'package' ]]; then
if [[ "$build_type" == 'Debug' ]]; then
bash "$PROJECT_ROOT_DIR"/scripts/rpm/package.sh -D
else
bash "$PROJECT_ROOT_DIR"/scripts/rpm/package.sh
fi
exit $?
fi
build_cmake
END_TIME=$(date +%s.%N)
EXEC_TIME=$(echo "scale=3; ($END_TIME - $START_TIME) / 1" | bc)
echo -e $(date +"[%Y-%m-%d %H:%M]"): "\033[32m" Build complated in "$EXEC_TIME"s '\033[0m'