• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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