1#! /usr/bin/env bash 2# Copyright (C) 2016 Sebastian Pipping <sebastian@pipping.org> 3# Licensed under MIT license 4 5set -e 6set -o nounset 7 8 9ANNOUNCE() { 10 local open='\e[1m' 11 local close='\e[0m' 12 13 echo -e -n "${open}" >&2 14 echo -n "# $*" >&2 15 echo -e "${close}" >&2 16} 17 18 19WARNING() { 20 local open='\e[1;33m' 21 local close='\e[0m' 22 23 echo -e -n "${open}" >&2 24 echo -n "WARNING: $*" >&2 25 echo -e "${close}" >&2 26} 27 28 29RUN() { 30 ANNOUNCE "$@" 31 env "$@" 32} 33 34 35populate_environment() { 36 : ${MAKE:=make} 37 38 case "${QA_COMPILER}" in 39 clang) 40 : ${CC:=clang} 41 : ${CXX:=clang++} 42 : ${LD:=clang++} 43 ;; 44 gcc) 45 : ${CC:=gcc} 46 : ${CXX:=g++} 47 : ${LD:=ld} 48 ;; 49 esac 50 51 : ${BASE_COMPILE_FLAGS:="-pipe -Wall -Wextra -pedantic -Wno-overlength-strings -Wno-long-long"} 52 : ${BASE_LINK_FLAGS:=} 53 54 if [[ ${QA_COMPILER} = clang ]]; then 55 case "${QA_SANITIZER}" in 56 address) 57 # http://clang.llvm.org/docs/AddressSanitizer.html 58 BASE_COMPILE_FLAGS+=" -g -fsanitize=address -fno-omit-frame-pointer -fno-common" 59 BASE_LINK_FLAGS+=" -g -fsanitize=address" 60 ;; 61 memory) 62 # http://clang.llvm.org/docs/MemorySanitizer.html 63 BASE_COMPILE_FLAGS+=" -fsanitize=memory -fno-omit-frame-pointer -g -O2 -fsanitize-memory-track-origins -fsanitize-blacklist=$PWD/memory-sanitizer-blacklist.txt" 64 ;; 65 undefined) 66 # http://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html 67 BASE_COMPILE_FLAGS+=" -fsanitize=undefined" 68 BASE_LINK_FLAGS+=" -fsanitize=undefined" 69 export UBSAN_OPTIONS="print_stacktrace=1:halt_on_error=1:abort_on_error=1" 70 ;; 71 esac 72 fi 73 74 75 if [[ ${QA_COMPILER} = gcc ]]; then 76 case "${QA_PROCESSOR}" in 77 egypt) BASE_COMPILE_FLAGS+=" -fdump-rtl-expand" ;; 78 gcov) BASE_COMPILE_FLAGS+=" --coverage -O0" ;; 79 esac 80 fi 81 82 83 CFLAGS="-std=c99 ${BASE_COMPILE_FLAGS} ${CFLAGS:-}" 84 CXXFLAGS="-std=c++98 ${BASE_COMPILE_FLAGS} ${CXXFLAGS:-}" 85 LDFLAGS="${BASE_LINK_FLAGS} ${LDFLAGS:-}" 86} 87 88 89run_cmake() { 90 local cmake_args=( 91 -DCMAKE_C_COMPILER="${CC}" 92 -DCMAKE_C_FLAGS="${CFLAGS}" 93 94 -DCMAKE_CXX_COMPILER="${CXX}" 95 -DCMAKE_CXX_FLAGS="${CXXFLAGS}" 96 97 -DCMAKE_LINKER="${LD}" 98 -DCMAKE_EXE_LINKER_FLAGS="${LDFLAGS}" 99 -DCMAKE_SHARED_LINKER_FLAGS="${LDFLAGS}" 100 101 -DEXPAT_WARNINGS_AS_ERRORS=ON 102 ) 103 RUN cmake "${cmake_args[@]}" "$@" . 104} 105 106 107run_compile() { 108 local make_args=( 109 VERBOSE=1 110 -j2 111 ) 112 113 RUN "${MAKE}" "${make_args[@]}" clean all 114} 115 116 117run_tests() { 118 case "${QA_PROCESSOR}" in 119 egypt) return 0 ;; 120 esac 121 122 if [[ ${CC} =~ mingw ]]; then 123 # NOTE: Filenames are hardcoded for Travis Ubuntu trusty, as of now 124 for i in tests xmlwf ; do 125 RUN ln -s \ 126 /usr/i686-w64-mingw32/lib/libwinpthread-1.dll \ 127 /usr/lib/gcc/i686-w64-mingw32/*/libgcc_s_sjlj-1.dll \ 128 /usr/lib/gcc/i686-w64-mingw32/*/libstdc++-6.dll \ 129 "$PWD"/libexpat{,w}.dll \ 130 ${i}/ 131 done 132 fi 133 134 local make_args=( 135 CTEST_OUTPUT_ON_FAILURE=1 136 CTEST_PARALLEL_LEVEL=2 137 VERBOSE=1 138 test 139 ) 140 [[ $* =~ -DEXPAT_DTD=OFF ]] || make_args+=( run-xmltest ) 141 142 RUN "${MAKE}" "${make_args[@]}" 143} 144 145 146run_processor() { 147 if [[ ${QA_COMPILER} != gcc ]]; then 148 return 0 149 fi 150 151 case "${QA_PROCESSOR}" in 152 egypt) 153 local DOT_FORMAT="${DOT_FORMAT:-svg}" 154 local o="callgraph.${DOT_FORMAT}" 155 ANNOUNCE "egypt ...... | dot ...... > ${o}" 156 find -name '*.expand' \ 157 | sort \ 158 | xargs -r egypt \ 159 | unflatten -c 20 \ 160 | dot -T${DOT_FORMAT} -Grankdir=LR \ 161 > "${o}" 162 ;; 163 gcov) 164 for gcov_dir in lib xmlwf ; do 165 ( 166 cd "${gcov_dir}" || exit 1 167 for gcda_file in $(find . -name '*.gcda' | sort) ; do 168 RUN gcov -s .libs/ ${gcda_file} 169 done 170 ) 171 done 172 173 RUN find -name '*.gcov' | sort 174 ;; 175 esac 176} 177 178 179run() { 180 populate_environment 181 dump_config 182 183 run_cmake "$@" 184 run_compile 185 run_tests "$@" 186 run_processor 187} 188 189 190dump_config() { 191 cat <<EOF 192Configuration: 193 QA_COMPILER=${QA_COMPILER} # auto-detected from \$CC and \$CXX 194 QA_PROCESSOR=${QA_PROCESSOR} # GCC only 195 QA_SANITIZER=${QA_SANITIZER} # Clang only 196 197 CFLAGS=${CFLAGS} 198 CXXFLAGS=${CXXFLAGS} 199 LDFLAGS=${LDFLAGS} 200 201 CC=${CC} 202 CXX=${CXX} 203 LD=${LD} 204 MAKE=${MAKE} 205 206Compiler (\$CC): 207EOF 208 "${CC}" --version | sed 's,^, ,' 209 echo 210} 211 212 213classify_compiler() { 214 local i 215 for i in "${CC:-}" "${CXX:-}"; do 216 [[ "$i" =~ clang ]] && { echo clang ; return ; } 217 done 218 echo gcc 219} 220 221 222process_config() { 223 case "${QA_COMPILER:=$(classify_compiler)}" in 224 clang|gcc) ;; 225 *) usage; exit 1 ;; 226 esac 227 228 229 if [[ ${QA_COMPILER} != gcc && -n ${QA_PROCESSOR:-} ]]; then 230 WARNING "QA_COMPILER=${QA_COMPILER} is not 'gcc' -- ignoring QA_PROCESSOR=${QA_PROCESSOR}" 231 fi 232 233 case "${QA_PROCESSOR:=gcov}" in 234 egypt|gcov) ;; 235 *) usage; exit 1 ;; 236 esac 237 238 239 if [[ ${QA_COMPILER} != clang && -n ${QA_SANITIZER:-} ]]; then 240 WARNING "QA_COMPILER=${QA_COMPILER} is not 'clang' -- ignoring QA_SANITIZER=${QA_SANITIZER}" >&2 241 fi 242 243 case "${QA_SANITIZER:=address}" in 244 address|memory|undefined) ;; 245 *) usage; exit 1 ;; 246 esac 247} 248 249 250usage() { 251 cat <<"EOF" 252Usage: 253 $ ./qa.sh [ARG ..] 254 255Environment variables 256 QA_COMPILER=(clang|gcc) # default: auto-detected 257 QA_PROCESSOR=(egypt|gcov) # default: gcov 258 QA_SANITIZER=(address|memory|undefined) # default: address 259 260EOF 261} 262 263 264main() { 265 if [[ ${1:-} = --help ]]; then 266 usage; exit 0 267 fi 268 269 process_config 270 271 run "$@" 272} 273 274 275main "$@" 276