#! /usr/bin/env bash # Copyright (C) 2016 Sebastian Pipping # Licensed under MIT license set -e set -o nounset ANNOUNCE() { local open='\e[1m' local close='\e[0m' echo -e -n "${open}" >&2 echo -n "# $*" >&2 echo -e "${close}" >&2 } WARNING() { local open='\e[1;33m' local close='\e[0m' echo -e -n "${open}" >&2 echo -n "WARNING: $*" >&2 echo -e "${close}" >&2 } RUN() { ANNOUNCE "$@" env "$@" } populate_environment() { : ${MAKE:=make} case "${QA_COMPILER}" in clang) : ${CC:=clang} : ${CXX:=clang++} : ${LD:=clang++} ;; gcc) : ${CC:=gcc} : ${CXX:=g++} : ${LD:=ld} ;; esac : ${BASE_COMPILE_FLAGS:="-pipe -Wall -Wextra -pedantic -Wno-overlength-strings -Wno-long-long"} : ${BASE_LINK_FLAGS:=} if [[ ${QA_COMPILER} = clang ]]; then case "${QA_SANITIZER}" in address) # http://clang.llvm.org/docs/AddressSanitizer.html BASE_COMPILE_FLAGS+=" -g -fsanitize=address -fno-omit-frame-pointer -fno-common" BASE_LINK_FLAGS+=" -g -fsanitize=address" # macOS's XCode does not support LeakSanitizer and reports error: # AddressSanitizer: detect_leaks is not supported on this platform. if [[ "$(uname -s)" != Darwin* ]]; then export ASAN_OPTIONS=detect_leaks=1 fi ;; memory) # http://clang.llvm.org/docs/MemorySanitizer.html BASE_COMPILE_FLAGS+=" -fsanitize=memory -fno-omit-frame-pointer -g -O2 -fsanitize-memory-track-origins -fsanitize-blacklist=$PWD/memory-sanitizer-blacklist.txt" ;; undefined) # http://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html BASE_COMPILE_FLAGS+=" -fsanitize=undefined" BASE_LINK_FLAGS+=" -fsanitize=undefined" export UBSAN_OPTIONS="print_stacktrace=1:halt_on_error=1:abort_on_error=1" ;; esac fi if [[ ${QA_COMPILER} = gcc ]]; then case "${QA_PROCESSOR}" in egypt) BASE_COMPILE_FLAGS+=" -fdump-rtl-expand" ;; gcov) BASE_COMPILE_FLAGS+=" --coverage -O0" ;; esac fi CFLAGS="-std=c99 ${BASE_COMPILE_FLAGS} ${CFLAGS:-}" CXXFLAGS="-std=c++98 ${BASE_COMPILE_FLAGS} ${CXXFLAGS:-}" LDFLAGS="${BASE_LINK_FLAGS} ${LDFLAGS:-}" } run_cmake() { local cmake_args=( -DCMAKE_C_COMPILER="${CC}" -DCMAKE_C_FLAGS="${CFLAGS}" -DCMAKE_CXX_COMPILER="${CXX}" -DCMAKE_CXX_FLAGS="${CXXFLAGS}" -DCMAKE_LINKER="${LD}" -DCMAKE_EXE_LINKER_FLAGS="${LDFLAGS}" -DCMAKE_MODULE_LINKER_FLAGS="${LDFLAGS}" -DCMAKE_SHARED_LINKER_FLAGS="${LDFLAGS}" -DEXPAT_WARNINGS_AS_ERRORS=ON ) RUN cmake "${cmake_args[@]}" "$@" . } run_compile() { local make_args=( VERBOSE=1 -j2 ) RUN "${MAKE}" "${make_args[@]}" clean all } run_tests() { case "${QA_PROCESSOR}" in egypt) return 0 ;; esac if [[ ${CC} =~ mingw ]]; then # NOTE: Filenames are hardcoded for Travis' Ubuntu Bionic, as of now for i in tests xmlwf ; do mingw32_dir="$(ls -1d /usr/lib/gcc/i686-w64-mingw32/* | head -n1)" RUN ln -s \ /usr/i686-w64-mingw32/lib/libwinpthread-1.dll \ "${mingw32_dir}"/libgcc_s_sjlj-1.dll \ "${mingw32_dir}"/libstdc++-6.dll \ "$PWD"/libexpat{,w}.dll \ ${i}/ done fi local make_args=( CTEST_OUTPUT_ON_FAILURE=1 CTEST_PARALLEL_LEVEL=2 VERBOSE=1 test ) [[ $* =~ -DEXPAT_DTD=OFF ]] || make_args+=( run-xmltest ) RUN "${MAKE}" "${make_args[@]}" } run_processor() { if [[ ${QA_COMPILER} != gcc ]]; then return 0 fi case "${QA_PROCESSOR}" in egypt) local DOT_FORMAT="${DOT_FORMAT:-svg}" local o="callgraph.${DOT_FORMAT}" ANNOUNCE "egypt ...... | dot ...... > ${o}" find -name '*.expand' \ | sort \ | xargs -r egypt \ | unflatten -c 20 \ | dot -T${DOT_FORMAT} -Grankdir=LR \ > "${o}" ;; gcov) for gcov_dir in lib xmlwf ; do ( cd "${gcov_dir}" || exit 1 for gcda_file in $(find . -name '*.gcda' | sort) ; do RUN gcov -s .libs/ ${gcda_file} done ) done RUN find -name '*.gcov' | sort ;; esac } run() { populate_environment dump_config run_cmake "$@" run_compile run_tests "$@" run_processor } dump_config() { cat <&2 fi case "${QA_SANITIZER:=address}" in address|memory|undefined) ;; *) usage; exit 1 ;; esac } usage() { cat <<"EOF" Usage: $ ./qa.sh [ARG ..] Environment variables QA_COMPILER=(clang|gcc) # default: auto-detected QA_PROCESSOR=(egypt|gcov) # default: gcov QA_SANITIZER=(address|memory|undefined) # default: address EOF } main() { if [[ ${1:-} = --help ]]; then usage; exit 0 fi process_config run "$@" } main "$@"