]> git.ipfire.org Git - thirdparty/gcc.git/blob - contrib/patch_tester.sh
2007-12-15 Sebastian Pop <sebastian.pop@amd.com>
[thirdparty/gcc.git] / contrib / patch_tester.sh
1 #!/bin/sh
2
3 # Tests a set of patches from a directory.
4 # Copyright (C) 2007 Free Software Foundation, Inc.
5 # Contributed by Sebastian Pop <sebastian.pop@amd.com>
6
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2 of the License, or
10 # (at your option) any later version.
11
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
16
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20
21 cat <<EOF
22
23 WARNING: This script should only be fed with patches from known
24 authorized and trusted sources. Don't even think about
25 hooking it up to a raw feed from the gcc-patches list or
26 you'll regret it.
27
28 EOF
29
30 args=$@
31
32 dashj=
33 default_standby=1
34 standby=$default_standby
35 default_watermark=0.60
36 watermark=$default_watermark
37 savecompilers=false
38 nogpg=false
39
40 usage() {
41 cat <<EOF
42 patch_tester.sh [-j<N>] [-standby N] [-watermark N] [-savecompilers] [-nogpg]
43 <source_dir> [patches_dir [state_dir [build_dir]]]
44
45 J is the flag passed to make. Default is empty string.
46
47 STANDBY is the number of minutes between checks for new patches in
48 PATCHES_DIR. Default is ${default_standby} minutes.
49
50 WATERMARK is the 5 minute average system charge under which a new
51 compile can start. Default is ${default_watermark}. Note that the comparison
52 is done in lexicographical order, so don't forget the leading 0.
53
54 SAVECOMPILERS copies the compilers in the same directory as the
55 test results for the non patched version. Default is not copy.
56
57 NOGPG can be used to avoid checking the GPG signature of patches.
58
59 SOURCE_DIR is the directory containing GCC's toplevel configure.
60
61 PATCHES_DIR is the directory containing the patches to be tested.
62 Default is SOURCE_DIR/patches.
63
64 STATE_DIR is where the tester maintains its internal state.
65 Default is SOURCE_DIR/state.
66
67 BUILD_DIR is the build tree, a temporary directory that this
68 script will delete and recreate. Default is SOURCE_DIR/obj.
69
70 EOF
71 exit 1
72 }
73
74 while [ $# -ne 0 ]; do
75 case $1 in
76 -j*)
77 dashj=$1; shift
78 ;;
79 -standby)
80 [[ $# > 2 ]] || usage
81 standby=$2; shift; shift
82 ;;
83 -watermark)
84 [[ $# > 2 ]] || usage
85 watermark=$2; shift; shift
86 ;;
87 -savecompilers)
88 savecompilers=true; shift
89 ;;
90 -nogpg)
91 nogpg=true; shift
92 ;;
93 -*)
94 echo "Invalid option: $1"
95 usage
96 ;;
97 *)
98 break
99 ;;
100 esac
101 done
102
103 test $# -eq 0 && usage
104
105 SOURCE=$1
106 PATCHES=
107 STATE=
108 BUILD=
109
110 if [[ $# < 2 ]]; then
111 PATCHES=$SOURCE/patches
112 else
113 PATCHES=$2
114 fi
115 if [[ $# < 3 ]]; then
116 STATE=$SOURCE/state
117 else
118 STATE=$3
119 fi
120 if [[ $# < 4 ]]; then
121 BUILD=$SOURCE/obj
122 else
123 BUILD=$4
124 fi
125
126 [ -d $PATCHES ] || mkdir -p $PATCHES
127 [ -d $STATE ] || mkdir -p $STATE
128 [ -d $STATE/patched ] || mkdir -p $STATE/patched
129 [ -d $SOURCE ] || mkdir -p $SOURCE
130 [ -f $SOURCE/config.guess ] || {
131 cd $SOURCE
132 svn -q co svn://gcc.gnu.org/svn/gcc/trunk .
133 }
134
135 VERSION=`svn info $SOURCE | grep "^Revision:" | sed -e "s/^Revision://g" -e "s/ //g"`
136
137 exec >> $STATE/tester.log 2>&1 || exit 1
138 set -x
139
140 TESTING=$STATE/testing
141 REPORT=$TESTING/report
142 PRISTINE=$TESTING/pristine
143 PATCHED=$TESTING/patched
144 PATCH=
145 TARGET=`$SOURCE/config.guess || exit 1`
146 TESTLOGS="gcc/testsuite/gcc/gcc.sum
147 gcc/testsuite/gfortran/gfortran.sum
148 gcc/testsuite/g++/g++.sum
149 gcc/testsuite/objc/objc.sum
150 $TARGET/libstdc++-v3/testsuite/libstdc++.sum
151 $TARGET/libffi/testsuite/libffi.sum
152 $TARGET/libjava/testsuite/libjava.sum
153 $TARGET/libgomp/testsuite/libgomp.sum
154 $TARGET/libmudflap/testsuite/libmudflap.sum"
155 COMPILERS="gcc/cc1
156 gcc/cc1obj
157 gcc/cc1plus
158 gcc/f951
159 gcc/jc1
160 gcc/gnat1
161 gcc/tree1"
162
163 now () {
164 echo `TZ=UTC date +"%Y_%m_%d_%H_%M_%S"`
165 }
166
167 report () {
168 echo "Checker: (`now`): $@" >> $REPORT
169 }
170
171 freport () {
172 if [ -s $1 ]; then
173 report "(cat $1"
174 cat $1 >> $REPORT
175 report "tac)"
176 fi
177 }
178
179 cleanup () {
180 cd $SOURCE
181 svn cleanup && svn revert -R . && svn st | cut -d' ' -f5- | xargs rm -v
182 }
183
184 selfexec () {
185 exec ${CONFIG_SHELL-/bin/sh} $SOURCE/contrib/patch_tester.sh $args
186 }
187
188 update () {
189 svn_branch=`grep "^branch:" $PATCH | sed -e "s/^branch://g" -e "s/ //g"`
190 if [ x$svn_branch = x ]; then
191 svn_branch=trunk
192 fi
193
194 svn_revision=`grep "^revision:" $PATCH | sed -e "s/^revision://g" -e "s/ //g"`
195 if [ x$svn_revision = x ]; then
196 svn_revision=HEAD
197 fi
198
199 cleanup
200 cd $SOURCE
201 case $svn_branch in
202 trunk)
203 if ! svn switch -r $svn_revision svn://gcc.gnu.org/svn/gcc/trunk &> $TESTING/svn ; then
204 report "failed to update svn sources with"
205 report "svn switch -r $svn_revision svn://gcc.gnu.org/svn/gcc/trunk"
206 freport $TESTING/svn
207 return 1
208 fi
209 ;;
210
211 svn://gcc.gnu.org/svn/gcc/*)
212 if ! svn switch -r $svn_revision $svn_branch &> $TESTING/svn ; then
213 report "failed to update svn sources with"
214 report "svn switch -r $svn_revision $svn_branch"
215 freport $TESTING/svn
216 return 1
217 fi
218 ;;
219
220 *)
221 if ! svn switch -r $svn_revision svn://gcc.gnu.org/svn/gcc/branches/$svn_branch &> $TESTING/svn ; then
222 report "failed to update svn sources with"
223 report "svn switch -r $svn_revision svn://gcc.gnu.org/svn/gcc/branches/$svn_branch"
224 freport $TESTING/svn
225 return 1
226 fi
227 ;;
228 esac
229
230 current_version=`svn info $SOURCE | grep "^Revision:" | sed -e "s/^Revision://g" -e "s/ //g"`
231 if [[ $VERSION < $current_version ]]; then
232 if [ -f $SOURCE/contrib/patch_tester.sh ]; then
233 selfexec
234 fi
235 fi
236
237 return 0
238 }
239
240 apply_patch () {
241 if [ $nogpg = false ]; then
242 if ! gpg --batch --verify $PATCH &> $TESTING/gpgverify ; then
243 report "your patch failed to verify:"
244 freport $TESTING/gpgverify
245 return 1
246 fi
247 fi
248
249 # Detect if the patch was created in toplev GCC.
250 grep "^Index: " $PATCH | grep "gcc/"
251 if [ $? = 0 ]; then
252 cd $SOURCE
253 if ! patch -p0 < $PATCH &> $TESTING/patching ; then
254 report "your patch failed to apply:"
255 freport $TESTING/patching
256 return 1
257 fi
258 else
259 cd $SOURCE/gcc
260 if ! patch -p0 < $PATCH &> $TESTING/patching ; then
261 report "your patch failed to apply:"
262 freport $TESTING/patching
263 return 1
264 fi
265 fi
266 }
267
268 save_compilers () {
269 for COMPILER in $COMPILERS ; do
270 if [ -f $BUILD/$COMPILER ]; then
271 cp $BUILD/$COMPILER $PRISTINE
272 fi
273 done
274 }
275
276 bootntest () {
277 rm -rf $BUILD
278 mkdir $BUILD
279 cd $BUILD
280
281 CONFIG_OPTIONS=`grep "^configure:" $PATCH | sed -e "s/^configure://g"`
282 if ! $SOURCE/configure $CONFIG_OPTIONS &> $1/configure ; then
283 report "configure failed with:"
284 freport $1/configure
285 return 1
286 fi
287
288 if ! make $dashj `grep "^make:" $PATCH | sed -e "s/^make://g"` bootstrap &> $1/bootstrap ; then
289 report "bootstrap failed with last lines:"
290 tail -30 $1/bootstrap > $1/last_bootstrap
291 freport $1/last_bootstrap
292 report "grep --context=20 Error bootstrap:"
293 grep --context=20 Error $1/bootstrap > $1/bootstrap_error
294 freport $1/bootstrap_error
295 return 1
296 fi
297
298 CHECK_OPTIONS=`grep "^check:" $PATCH | sed -e "s/^check://g"`
299 make $dashj $CHECK_OPTIONS -k check &> $1/check
300
301 for LOG in $TESTLOGS ; do
302 if [ -f $BUILD/$LOG ]; then
303 mv $BUILD/$LOG $1
304 mv `echo "$BUILD/$LOG" | sed -e "s/\.sum/\.log/g"` $1
305 fi
306 done
307
308 return 0
309 }
310
311 bootntest_patched () {
312 cleanup
313 mkdir -p $PATCHED
314 apply_patch && bootntest $PATCHED
315 return $?
316 }
317
318 # Build the pristine tree with exactly the same options as the patch under test.
319 bootntest_pristine () {
320 cleanup
321 current_branch=`svn info $SOURCE | grep "^URL:" | sed -e "s/URL: //g" -e "s/svn:\/\/gcc.gnu.org\/svn\/gcc\///g"`
322 current_version=`svn info $SOURCE | grep "^Revision:" | sed -e "s/^Revision://g" -e "s/ //g"`
323 PRISTINE=$STATE/$current_branch/$current_version
324
325 if [ -d $PRISTINE ]; then
326 ln -s $PRISTINE $TESTING/pristine
327 return 0
328 else
329 mkdir -p $PRISTINE
330 ln -s $PRISTINE $TESTING/pristine
331 bootntest $PRISTINE
332 RETVAL=$?
333 if [ $RETVAL = 0 -a $savecompilers = true ]; then
334 save_compilers
335 fi
336 return $RETVAL
337 fi
338 }
339
340 regtest () {
341 touch $1/report
342 touch $1/passes
343 touch $1/failed
344 touch $1/regress
345
346 for LOG in $TESTLOGS ; do
347 NLOG=`basename $LOG`
348 if [ -f $1/$NLOG ]; then
349 awk '/^FAIL: / { print "'$NLOG'",$2; }' $1/$NLOG
350 fi
351 done | sort | uniq > $1/failed
352
353 comm -12 $1/failed $1/passes >> $1/regress
354 NUMREGRESS=`wc -l < $1/regress | tr -d ' '`
355
356 if [ $NUMREGRESS -eq 0 ] ; then
357 for LOG in $TESTLOGS ; do
358 NLOG=`basename $LOG`
359 if [ -f $1/$NLOG ] ; then
360 awk '/^PASS: / { print "'$NLOG'",$2; }' $1/$NLOG
361 fi
362 done | sort | uniq | comm -23 - $1/failed > $1/passes
363 echo "there are no regressions with your patch." >> $1/report
364 else
365 echo "with your patch there are $NUMREGRESS regressions." >> $1/report
366 echo "list of regressions with your patch:" >> $1/report
367 cat $1/regress >> $1/report
368 fi
369 }
370
371 contrib_compare_tests () {
372 report "comparing logs with contrib/compare_tests:"
373 for LOG in $TESTLOGS ; do
374 NLOG=`basename $LOG`
375 if [ -f $PRISTINE/$NLOG -a -f $PATCHED/$NLOG ]; then
376 $SOURCE/contrib/compare_tests $PRISTINE/$NLOG $PATCHED/$NLOG > $TESTING/compare_$NLOG
377 freport $TESTING/compare_$NLOG
378 fi
379 done
380 }
381
382 compare_passes () {
383 regtest $PRISTINE
384 cp $PRISTINE/passes $PATCHED
385 regtest $PATCHED
386 freport $PATCHED/report
387 report "FAILs with patched version:"
388 freport $PATCHED/failed
389 report "FAILs with pristine version:"
390 freport $PRISTINE/failed
391
392 # contrib_compare_tests
393 }
394
395 write_report () {
396 backup_patched=$STATE/patched/`now`
397 report "The files used for the validation of your patch are stored in $backup_patched on the tester machine."
398
399 EMAIL=`grep "^email:" $PATCH | sed -e "s/^email://g" -e "s/ //g"`
400 if [ x$EMAIL != x ]; then
401 mutt -s "[regtest] Results for `basename $PATCH` on $TARGET" -i $REPORT -a $PATCH $EMAIL
402 fi
403
404 mv $TESTING $backup_patched
405 }
406
407 announce () {
408 EMAIL=`grep "^email:" $PATCH | sed -e "s/^email://g" -e "s/ //g"`
409 if [ x$EMAIL != x ]; then
410
411 START_REPORT=$TESTING/start_report
412 echo "Hi, " >> $START_REPORT
413 echo "I'm the automatic tester running on $TARGET." >> $START_REPORT
414 echo "I just started to look at your patch `basename $PATCH`." >> $START_REPORT
415 echo "Bye, your automatic tester." >> $START_REPORT
416 mutt -s "[regtest] Starting bootstrap for `basename $PATCH` on $TARGET" -i $START_REPORT $EMAIL
417 fi
418 }
419
420 # After selfexec, $TESTING is already set up.
421 if [ -d $TESTING ]; then
422 # The only file in $TESTING is the patch.
423 PATCH=`ls -rt -1 $TESTING | head -1`
424 PATCH=$TESTING/$PATCH
425 if [ -f $PATCH ]; then
426 bootntest_patched && bootntest_pristine && compare_passes
427 write_report
428 fi
429 fi
430
431 while true; do
432 PATCH=`ls -rt -1 $PATCHES | head -1`
433 if [ x$PATCH = x ]; then
434 sleep ${standby}m
435 else
436 sysload=`uptime | cut -d, -f 5`
437 if [[ $sysload > $watermark ]]; then
438 # Wait a bit when system load is too high.
439 sleep ${standby}m
440 else
441 mkdir -p $TESTING
442 mv $PATCHES/$PATCH $TESTING/
443 PATCH=$TESTING/$PATCH
444
445 announce
446 update && bootntest_patched && bootntest_pristine && compare_passes
447 write_report
448 fi
449 fi
450 done