1#! /usr/bin/env bash 2# __ __ _ 3# ___\ \/ /_ __ __ _| |_ 4# / _ \\ /| '_ \ / _` | __| 5# | __// \| |_) | (_| | |_ 6# \___/_/\_\ .__/ \__,_|\__| 7# |_| XML parser 8# 9# Copyright (c) 2016-2022 Sebastian Pipping <sebastian@pipping.org> 10# Copyright (c) 2019 Philippe Antoine <contact@catenacyber.fr> 11# Copyright (c) 2019 Hanno Böck <hanno@gentoo.org> 12# Licensed under the MIT license: 13# 14# Permission is hereby granted, free of charge, to any person obtaining 15# a copy of this software and associated documentation files (the 16# "Software"), to deal in the Software without restriction, including 17# without limitation the rights to use, copy, modify, merge, publish, 18# distribute, sublicense, and/or sell copies of the Software, and to permit 19# persons to whom the Software is furnished to do so, subject to the 20# following conditions: 21# 22# The above copyright notice and this permission notice shall be included 23# in all copies or substantial portions of the Software. 24# 25# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 26# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 27# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 28# NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 29# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 30# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 31# USE OR OTHER DEALINGS IN THE SOFTWARE. 32 33set -e 34set -o nounset 35 36 37ANNOUNCE() { 38 local open='\e[1m' 39 local close='\e[0m' 40 41 echo -e -n "${open}" >&2 42 echo -n "# $*" >&2 43 echo -e "${close}" >&2 44} 45 46 47WARNING() { 48 local open='\e[1;33m' 49 local close='\e[0m' 50 51 echo -e -n "${open}" >&2 52 echo -n "WARNING: $*" >&2 53 echo -e "${close}" >&2 54} 55 56 57RUN() { 58 ANNOUNCE "$@" 59 env "$@" 60} 61 62 63populate_environment() { 64 : ${MAKE:=make} 65 66 case "${QA_COMPILER}" in 67 clang) 68 : ${CC:=clang} 69 : ${CXX:=clang++} 70 : ${LD:=clang++} 71 ;; 72 gcc) 73 : ${CC:=gcc} 74 : ${CXX:=g++} 75 : ${LD:=ld} 76 ;; 77 esac 78 79 : ${BASE_COMPILE_FLAGS:="-pipe -Wall -Wextra -pedantic -Wno-overlength-strings -Wno-long-long"} 80 : ${BASE_LINK_FLAGS:=} 81 82 if [[ ${QA_COMPILER} = clang ]]; then 83 case "${QA_SANITIZER}" in 84 address) 85 # http://clang.llvm.org/docs/AddressSanitizer.html 86 BASE_COMPILE_FLAGS+=" -g -fsanitize=address -fno-omit-frame-pointer -fno-common" 87 BASE_LINK_FLAGS+=" -g -fsanitize=address" 88 # macOS's XCode does not support LeakSanitizer and reports error: 89 # AddressSanitizer: detect_leaks is not supported on this platform. 90 if [[ "$(uname -s)" != Darwin* ]]; then 91 export ASAN_OPTIONS=detect_leaks=1 92 fi 93 ;; 94 memory) 95 # http://clang.llvm.org/docs/MemorySanitizer.html 96 BASE_COMPILE_FLAGS+=" -fsanitize=memory -fno-omit-frame-pointer -g -O2 -fsanitize-memory-track-origins -fsanitize-blacklist=$PWD/memory-sanitizer-blacklist.txt" 97 ;; 98 undefined) 99 # http://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html 100 BASE_COMPILE_FLAGS+=" -fsanitize=undefined" 101 BASE_LINK_FLAGS+=" -fsanitize=undefined" 102 export UBSAN_OPTIONS="print_stacktrace=1:halt_on_error=1:abort_on_error=1" 103 ;; 104 esac 105 fi 106 107 108 if [[ ${QA_COMPILER} = gcc ]]; then 109 case "${QA_PROCESSOR}" in 110 egypt) BASE_COMPILE_FLAGS+=" -fdump-rtl-expand" ;; 111 gcov) BASE_COMPILE_FLAGS+=" --coverage -O0" ;; 112 esac 113 fi 114 115 116 CFLAGS="-std=c99 ${BASE_COMPILE_FLAGS} ${CFLAGS:-}" 117 CXXFLAGS="-std=c++98 ${BASE_COMPILE_FLAGS} ${CXXFLAGS:-}" 118 LDFLAGS="${BASE_LINK_FLAGS} ${LDFLAGS:-}" 119} 120 121 122run_cmake() { 123 local cmake_args=( 124 -DCMAKE_C_COMPILER="${CC}" 125 -DCMAKE_C_FLAGS="${CFLAGS}" 126 127 -DCMAKE_CXX_COMPILER="${CXX}" 128 -DCMAKE_CXX_FLAGS="${CXXFLAGS}" 129 130 -DCMAKE_LINKER="${LD}" 131 -DCMAKE_EXE_LINKER_FLAGS="${LDFLAGS}" 132 -DCMAKE_MODULE_LINKER_FLAGS="${LDFLAGS}" 133 -DCMAKE_SHARED_LINKER_FLAGS="${LDFLAGS}" 134 135 -DEXPAT_WARNINGS_AS_ERRORS=ON 136 ) 137 RUN cmake "${cmake_args[@]}" "$@" . 138} 139 140 141run_compile() { 142 local make_args=( 143 VERBOSE=1 144 -j2 145 ) 146 147 RUN "${MAKE}" "${make_args[@]}" clean all 148} 149 150 151run_tests() { 152 case "${QA_PROCESSOR}" in 153 egypt) return 0 ;; 154 esac 155 156 if [[ ${CC} =~ mingw ]]; then 157 for i in tests xmlwf ; do 158 mingw32_dir="$(dirname "$(ls -1 /usr/lib*/gcc/i686-w64-mingw32/*/{libgcc_s_sjlj-1.dll,libstdc++-6.dll} | head -n1)")" 159 RUN ln -s \ 160 /usr/i686-w64-mingw32/lib/libwinpthread-1.dll \ 161 "${mingw32_dir}"/libgcc_s_dw2-1.dll \ 162 "${mingw32_dir}"/libgcc_s_sjlj-1.dll \ 163 "${mingw32_dir}"/libstdc++-6.dll \ 164 "$PWD"/libexpat{,w}-*.dll \ 165 ${i}/ 166 done 167 fi 168 169 local make_args=( 170 CTEST_OUTPUT_ON_FAILURE=1 171 CTEST_PARALLEL_LEVEL=2 172 VERBOSE=1 173 test 174 ) 175 [[ $* =~ -DEXPAT_DTD=OFF ]] || make_args+=( run-xmltest ) 176 177 RUN "${MAKE}" "${make_args[@]}" 178} 179 180 181run_processor() { 182 if [[ ${QA_COMPILER} != gcc ]]; then 183 return 0 184 fi 185 186 case "${QA_PROCESSOR}" in 187 egypt) 188 local DOT_FORMAT="${DOT_FORMAT:-svg}" 189 local o="callgraph.${DOT_FORMAT}" 190 ANNOUNCE "egypt ...... | dot ...... > ${o}" 191 find -name '*.expand' \ 192 | sort \ 193 | xargs -r egypt \ 194 | unflatten -c 20 \ 195 | dot -T${DOT_FORMAT} -Grankdir=LR \ 196 > "${o}" 197 ;; 198 gcov) 199 for gcov_dir in lib xmlwf ; do 200 ( 201 cd "${gcov_dir}" || exit 1 202 for gcda_file in $(find . -name '*.gcda' | sort) ; do 203 RUN gcov -s .libs/ ${gcda_file} 204 done 205 ) 206 done 207 208 RUN find -name '*.gcov' | sort 209 ;; 210 esac 211} 212 213 214run() { 215 populate_environment 216 dump_config 217 218 run_cmake "$@" 219 run_compile 220 run_tests "$@" 221 run_processor 222} 223 224 225dump_config() { 226 cat <<EOF 227Configuration: 228 QA_COMPILER=${QA_COMPILER} # auto-detected from \$CC and \$CXX 229 QA_PROCESSOR=${QA_PROCESSOR} # GCC only 230 QA_SANITIZER=${QA_SANITIZER} # Clang only 231 232 CFLAGS=${CFLAGS} 233 CXXFLAGS=${CXXFLAGS} 234 LDFLAGS=${LDFLAGS} 235 236 CC=${CC} 237 CXX=${CXX} 238 LD=${LD} 239 MAKE=${MAKE} 240 241Compiler (\$CC): 242EOF 243 "${CC}" --version | sed 's,^, ,' 244 echo 245} 246 247 248classify_compiler() { 249 local i 250 for i in "${CC:-}" "${CXX:-}"; do 251 [[ "$i" =~ clang ]] && { echo clang ; return ; } 252 done 253 echo gcc 254} 255 256 257process_config() { 258 case "${QA_COMPILER:=$(classify_compiler)}" in 259 clang|gcc) ;; 260 *) usage; exit 1 ;; 261 esac 262 263 264 if [[ ${QA_COMPILER} != gcc && -n ${QA_PROCESSOR:-} ]]; then 265 WARNING "QA_COMPILER=${QA_COMPILER} is not 'gcc' -- ignoring QA_PROCESSOR=${QA_PROCESSOR}" 266 fi 267 268 case "${QA_PROCESSOR:=gcov}" in 269 egypt|gcov) ;; 270 *) usage; exit 1 ;; 271 esac 272 273 274 if [[ ${QA_COMPILER} != clang && -n ${QA_SANITIZER:-} ]]; then 275 WARNING "QA_COMPILER=${QA_COMPILER} is not 'clang' -- ignoring QA_SANITIZER=${QA_SANITIZER}" >&2 276 fi 277 278 case "${QA_SANITIZER:=address}" in 279 address|memory|undefined) ;; 280 *) usage; exit 1 ;; 281 esac 282} 283 284 285usage() { 286 cat <<"EOF" 287Usage: 288 $ ./qa.sh [ARG ..] 289 290Environment variables 291 QA_COMPILER=(clang|gcc) # default: auto-detected 292 QA_PROCESSOR=(egypt|gcov) # default: gcov 293 QA_SANITIZER=(address|memory|undefined) # default: address 294 295EOF 296} 297 298 299main() { 300 if [[ ${1:-} = --help ]]; then 301 usage; exit 0 302 fi 303 304 process_config 305 306 run "$@" 307} 308 309 310main "$@" 311