OSDN Git Service

2007-12-15 Sebastian Pop <sebastian.pop@amd.com>
[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  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