OSDN Git Service

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