#!/bin/bash
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"]="dist/debug"
["Release"]="dist/release"
["RelWithDebInfo"]="relwithdebinfo"
["MinSizeRel"]="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="dist"
std="17"
generator="Unix Makefiles"
enable_coverage="OFF"
enable_test="OFF"
skip_run_tests="OFF"
force_colored_output="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 "UBTurbo 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
;;
-c | --clean)
enable_clean='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
;;
--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
;;
--)
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
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$ ]]; then
enable_test='ON'
build_type='Debug'
CURRENT_PATH=$(cd "$(dirname "$0")"; pwd)
echo "${CURRENT_PATH:?}"
cd "${CURRENT_PATH:?}"
sh ./test/run_dt.sh
exit 0
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
cmake -S . -B "${build_dir}" -G "${generator}" "${trans_params[@]}" \
-DCMAKE_BUILD_TYPE=${build_type} \
-DCMAKE_CXX_STANDARD="${std}" \
-DCMAKE_TOOLCHAIN_FILE="${toolchain_file}" \
-DCMAKE_INSTALL_PREFIX=${build_dir}/install \
-DBUILD_TESTS=${enable_test} \
-DENABLE_COVERAGE=${enable_coverage} \
-DASAN_BUILD=${enable_asan} \
-DLSAN_BUILD=${enable_lsan} \
-DTSAN_BUILD=${enable_tsan} \
-DUBSAN_BUILD=${enable_ubsan} \
-DFORCE_COLORED_OUTPUT=${force_colored_output}
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
if find ${build_dir} -name '*.gcda' | 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 "$@"
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'