#! /usr/bin/env bash # # Copyright (c) 2020-now by the Zeek Project. See LICENSE for details. # # Runs selected stages of the CI pipeline. Each stage assumes the # preceesing one has been successfully executed already. # # We use bash here since that's useful (for pipefail in particular) and # users are unlikely to run this script. set -o pipefail name=$(basename $0) root=$(cd $(dirname $0)/.. && pwd) build= install= color_red=$'\e[1;31m' color_green=$'\e[1;32m' color_yellow=$'\e[1;33m' color_blue=$'\e[1;34m' color_magenta=$'\e[1;35m' color_cyan=$'\e[1;36m' color_normal=$'\e[0m' function usage { cat <] [] Stages configure [release|debug] [] Configure the build for compiling a release/debug version build Compile the code install Install the built version test-code Run code & formatting checks test-build Run the test suite on the built version test-install Run the test suite on the installed version cleanup Delete all build artifacts. Global options: -r Base directory of repository checkout (default: ${root}) -b Build directory; will be deleted at completion (default: \${root}/build-ci) Configure options: --build-toolchain={yes,no} Build the Spicy compiler toolchain [default: yes] --clang-tidy Path to clang-tidy to use (default: found in PATH) --cxx-compiler Path to C++ compiler to use (default: found by cmake) --disable-precompiled-headers Disable use of precompiled headers for developer tests EOF exit 1 } function log_colored { color=$1 shift printf "%s" "${color}" printf "%s" "$@" printf "%s\n" "${color_normal}" } function log_stage { echo log_colored "${color_magenta}" "### $@" } function log_warning { log_colored "${color_yellow}" "### $@" } function log_error { log_colored "${color_red}" "### $@" } function log_diag { log_colored "${color_yellow}" "--- $@" } function error { echo "### Error: $@" exit 1 } function run_configure { build_type="$1" shift mkdir -p ${install} configure="./configure --builddir=${build} --prefix=${install} --generator=Ninja --enable-werror --enable-ccache --with-hilti-compiler-launcher=ccache" clang_tidy=$(which clang-tidy 2>/dev/null) if [ "${build_type}" == "release" ]; then : elif [ "${build_type}" == "debug" ]; then configure="${configure} --enable-debug --enable-sanitizer" else usage fi while [ $# -ne 0 ]; do case "$1" in --cxx-compiler) test $# -gt 0 || usage configure="${configure} --with-cxx-compiler=$2" shift 2; ;; --clang-tidy) test $# -gt 0 || usage clang_tidy="$2" test -x ${clang_tidy} || error "clang-tidy not found in $2" shift 2; ;; --build-toolchain) configure="${configure} --build-toolchain=$2" shift 2; ;; --disable-precompiled-headers) configure="${configure} --disable-precompiled-headers" shift 1; ;; --enable-werror) configure="${configure} --enable-werror" shift 1; ;; --build-static-libs) configure="${configure} --build-static-libs" shift 1; ;; *) usage;; esac done if [ -e ${build} ]; then error "Build directory ${build} already exists, delete first" fi test -z "${clang_tidy}" && log_stage "Warning: No clang-tidy found, will skip any related tests" # Looks like Cirrus CI doesn't fetch tags. git fetch --tags log_stage "Running configure ... (${configure})" ${configure} || exit 1 mkdir -p ${artifacts} echo "${clang_tidy}" >${build}/.clang_tidy if [ -x "${clang_tidy}" ]; then pushd "${build}" >/dev/null || exit 1 cmake -DCMAKE_CXX_CLANG_TIDY="${clang_tidy}" -DCMAKE_C_CLANG_TIDY="${clang_tidy}" .. popd >/dev/null || exit 1 fi } function run_build { log_stage "Building code ..." # The level of parallelism chosen here is tuned for what's configured # in `.cirrus.yml` so that (1) we align with number of CPUs, and (2) we # do not trigger OOM kills in the Docker environments. (cd ${build} && ninja -j4 all) || exit 1 log_stage "Building docs ..." (cd ${root}/doc && make BUILDDIR=${build} DESTDIR=${artifacts}/doc doxygen html) || exit 1 } function run_install { log_stage "Installing code ..." (cd ${build} && ninja install) || exit 1 } function execute_btest { name="$1" cwd="$2" alternative="$3" if [ -n "${alternative}" ]; then alternative="-a ${alternative}" else alternative="" fi if [ -n "${SPICY_BTEST_GROUPS}" ]; then group="-g ${SPICY_BTEST_GROUPS}" else group="" fi log_stage "Running ${name} tests ... (${btest_alternative} ${btest_group})" pushd ${cwd} >/dev/null eval ${preload} \ btest -j 5 \ -f ${artifacts}/diag.log \ -x ${artifacts}/diag.xml \ -z 3 \ ${alternative} \ ${group} rc=$? if [ ${rc} != 0 ]; then cp -a .tmp ${artifacts}/btest-tmp log_diag "Begin diagnostics" cat ${artifacts}/diag.log log_diag "End diagnostics (complete test output in 'btest-tmp')" log_error "Tests have failed" fi popd >/dev/null return ${rc} } function run_test_btest { alternative="$1" execute_btest "Spicy" "tests" "${alternative}" } function run_test_code { rc=0 pre-commit run -a --show-diff-on-failure || rc=1 return ${rc} } function run_test_common { btest_alternative="$1" rc=0 run_test_btest "${btest_alternative}" || rc=1 log_stage "Checking docs ..." # We currently ignore this check since lint checks can fail spuriously if # the remote is temporarily unavailable. (cd "${root}"/doc && make BUILDDIR="${build}" DESTDIR="${artifacts}"/doc check) || true return ${rc} } function run_test_build { export SPICY_BUILD_DIRECTORY=${build} run_test_common return ${rc} } function run_test_install { export SPICY_INSTALLATION_DIRECTORY=${install} run_test_common installation } function run_cleanup { log_stage "Cleaning up ..." (cd ${build} && ninja clean) || exit 1 (cd doc && make clean) || exit 1 } ### Main while getopts "r:b:" opt; do case "${opt}" in h) usage ;; r) root=${OPTARG} ;; b) build=${OPTARG} ;; *) echo "unknown option -${opt}" exit 1 ;; esac done shift $((OPTIND-1)) if [ -z "${build}" ]; then build=${root}/build-ci fi install=/tmp/ci-install-$(basename ${build}) root=$(realpath ${root}) build=$(realpath ${build}) artifacts=${build}/ci cmd=$1 shift test -n "${cmd}" || usage cd ${root} test -e CMakeLists.txt || error "${root} is not the project's root git repository" case "${cmd}" in configure) (run_configure $@);; build) (run_build $@);; install) (run_install $@);; test-build) (run_test_build $@);; test-install) (run_test_install $@);; test-code) (run_test_code $@);; cleanup) (run_cleanup $@);; *) usage;; esac