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