-
Notifications
You must be signed in to change notification settings - Fork 82
/
do_quality_checks.sh
executable file
·362 lines (330 loc) · 12.3 KB
/
do_quality_checks.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
#!/bin/sh
#
# this script tries to do some quality checks
# automatically. It counts compiler warnings,
# builds in both debug/release mode, test multiple
# compilers etc.
#
# To get the most out of it, install as many variants of gcc and clang
# as you can. If g++ is found, it will look for g++-* in the same directory.
# If clang++ is found, it will look for clang++-* in the same directory.
# This means you need to have either system wide installs, or your PATH is
# setup in such a way that g++/clang++ points to the same location as the compilers
# you want to test.
#
# If clang is available, builds with address and undefined sanitizer will be made.
#
# If clang is available and possible to use with libc++, it will be built. On Ubuntu,
# install libc++abi-dev and libc++-dev
#
# If valgrind is available, it will be added as one thing to test.
#
# If dpkg-buildflags is available, a test build will be added with the flags
# coming from that tool.
#
# A build with debug iterators (https://gcc.gnu.org/onlinedocs/libstdc++/manual/debug_mode.html)
# is made.
#
# All compiles are checked to be warning free, all unit tests should pass.
#
# LICENSE: GPLv2 or later, at your option.
# by Paul Dreik 20181014
set -e
export LANG=
rootdir=$(dirname $0)
me=$(basename $0)
#flags to configure, for assert.
ASSERT=
###############################################################################
start_from_scratch() {
cd $rootdir
if [ -e Makefile ] ; then
make distclean >/dev/null 2>&1
fi
}
###############################################################################
#argument 1 is the compiler
#argument 2 is the c++ standard
#argument 3 (optional) is appended to CXXFLAGS
compile_and_test_standard() {
start_from_scratch
/bin/echo -n "$me: using $(basename $1) with standard $2"
if [ -n "$3" ] ; then
echo " (with additional CXXFLAGS $3)"
else
echo ""
fi
if ! ./bootstrap.sh >bootstrap.log 2>&1; then
echo $me:failed bootstrap - see bootstrap.log
exit 1
fi
if ! ./configure $ASSERT --enable-warnings CXX=$1 CXXFLAGS="-std=$2 $3" >configure.log 2>&1 ; then
echo $me: failed configure - see configure.log
exit 1
fi
#make sure it compiles
if [ ! -x /usr/bin/time ] ; then
echo "$me: please install /usr/bin/time (apt install time)"
exit 1
fi
if ! /usr/bin/time --format=%e --output=time.log make >make.log 2>&1; then
echo $me: failed make
exit 1
fi
if [ ! -z $MEASURE_COMPILE_TIME ] ; then
echo $me: " compile with $(basename $1) $2 took $(cat time.log) seconds"
fi
#check for warnings
if grep -q "warning" make.log; then
# store as an artifact instead of erroring out
name=$(cat *.log |sha256sum |head -c 12)
cp make.log make_${name}.log
echo $me: found compile warning - see make.log, also stored as make_${name}.log
fi
#run the tests
if ! make check >makecheck.log 2>&1 ; then
echo $me: failed make check - see makecheck.log
exit 1
fi
}
###############################################################################
#argument 1 is the compiler
compile_and_test() {
#this is the test program to compile, so we know the compiler and standard lib
#works. clang 4 with c++2a does not.
/bin/echo -e "#include <iostream>">x.cpp
#does the compiler understand c++11? That is mandatory.
if ! $1 -c x.cpp -std=c++11 >/dev/null 2>&1 ; then
echo $me: this compiler $1 does not understand c++11
return 0
fi
#loop over all standard flags>=11 and try those which work.
#use the code words.
for std in 11 1y 1z 2a 2b ; do
if ! $1 -c x.cpp -std=c++$std >/dev/null 2>&1 ; then
echo $me: compiler does not understand c++$std, skipping this combination.
else
# debug build
ASSERT=--enable-assert
compile_and_test_standard $1 c++$std "-Og"
# release build
ASSERT=--disable-assert
#compile_and_test_standard $1 c++$std "-O2"
compile_and_test_standard $1 c++$std "-O3"
#compile_and_test_standard $1 c++$std "-Os"
fi
done
rm x.cpp
}
###############################################################################
run_with_sanitizer() {
echo $me: "running with sanitizer (options $1)"
#find the latest clang compiler
latestclang=$(ls $(which clang++)* |grep -v libc |sort -g |tail -n1)
if [ ! -x $latestclang ] ; then
echo $me: could not find latest clang $latestclang
return 0
fi
start_from_scratch
./bootstrap.sh >bootstrap.log
./configure $ASSERT CXX=$latestclang CXXFLAGS="-std=c++1y $1" >configure.log
make > make.log 2>&1
export UBSAN_OPTIONS="halt_on_error=true exitcode=1"
export ASAN_OPTIONS="halt_on_error=true exitcode=1"
make check >make-check.log 2>&1
unset UBSAN_OPTIONS
unset ASAN_OPTIONS
}
###############################################################################
#This tries to mimic how the debian package is built
run_with_debian_buildflags() {
echo $me: "running with buildflags from debian dpkg-buildflags"
if ! which dpkg-buildflags >/dev/null ; then
echo $me: dpkg-buildflags not found - skipping
return 0
fi
start_from_scratch
./bootstrap.sh >bootstrap.log
eval $(DEB_BUILD_MAINT_OPTIONS="hardening=+all qa=+all,-canary reproducible=+all" dpkg-buildflags --export=sh)
./configure >configure.log
make > make.log 2>&1
#check for warnings
if grep -q "warning" make.log; then
# store as an artifact instead of erroring out
name=$(cat *.log |sha256sum |head -c 12)
cp make.log make_${name}.log
echo $me: found compile warnings - see make.log, also stored as make_${name}.log
fi
make check >make-check.log 2>&1
#restore the build environment
for flag in $(dpkg-buildflags |cut -f1 -d=) ; do
unset $flag
done
}
###############################################################################
run_with_libcpp() {
# find a clang that works. try the latest version first.
echo "#include <iostream>
int main() { std::cout<<\"libc++ works!\";}" >x.cpp
for clang in $(ls $(which clang++)* |grep -v libc|sort -g --reverse) ; do
if [ ! -x $clang ] ; then
continue
fi
if ! $clang -std=c++11 -stdlib=libc++ -lc++abi x.cpp >/dev/null 2>&1 || [ ! -x ./a.out ] || ! ./a.out ; then
echo $me: "debug: $clang could not compile with libc++ - perhaps uninstalled."
continue
fi
compile_and_test_standard $clang c++11 "-stdlib=libc++ -D_LIBCPP_DEBUG=1"
return
done
# we will get here if no working clang could be found. that is not an error,
# having clang and libc++ installed is optional
echo $me: no working clang with libc++ found, skipping.
}
###############################################################################
verify_packaging() {
#make sure the packaging works as intended.
echo $me: "trying to make a tar ball for release and building it..."
log="$(pwd)/packagetest.log"
./bootstrap.sh >$log
./configure >>$log
touch dummy
make dist >>$log
TARGZ=$(find "$(pwd)" -newer dummy -name "rdfind*gz" -type f |head -n1)
rm dummy
temp=$(mktemp -d)
cp "$TARGZ" "$temp"
cd "$temp"
tar xzf $(basename "$TARGZ") >>$log
cd $(basename "$TARGZ" .tar.gz)
./configure --prefix=$temp >>$log
make >>$log
make check >>$log
make install >>$log
$temp/bin/rdfind --version >>$log
#coming here means all went fine, go back to the source dir.
cd $(dirname "$TARGZ")
rm -rf "$temp"
}
###############################################################################
verify_self_contained_headers() {
/bin/echo -n "$me: verify that all header files are self contained..."
if [ ! -e configure ]; then
./bootstrap.sh >bootstrap.log 2>&1
fi
if [ ! -e config.h ]; then
./configure >configure.log 2>&1
fi
for header in *.hh ; do
cp $header tmp.cc
if ! g++ -std=c++11 -I. -c tmp.cc -o /dev/null >header.log 2>&1 ; then
echo "$me: found a header which is not self contained: $header."
echo "$me: see header.log for details"
exit 1
fi
rm tmp.cc
done
echo "OK!"
}
###############################################################################
build_32bit() {
#compiling to 32 bit, on amd64.
#apt install libc6-i386 gcc-multilib g++-multilib
#
if [ $(uname -m) != x86_64 ] ; then
echo $me: "not on x64, won't cross compile with -m32"
return;
fi
echo $me: "trying to compile in 32 bit mode with -m32..."
configureflags="--build=i686-pc-linux-gnu CFLAGS=-m32 CXXFLAGS=-m32 LDFLAGS=-m32"
here=$(pwd)
nettleinstall=$here/nettle32bit
if [ -d "$nettleinstall" ] ; then
echo $me: "local nettle already seems to be installed"
else
mkdir "$nettleinstall"
cd "$nettleinstall"
nettleversion=3.7.3
echo "$me: downloading nettle from gnu.org..."
wget --quiet https://ftp.gnu.org/gnu/nettle/nettle-$nettleversion.tar.gz
echo "661f5eb03f048a3b924c3a8ad2515d4068e40f67e774e8a26827658007e3bcf0 nettle-$nettleversion.tar.gz" >checksum
sha256sum --strict --quiet -c checksum
tar xzf nettle-$nettleversion.tar.gz
cd nettle-$nettleversion
echo $me: trying to configure nettle
./configure $configureflags --prefix="$nettleinstall" >$here/nettle.configure.log 2>&1
make install >$here/nettle.install.log 2>&1
echo $me: "local nettle install went ok"
cd $here
fi
./bootstrap.sh >bootstrap.log 2>&1
echo "$me: attempting configure with 32 bit flags... (see configure.log if it fails)"
./configure --build=i686-pc-linux-gnu CFLAGS=-m32 CXXFLAGS="-m32 -I$nettleinstall/include" LDFLAGS="-m32 -L$nettleinstall/lib" >configure.log 2>&1
echo "$me: building with 32 bit flags... (check make.log if it fails)"
make >make.log 2>&1
echo "$me: make check with 32 bit flags... (check make-check.log if it fails)"
LD_LIBRARY_PATH=$nettleinstall/lib make check >make-check.log 2>&1
echo "$me: 32 bit tests went fine!"
}
###############################################################################
#this is pretty quick so start with it.
verify_self_contained_headers
#keep track of which compilers have already been tested
echo "">inodes_for_tested_compilers.txt
#try all variants of g++
if which g++ >/dev/null ; then
for COMPILER in $(ls $(which g++)* |grep -v libc); do
inode=$(stat --dereference --format=%i $COMPILER)
if grep -q "^$inode\$" inodes_for_tested_compilers.txt ; then
echo $me: skipping this compiler $COMPILER - already tested
else
#echo trying gcc $GCC:$($GCC --version|head -n1)
echo $inode >>inodes_for_tested_compilers.txt
compile_and_test $COMPILER
fi
done
fi
#try all variants of clang
if which clang++ >/dev/null ; then
for COMPILER in $(ls $(which clang++)* |grep -v libc); do
inode=$(stat --dereference --format=%i $COMPILER)
if grep -q "^$inode\$" inodes_for_tested_compilers.txt ; then
echo $me: skipping this compiler $COMPILER - already tested
else
#echo trying gcc $GCC:$($GCC --version|head -n1)
echo $inode >>inodes_for_tested_compilers.txt
compile_and_test $COMPILER
fi
done
fi
rm inodes_for_tested_compilers.txt
#run unit tests with sanitizers enabled
ASSERT="--enable-assert"
run_with_sanitizer "-fsanitize=undefined -O3"
run_with_sanitizer "-fsanitize=address -O0"
#build and test with all flags from debian, if available. this increases
#the likelilihood rdfind will build when creating a deb package.
ASSERT=""
run_with_debian_buildflags
#make a test build with debug iterators
ASSERT="--enable-assert"
compile_and_test_standard g++ c++11 "-D_GLIBCXX_DEBUG"
#test run with clang/libc++
ASSERT="--enable-assert"
run_with_libcpp
ASSERT="--disable-assert"
run_with_libcpp
#test build with running through valgrind
if which valgrind >/dev/null; then
echo $me: running unit tests through valgrind
ASSERT="--disable-assert"
compile_and_test_standard g++ c++11 "-O3"
VALGRIND=valgrind make check >make-check.log
fi
#make sure it is possible to build a tar ball,
#unpack it, build and execute tests, then finally
#installing and running the program.
verify_packaging
#try to compile to 32 bit (downloads nettle and builds it in 32 bit mode)
build_32bit
echo "$me: congratulations, all tests that were possible to run passed!"