| #! /usr/bin/env bash |
| # Copyright (C) 2016 Sebastian Pipping <sebastian@pipping.org> |
| # 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" |
| ;; |
| 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_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 trusty, as of now |
| for i in tests xmlwf ; do |
| RUN ln -s \ |
| /usr/i686-w64-mingw32/lib/libwinpthread-1.dll \ |
| /usr/lib/gcc/i686-w64-mingw32/*/libgcc_s_sjlj-1.dll \ |
| /usr/lib/gcc/i686-w64-mingw32/*/libstdc++-6.dll \ |
| "$PWD"/libexpat.dll \ |
| ${i}/ |
| done |
| fi |
| |
| local make_env_args=( |
| CTEST_OUTPUT_ON_FAILURE=1 |
| CTEST_PARALLEL_LEVEL=2 |
| VERBOSE=1 |
| ) |
| |
| RUN "${MAKE}" "${make_env_args[@]}" test run-xmltest |
| } |
| |
| |
| 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 <<EOF |
| Configuration: |
| QA_COMPILER=${QA_COMPILER} # auto-detected from \$CC and \$CXX |
| QA_PROCESSOR=${QA_PROCESSOR} # GCC only |
| QA_SANITIZER=${QA_SANITIZER} # Clang only |
| |
| CFLAGS=${CFLAGS} |
| CXXFLAGS=${CXXFLAGS} |
| LDFLAGS=${LDFLAGS} |
| |
| CC=${CC} |
| CXX=${CXX} |
| LD=${LD} |
| MAKE=${MAKE} |
| |
| Compiler (\$CC): |
| EOF |
| "${CC}" --version | sed 's,^, ,' |
| echo |
| } |
| |
| |
| classify_compiler() { |
| local i |
| for i in "${CC:-}" "${CXX:-}"; do |
| [[ "$i" =~ clang ]] && { echo clang ; return ; } |
| done |
| echo gcc |
| } |
| |
| |
| process_config() { |
| case "${QA_COMPILER:=$(classify_compiler)}" in |
| clang|gcc) ;; |
| *) usage; exit 1 ;; |
| esac |
| |
| |
| if [[ ${QA_COMPILER} != gcc && -n ${QA_PROCESSOR:-} ]]; then |
| WARNING "QA_COMPILER=${QA_COMPILER} is not 'gcc' -- ignoring QA_PROCESSOR=${QA_PROCESSOR}" |
| fi |
| |
| case "${QA_PROCESSOR:=gcov}" in |
| egypt|gcov) ;; |
| *) usage; exit 1 ;; |
| esac |
| |
| |
| if [[ ${QA_COMPILER} != clang && -n ${QA_SANITIZER:-} ]]; then |
| WARNING "QA_COMPILER=${QA_COMPILER} is not 'clang' -- ignoring QA_SANITIZER=${QA_SANITIZER}" >&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 "$@" |