1#! /usr/bin/env bash 2# __ __ _ 3# ___\ \/ /_ __ __ _| |_ 4# / _ \\ /| '_ \ / _` | __| 5# | __// \| |_) | (_| | |_ 6# \___/_/\_\ .__/ \__,_|\__| 7# |_| XML parser 8# 9# Copyright (c) 2016-2023 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"} 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 cfi) 95 BASE_COMPILE_FLAGS+=' -fsanitize=cfi -flto -fvisibility=hidden -fno-sanitize-trap=all -fsanitize-cfi-cross-dso' 96 BASE_LINK_FLAGS+=' -fuse-ld=gold' 97 ;; 98 memory) 99 # http://clang.llvm.org/docs/MemorySanitizer.html 100 BASE_COMPILE_FLAGS+=" -fsanitize=memory -fno-omit-frame-pointer -g -O2 -fsanitize-memory-track-origins -fsanitize-blacklist=$PWD/memory-sanitizer-blacklist.txt" 101 ;; 102 undefined) 103 # http://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html 104 BASE_COMPILE_FLAGS+=" -fsanitize=undefined" 105 BASE_LINK_FLAGS+=" -fsanitize=undefined" 106 export UBSAN_OPTIONS="print_stacktrace=1:halt_on_error=1:abort_on_error=1" 107 ;; 108 esac 109 fi 110 111 112 if [[ ${QA_COMPILER} = gcc ]]; then 113 case "${QA_PROCESSOR}" in 114 egypt) BASE_COMPILE_FLAGS+=" -fdump-rtl-expand" ;; 115 gcov) BASE_COMPILE_FLAGS+=" --coverage -O0" ;; 116 esac 117 fi 118 119 120 CFLAGS="-std=c99 ${BASE_COMPILE_FLAGS} ${CFLAGS:-}" 121 CXXFLAGS="-std=c++11 ${BASE_COMPILE_FLAGS} ${CXXFLAGS:-}" 122 LDFLAGS="${BASE_LINK_FLAGS} ${LDFLAGS:-}" 123} 124 125 126run_cmake() { 127 local cmake_args=( 128 -DCMAKE_C_COMPILER="${CC}" 129 -DCMAKE_C_FLAGS="${CFLAGS}" 130 131 -DCMAKE_CXX_COMPILER="${CXX}" 132 -DCMAKE_CXX_FLAGS="${CXXFLAGS}" 133 134 -DCMAKE_LINKER="${LD}" 135 -DCMAKE_EXE_LINKER_FLAGS="${LDFLAGS}" 136 -DCMAKE_MODULE_LINKER_FLAGS="${LDFLAGS}" 137 -DCMAKE_SHARED_LINKER_FLAGS="${LDFLAGS}" 138 139 -DEXPAT_WARNINGS_AS_ERRORS=ON 140 ) 141 RUN cmake "${cmake_args[@]}" "$@" . 142} 143 144 145run_compile() { 146 local make_args=( 147 VERBOSE=1 148 -j2 149 ) 150 151 RUN "${MAKE}" "${make_args[@]}" clean all 152} 153 154 155run_tests() { 156 case "${QA_PROCESSOR}" in 157 egypt) return 0 ;; 158 esac 159 160 if [[ ${CC} =~ mingw ]]; then 161 for i in tests xmlwf ; do 162 mingw32_dir="$(dirname "$(ls -1 /usr/lib*/gcc/i686-w64-mingw32/*/{libgcc_s_sjlj-1.dll,libstdc++-6.dll} | head -n1)")" 163 RUN ln -s \ 164 /usr/i686-w64-mingw32/lib/libwinpthread-1.dll \ 165 "${mingw32_dir}"/libgcc_s_dw2-1.dll \ 166 "${mingw32_dir}"/libgcc_s_sjlj-1.dll \ 167 "${mingw32_dir}"/libstdc++-6.dll \ 168 "$PWD"/libexpat{,w}-*.dll \ 169 ${i}/ 170 done 171 fi 172 173 local make_args=( 174 CTEST_OUTPUT_ON_FAILURE=1 175 CTEST_PARALLEL_LEVEL=2 176 VERBOSE=1 177 test 178 ) 179 [[ $* =~ -DEXPAT_DTD=OFF ]] || make_args+=( run-xmltest ) 180 181 RUN "${MAKE}" "${make_args[@]}" 182} 183 184 185run_processor() { 186 if [[ ${QA_COMPILER} != gcc ]]; then 187 return 0 188 fi 189 190 case "${QA_PROCESSOR}" in 191 egypt) 192 local DOT_FORMAT="${DOT_FORMAT:-svg}" 193 local o="callgraph.${DOT_FORMAT}" 194 ANNOUNCE "egypt ...... | dot ...... > ${o}" 195 find -name '*.expand' \ 196 | sort \ 197 | xargs -r egypt \ 198 | unflatten -c 20 \ 199 | dot -T${DOT_FORMAT} -Grankdir=LR \ 200 > "${o}" 201 ;; 202 gcov) 203 for gcov_dir in lib xmlwf ; do 204 ( 205 cd "${gcov_dir}" || exit 1 206 for gcda_file in $(find . -name '*.gcda' | sort) ; do 207 RUN gcov -s .libs/ ${gcda_file} 208 done 209 ) 210 done 211 212 RUN find -name '*.gcov' | sort 213 ;; 214 esac 215} 216 217 218run() { 219 populate_environment 220 dump_config 221 222 run_cmake "$@" 223 run_compile 224 run_tests "$@" 225 run_processor 226} 227 228 229dump_config() { 230 cat <<EOF 231Configuration: 232 QA_COMPILER=${QA_COMPILER} # auto-detected from \$CC and \$CXX 233 QA_PROCESSOR=${QA_PROCESSOR} # GCC only 234 QA_SANITIZER=${QA_SANITIZER} 235 236 CFLAGS=${CFLAGS} 237 CXXFLAGS=${CXXFLAGS} 238 LDFLAGS=${LDFLAGS} 239 240 CC=${CC} 241 CXX=${CXX} 242 LD=${LD} 243 MAKE=${MAKE} 244 245Compiler (\$CC): 246EOF 247 "${CC}" --version | sed 's,^, ,' 248 echo 249} 250 251 252classify_compiler() { 253 local i 254 for i in "${CC:-}" "${CXX:-}"; do 255 [[ "$i" =~ clang ]] && { echo clang ; return ; } 256 done 257 echo gcc 258} 259 260 261process_config() { 262 case "${QA_COMPILER:=$(classify_compiler)}" in 263 clang|gcc) ;; 264 *) usage; exit 1 ;; 265 esac 266 267 268 if [[ ${QA_COMPILER} != gcc && -n ${QA_PROCESSOR:-} ]]; then 269 WARNING "QA_COMPILER=${QA_COMPILER} is not 'gcc' -- ignoring QA_PROCESSOR=${QA_PROCESSOR}" 270 fi 271 272 case "${QA_PROCESSOR:=gcov}" in 273 egypt|gcov) ;; 274 *) usage; exit 1 ;; 275 esac 276 277 278 if [[ ${QA_COMPILER} != clang && ( ${QA_SANITIZER:-} == cfi || ${QA_SANITIZER:-} == memory ) ]]; then 279 WARNING "QA_COMPILER=${QA_COMPILER} is not 'clang' -- ignoring QA_SANITIZER=${QA_SANITIZER}" >&2 280 QA_SANITIZER= 281 fi 282 283 case "${QA_SANITIZER:=address}" in 284 address|cfi|memory|undefined) ;; 285 *) usage; exit 1 ;; 286 esac 287} 288 289 290usage() { 291 cat <<"EOF" 292Usage: 293 $ ./qa.sh [ARG ..] 294 295Environment variables 296 QA_COMPILER=(clang|gcc) # default: auto-detected 297 QA_PROCESSOR=(egypt|gcov) # default: gcov 298 QA_SANITIZER=(address|cfi|memory|undefined) # default: address 299 300EOF 301} 302 303 304main() { 305 if [[ ${1:-} = --help ]]; then 306 usage; exit 0 307 fi 308 309 process_config 310 311 run "$@" 312} 313 314 315main "$@" 316