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