319 lines
7.8 KiB
Bash
Executable File
319 lines
7.8 KiB
Bash
Executable File
#! /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 <<EOF
|
|
Usage: ${mame} [<global options>] <stage> [<command options>]
|
|
|
|
Stages
|
|
|
|
configure [release|debug] [<configure options>] 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 <dir> Base directory of repository checkout (default: ${root})
|
|
-b <dir> 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> Path to clang-tidy to use (default: found in PATH)
|
|
--cxx-compiler <path> 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
|