comparison contrib/patch_tester.sh @ 0:a06113de4d67

first commit
author kent <kent@cr.ie.u-ryukyu.ac.jp>
date Fri, 17 Jul 2009 14:47:48 +0900
parents
children f6334be47118
comparison
equal deleted inserted replaced
-1:000000000000 0:a06113de4d67
1 #!/bin/sh
2
3 # Tests a set of patches from a directory.
4 # Copyright (C) 2007, 2008 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 3 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 svnpath=svn://gcc.gnu.org/svn/gcc
33 dashj=
34 default_standby=1
35 standby=$default_standby
36 default_watermark=0.60
37 watermark=$default_watermark
38 savecompilers=false
39 nogpg=false
40 stop=false
41
42 usage() {
43 cat <<EOF
44 patch_tester.sh [-j<N>] [-standby N] [-watermark N] [-savecompilers] [-nogpg]
45 [-svnpath URL] [-stop]
46 <source_dir> [patches_dir [state_dir [build_dir]]]
47
48 J is the flag passed to make. Default is empty string.
49
50 STANDBY is the number of minutes between checks for new patches in
51 PATCHES_DIR. Default is ${default_standby} minutes.
52
53 WATERMARK is the 5 minute average system charge under which a new
54 compile can start. Default is ${default_watermark}.
55
56 SAVECOMPILERS copies the compilers in the same directory as the
57 test results for the non patched version. Default is not copy.
58
59 NOGPG can be used to avoid checking the GPG signature of patches.
60
61 URL is the location of the GCC SVN repository. The default is
62 ${svnpath}.
63
64 STOP exits when PATCHES_DIR is empty.
65
66 SOURCE_DIR is the directory containing GCC's toplevel configure.
67
68 PATCHES_DIR is the directory containing the patches to be tested.
69 Default is SOURCE_DIR/patches.
70
71 STATE_DIR is where the tester maintains its internal state.
72 Default is SOURCE_DIR/state.
73
74 BUILD_DIR is the build tree, a temporary directory that this
75 script will delete and recreate. Default is SOURCE_DIR/obj.
76
77 EOF
78 exit 1
79 }
80
81 makedir () {
82 DIRNAME=$1
83 mkdir -p $DIRNAME
84 if [ $? -ne 0 ]; then
85 echo "ERROR: could not make directory $DIRNAME"
86 exit 1
87 fi
88 }
89
90 while [ $# -ne 0 ]; do
91 case $1 in
92 -j*)
93 dashj=$1; shift
94 ;;
95 -standby)
96 [[ $# > 2 ]] || usage
97 standby=$2; shift; shift
98 ;;
99 -watermark)
100 [[ $# > 2 ]] || usage
101 watermark=$2; shift; shift
102 ;;
103 -savecompilers)
104 savecompilers=true; shift
105 ;;
106 -nogpg)
107 nogpg=true; shift
108 ;;
109 -stop)
110 stop=true; shift
111 ;;
112 -svnpath)
113 svnpath=$2; shift; shift
114 ;;
115 -*)
116 echo "Invalid option: $1"
117 usage
118 ;;
119 *)
120 break
121 ;;
122 esac
123 done
124
125 test $# -eq 0 && usage
126
127 SOURCE=$1
128 PATCHES=
129 STATE=
130 BUILD=
131
132 if [[ $# < 2 ]]; then
133 PATCHES=$SOURCE/patches
134 else
135 PATCHES=$2
136 fi
137 if [[ $# < 3 ]]; then
138 STATE=$SOURCE/state
139 else
140 STATE=$3
141 fi
142 if [[ $# < 4 ]]; then
143 BUILD=$SOURCE/obj
144 else
145 BUILD=$4
146 fi
147
148 [ -d $PATCHES ] || makedir $PATCHES
149 [ -d $STATE ] || makedir $STATE
150 [ -d $STATE/patched ] || makedir $STATE/patched
151 [ -d $SOURCE ] || makedir $SOURCE
152 [ -f $SOURCE/config.guess ] || {
153 cd $SOURCE
154 svn -q co $svnpath/trunk .
155 if [ $? -ne 0 ]; then
156 echo "ERROR: initial svn checkout failed"
157 exit 1
158 fi
159 }
160
161 # This can contain required local settings:
162 # default_config configure options, always passed
163 # default_make make bootstrap options, always passed
164 # default_check make check options, always passed
165 [ -f $STATE/defaults ] && . $STATE/defaults
166
167 VERSION=`svn info $SOURCE | grep "^Revision:" | sed -e "s/^Revision://g" -e "s/ //g"`
168
169 exec >> $STATE/tester.log 2>&1 || exit 1
170 set -x
171
172 TESTING=$STATE/testing
173 REPORT=$TESTING/report
174 PRISTINE=$TESTING/pristine
175 PATCHED=$TESTING/patched
176 PATCH=
177 TARGET=`$SOURCE/config.guess || exit 1`
178 TESTLOGS="gcc/testsuite/gcc/gcc.sum
179 gcc/testsuite/gfortran/gfortran.sum
180 gcc/testsuite/g++/g++.sum
181 gcc/testsuite/objc/objc.sum
182 $TARGET/libstdc++-v3/testsuite/libstdc++.sum
183 $TARGET/libffi/testsuite/libffi.sum
184 $TARGET/libjava/testsuite/libjava.sum
185 $TARGET/libgomp/testsuite/libgomp.sum
186 $TARGET/libmudflap/testsuite/libmudflap.sum"
187 COMPILERS="gcc/cc1
188 gcc/cc1obj
189 gcc/cc1plus
190 gcc/f951
191 gcc/jc1
192 gcc/gnat1
193 gcc/tree1"
194
195 now () {
196 echo `TZ=UTC date +"%Y_%m_%d_%H_%M_%S"`
197 }
198
199 report () {
200 echo "$@" >> $REPORT
201 }
202
203 freport () {
204 if [ -s $1 ]; then
205 report "(cat $1"
206 cat $1 >> $REPORT
207 report "tac)"
208 fi
209 }
210
211 cleanup () {
212 cd $SOURCE
213 svn cleanup && svn revert -R . && svn st | cut -d' ' -f5- | xargs rm -v
214 }
215
216 selfexec () {
217 exec ${CONFIG_SHELL-/bin/sh} $0 $args
218 }
219
220 update () {
221 svn_branch=`grep "^branch:" $PATCH | sed -e "s/^branch://g" -e "s/ //g"`
222 if [ x$svn_branch = x ]; then
223 svn_branch=trunk
224 fi
225
226 svn_revision=`grep "^revision:" $PATCH | sed -e "s/^revision://g" -e "s/ //g"`
227 if [ x$svn_revision = x ]; then
228 svn_revision=HEAD
229 fi
230
231 cleanup
232 cd $SOURCE
233 case $svn_branch in
234 trunk)
235 if ! svn switch -r $svn_revision $svnpath/trunk &> $TESTING/svn ; then
236 report "failed to update svn sources with"
237 report "svn switch -r $svn_revision $svnpath/trunk"
238 freport $TESTING/svn
239 return 1
240 fi
241 ;;
242
243 ${svnpath}*)
244 if ! svn switch -r $svn_revision $svn_branch &> $TESTING/svn ; then
245 report "failed to update svn sources with"
246 report "svn switch -r $svn_revision $svn_branch"
247 freport $TESTING/svn
248 return 1
249 fi
250 ;;
251
252 *)
253 if ! svn switch -r $svn_revision $svnpath/branches/$svn_branch &> $TESTING/svn ; then
254 report "failed to update svn sources with"
255 report "svn switch -r $svn_revision $svnpath/branches/$svn_branch"
256 freport $TESTING/svn
257 return 1
258 fi
259 ;;
260 esac
261 contrib/gcc_update --touch
262
263 current_version=`svn info $SOURCE | grep "^Revision:" | sed -e "s/^Revision://g" -e "s/ //g"`
264 if [[ $VERSION < $current_version ]]; then
265 if [ -f $SOURCE/contrib/patch_tester.sh ]; then
266 selfexec
267 fi
268 fi
269
270 return 0
271 }
272
273 apply_patch () {
274 if [ $nogpg = false ]; then
275 if ! gpg --batch --verify $PATCH &> $TESTING/gpgverify ; then
276 report "your patch failed to verify:"
277 freport $TESTING/gpgverify
278 return 1
279 fi
280 fi
281
282 cd $SOURCE
283 if ! patch -p0 < $PATCH &> $TESTING/patching ; then
284 report "your patch failed to apply:"
285 report "(check that the patch was created at the top level)"
286 freport $TESTING/patching
287 return 1
288 fi
289
290 # Just assume indexes for now -- not really great, but svn always
291 # makes them.
292 grep "^Index: " $PATCH | sed -e 's/Index: //' | while read file; do
293 # If the patch resulted in an empty file, delete it.
294 # This is how svn reports deletions.
295 if [ ! -s $file ]; then
296 rm -f $file
297 report "Deleting empty file $file"
298 fi
299 done
300 }
301
302 save_compilers () {
303 for COMPILER in $COMPILERS ; do
304 if [ -f $BUILD/$COMPILER ]; then
305 cp $BUILD/$COMPILER $PRISTINE
306 fi
307 done
308 }
309
310 bootntest () {
311 rm -rf $BUILD
312 mkdir $BUILD
313 cd $BUILD
314
315 CONFIG_OPTIONS=`grep "^configure:" $PATCH | sed -e "s/^configure://g"`
316 CONFIG_OPTIONS="$default_config $CONFIG_OPTIONS"
317 if ! eval $SOURCE/configure $CONFIG_OPTIONS &> $1/configure ; then
318 report "configure with `basename $1` version failed with:"
319 freport $1/configure
320 return 1
321 fi
322
323 MAKE_ARGS=`grep "^make:" $PATCH | sed -e "s/^make://g"`
324 MAKE_ARGS="$default_make $MAKE_ARGS"
325 if ! eval make $dashj $MAKE_ARGS &> $1/bootstrap ; then
326 report "bootstrap with `basename $1` version failed with last lines:"
327 tail -30 $1/bootstrap > $1/last_bootstrap
328 freport $1/last_bootstrap
329 report "grep --context=20 Error bootstrap:"
330 grep --context=20 Error $1/bootstrap > $1/bootstrap_error
331 freport $1/bootstrap_error
332 return 1
333 fi
334
335 CHECK_OPTIONS=`grep "^check:" $PATCH | sed -e "s/^check://g"`
336 CHECK_OPTIONS="$default_check $CHECK_OPTIONS"
337 eval make $dashj $CHECK_OPTIONS -k check &> $1/check
338
339 SUITESRUN="`grep 'Summary ===' $1/check | cut -d' ' -f 2 | sort`"
340 if [ x$SUITESRUN = x ]; then
341 report "check with `basename $1` version failed, no testsuites were run"
342 return 1
343 fi
344
345 for LOG in $TESTLOGS ; do
346 if [ -f $BUILD/$LOG ]; then
347 mv $BUILD/$LOG $1
348 mv `echo "$BUILD/$LOG" | sed -e "s/\.sum/\.log/g"` $1
349 fi
350 done
351
352 return 0
353 }
354
355 bootntest_patched () {
356 cleanup
357 mkdir -p $PATCHED
358 apply_patch && bootntest $PATCHED
359 return $?
360 }
361
362 # Build the pristine tree with exactly the same options as the patch under test.
363 bootntest_pristine () {
364 cleanup
365 current_branch=`svn info $SOURCE | grep "^URL:" | sed -e "s/URL: //g" -e "s,${svnpath},,g"`
366 current_version=`svn info $SOURCE | grep "^Revision:" | sed -e "s/^Revision://g" -e "s/ //g"`
367 PRISTINE=$STATE/$current_branch/$current_version
368
369 if [ -d $PRISTINE ]; then
370 ln -s $PRISTINE $TESTING/pristine
371 return 0
372 else
373 mkdir -p $PRISTINE
374 ln -s $PRISTINE $TESTING/pristine
375 bootntest $PRISTINE
376 RETVAL=$?
377 if [ $RETVAL = 0 -a $savecompilers = true ]; then
378 save_compilers
379 fi
380 return $RETVAL
381 fi
382 }
383
384 regtest () {
385 touch $1/report
386 touch $1/passes
387 touch $1/failed
388 touch $1/regress
389
390 for LOG in $TESTLOGS ; do
391 NLOG=`basename $LOG`
392 if [ -f $1/$NLOG ]; then
393 awk '/^FAIL: / { print "'$NLOG'",$2; }' $1/$NLOG
394 fi
395 done | sort | uniq > $1/failed
396
397 comm -12 $1/failed $1/passes >> $1/regress
398 NUMREGRESS=`wc -l < $1/regress | tr -d ' '`
399
400 if [ $NUMREGRESS -eq 0 ] ; then
401 for LOG in $TESTLOGS ; do
402 NLOG=`basename $LOG`
403 if [ -f $1/$NLOG ] ; then
404 awk '/^PASS: / { print "'$NLOG'",$2; }' $1/$NLOG
405 fi
406 done | sort | uniq | comm -23 - $1/failed > $1/passes
407 echo "there are no regressions with your patch." >> $1/report
408 else
409 echo "with your patch there are $NUMREGRESS regressions." >> $1/report
410 echo "list of regressions with your patch:" >> $1/report
411 cat $1/regress >> $1/report
412 fi
413 }
414
415 contrib_compare_tests () {
416 report "comparing logs with contrib/compare_tests:"
417 for LOG in $TESTLOGS ; do
418 NLOG=`basename $LOG`
419 if [ -f $PRISTINE/$NLOG -a -f $PATCHED/$NLOG ]; then
420 $SOURCE/contrib/compare_tests $PRISTINE/$NLOG $PATCHED/$NLOG > $TESTING/compare_$NLOG
421 freport $TESTING/compare_$NLOG
422 fi
423 done
424 }
425
426 compare_passes () {
427 regtest $PRISTINE
428 cp $PRISTINE/passes $PATCHED
429 regtest $PATCHED
430 freport $PATCHED/report
431 report "FAILs with patched version:"
432 freport $PATCHED/failed
433 report "FAILs with pristine version:"
434 freport $PRISTINE/failed
435
436 # contrib_compare_tests
437 }
438
439 write_report () {
440 backup_patched=$STATE/patched/`now`
441 report "The files used for the validation of your patch are stored in $backup_patched on the tester machine."
442
443 EMAIL=`grep "^email:" $PATCH | sed -e "s/^email://g" -e "s/ //g"`
444 if [ x$EMAIL != x ]; then
445 mutt -s "[regtest] Results for `basename $PATCH` on $TARGET" -i $REPORT -a $PATCH $EMAIL
446 fi
447
448 mv $TESTING $backup_patched
449 }
450
451 announce () {
452 EMAIL=`grep "^email:" $PATCH | sed -e "s/^email://g" -e "s/ //g"`
453 if [ x$EMAIL != x ]; then
454
455 START_REPORT=$TESTING/start_report
456 echo "Hi, " >> $START_REPORT
457 echo "I'm the automatic tester running on $TARGET." >> $START_REPORT
458 echo "I just started to look at your patch `basename $PATCH`." >> $START_REPORT
459 echo "Bye, your automatic tester." >> $START_REPORT
460 mutt -s "[regtest] Starting bootstrap for `basename $PATCH` on $TARGET" -i $START_REPORT $EMAIL
461 fi
462 }
463
464 # After selfexec, $TESTING is already set up.
465 if [ -d $TESTING ]; then
466 # The only file in $TESTING is the patch.
467 PATCH=`ls -rt -1 $TESTING | head -1`
468 PATCH=$TESTING/$PATCH
469 if [ -f $PATCH ]; then
470 bootntest_patched && bootntest_pristine && compare_passes
471 write_report
472 fi
473 fi
474
475 firstpatch=true
476 while true; do
477 PATCH=`ls -rt -1 $PATCHES | head -1`
478 if [ x$PATCH = x ]; then
479 if [ $stop = true ]; then
480 if [ $firstpatch = true ]; then
481 echo "No patches ready to test, quitting."
482 exit 1
483 else
484 echo "No more patches to test."
485 exit 0
486 fi
487 fi
488 sleep ${standby}m
489 else
490 firstpatch=false
491 sysload=`uptime | cut -d, -f 5`
492 if [[ $sysload > $watermark ]]; then
493 # Wait a bit when system load is too high.
494 sleep ${standby}m
495 else
496 mkdir -p $TESTING
497 mv $PATCHES/$PATCH $TESTING/
498 PATCH=$TESTING/$PATCH
499
500 announce
501 update && bootntest_patched && bootntest_pristine && compare_passes
502 write_report
503 fi
504 fi
505 done