]> git.ipfire.org Git - thirdparty/gcc.git/blame - contrib/patch_tester.sh
patch_tester.sh: New.
[thirdparty/gcc.git] / contrib / patch_tester.sh
CommitLineData
0f55312a
SP
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
21cat <<EOF
22
23WARNING: 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
28EOF
29
30args=$@
31
32dashj=
33default_standby=1
34standby=$default_standby
35default_watermark=0.60
36watermark=$default_watermark
37savecompilers=false
38nogpg=false
39
40usage() {
41 cat <<EOF
42patch_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
70EOF
71 exit 1
72}
73
74while [ $# -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
101done
102
103test $# -eq 0 && usage
104
105SOURCE=$1
106PATCHES=
107STATE=
108BUILD=
109
110if [[ $# < 2 ]]; then
111 PATCHES=$SOURCE/patches
112else
113 PATCHES=$2
114fi
115if [[ $# < 3 ]]; then
116 STATE=$SOURCE/state
117else
118 STATE=$3
119fi
120if [[ $# < 4 ]]; then
121 BUILD=$SOURCE/obj
122else
123 BUILD=$4
124fi
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
135VERSION=`svn info $SOURCE | grep "^Revision:" | sed -e "s/^Revision://g" -e "s/ //g"`
136
137exec >> $STATE/tester.log 2>&1 || exit 1
138set -x
139
140TESTING=$STATE/testing
141REPORT=$TESTING/report
142PRISTINE=$TESTING/pristine
143PATCHED=$TESTING/patched
144PATCH=
145TARGET=`$SOURCE/config.guess || exit 1`
146TESTLOGS="gcc/testsuite/gcc/gcc.sum
147gcc/testsuite/gfortran/gfortran.sum
148gcc/testsuite/g++/g++.sum
149gcc/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"
155COMPILERS="gcc/cc1
156gcc/cc1obj
157gcc/cc1plus
158gcc/f951
159gcc/jc1
160gcc/gnat1
161gcc/tree1"
162
163now () {
164 echo `TZ=UTC date +"%Y_%m_%d_%H_%M_%S"`
165}
166
167report () {
168 echo "Checker: (`now`): $@" >> $REPORT
169}
170
171freport () {
172 if [ -s $1 ]; then
173 report "(cat $1"
174 cat $1 >> $REPORT
175 report "tac)"
176 fi
177}
178
179cleanup () {
180 cd $SOURCE
181
182 # FORNOW: Until this script is not committed to trunk, save and restore it.
183 mv $SOURCE/contrib/patch_tester.sh $STATE
184 svn cleanup && svn revert -R . && svn st | cut -d' ' -f5- | xargs rm -v
185 mv $STATE/patch_tester.sh $SOURCE/contrib/
186}
187
188selfexec () {
189 exec ${CONFIG_SHELL-/bin/sh} $SOURCE/contrib/patch_tester.sh $args
190}
191
192update () {
193 svn_branch=`grep "^branch:" $PATCH | sed -e "s/^branch://g" -e "s/ //g"`
194 if [ x$svn_branch = x ]; then
195 svn_branch=trunk
196 fi
197
198 svn_revision=`grep "^revision:" $PATCH | sed -e "s/^revision://g" -e "s/ //g"`
199 if [ x$svn_revision = x ]; then
200 svn_revision=HEAD
201 fi
202
203 cleanup
204 cd $SOURCE
205 case $svn_branch in
206 trunk)
207 if ! svn switch -r $svn_revision svn://gcc.gnu.org/svn/gcc/trunk &> $TESTING/svn ; then
208 report "failed to update svn sources with"
209 report "svn switch -r $svn_revision svn://gcc.gnu.org/svn/gcc/trunk"
210 freport $TESTING/svn
211 return 1
212 fi
213 ;;
214
215 svn://gcc.gnu.org/svn/gcc/*)
216 if ! svn switch -r $svn_revision $svn_branch &> $TESTING/svn ; then
217 report "failed to update svn sources with"
218 report "svn switch -r $svn_revision $svn_branch"
219 freport $TESTING/svn
220 return 1
221 fi
222 ;;
223
224 *)
225 if ! svn switch -r $svn_revision svn://gcc.gnu.org/svn/gcc/branches/$svn_branch &> $TESTING/svn ; then
226 report "failed to update svn sources with"
227 report "svn switch -r $svn_revision svn://gcc.gnu.org/svn/gcc/branches/$svn_branch"
228 freport $TESTING/svn
229 return 1
230 fi
231 ;;
232 esac
233
234 current_version=`svn info $SOURCE | grep "^Revision:" | sed -e "s/^Revision://g" -e "s/ //g"`
235 if [[ $VERSION < $current_version ]]; then
236 if [ -f $SOURCE/contrib/patch_tester.sh ]; then
237 selfexec
238 fi
239 fi
240
241 return 0
242}
243
244apply_patch () {
245 if [ $nogpg = false ]; then
246 if ! gpg --batch --verify $PATCH &> $TESTING/gpgverify ; then
247 report "your patch failed to verify:"
248 freport $TESTING/gpgverify
249 return 1
250 fi
251 fi
252
253 # Detect if the patch was created in toplev GCC.
254 grep "^Index: " $PATCH | grep "gcc/"
255 if [ $? = 0 ]; then
256 cd $SOURCE
257 if ! patch -p0 < $PATCH &> $TESTING/patching ; then
258 report "your patch failed to apply:"
259 freport $TESTING/patching
260 return 1
261 fi
262 else
263 cd $SOURCE/gcc
264 if ! patch -p0 < $PATCH &> $TESTING/patching ; then
265 report "your patch failed to apply:"
266 freport $TESTING/patching
267 return 1
268 fi
269 fi
270}
271
272save_compilers () {
273 for COMPILER in $COMPILERS ; do
274 if [ -f $BUILD/$COMPILER ]; then
275 cp $BUILD/$COMPILER $PRISTINE
276 fi
277 done
278}
279
280bootntest () {
281 rm -rf $BUILD
282 mkdir $BUILD
283 cd $BUILD
284
285 CONFIG_OPTIONS=`grep "^configure:" $PATCH | sed -e "s/^configure://g"`
286 if ! $SOURCE/configure $CONFIG_OPTIONS &> $1/configure ; then
287 report "configure failed with:"
288 freport $1/configure
289 return 1
290 fi
291
292 if ! make $dashj `grep "^make:" $PATCH | sed -e "s/^make://g"` bootstrap &> $1/bootstrap ; then
293 report "bootstrap failed with last lines:"
294 tail -30 $1/bootstrap > $1/last_bootstrap
295 freport $1/last_bootstrap
296 report "grep --context=20 Error bootstrap:"
297 grep --context=20 Error $1/bootstrap > $1/bootstrap_error
298 freport $1/bootstrap_error
299 return 1
300 fi
301
302 CHECK_OPTIONS=`grep "^check:" $PATCH | sed -e "s/^check://g"`
303 make $dashj $CHECK_OPTIONS -k check &> $1/check
304
305 for LOG in $TESTLOGS ; do
306 if [ -f $BUILD/$LOG ]; then
307 mv $BUILD/$LOG $1
308 mv `echo "$BUILD/$LOG" | sed -e "s/\.sum/\.log/g"` $1
309 fi
310 done
311
312 return 0
313}
314
315bootntest_patched () {
316 cleanup
317 mkdir -p $PATCHED
318 apply_patch && bootntest $PATCHED
319 return $?
320}
321
322# Build the pristine tree with exactly the same options as the patch under test.
323bootntest_pristine () {
324 cleanup
325 current_branch=`svn info $SOURCE | grep "^URL:" | sed -e "s/URL: //g" -e "s/svn:\/\/gcc.gnu.org\/svn\/gcc\///g"`
326 current_version=`svn info $SOURCE | grep "^Revision:" | sed -e "s/^Revision://g" -e "s/ //g"`
327 PRISTINE=$STATE/$current_branch/$current_version
328
329 if [ -d $PRISTINE ]; then
330 ln -s $PRISTINE $TESTING/pristine
331 return 0
332 else
333 mkdir -p $PRISTINE
334 ln -s $PRISTINE $TESTING/pristine
335 bootntest $PRISTINE
336 RETVAL=$?
337 if [ $RETVAL = 0 -a $savecompilers = true ]; then
338 save_compilers
339 fi
340 return $RETVAL
341 fi
342}
343
344regtest () {
345 touch $1/report
346 touch $1/passes
347 touch $1/failed
348 touch $1/regress
349
350 for LOG in $TESTLOGS ; do
351 NLOG=`basename $LOG`
352 if [ -f $1/$NLOG ]; then
353 awk '/^FAIL: / { print "'$NLOG'",$2; }' $1/$NLOG
354 fi
355 done | sort | uniq > $1/failed
356
357 comm -12 $1/failed $1/passes >> $1/regress
358 NUMREGRESS=`wc -l < $1/regress | tr -d ' '`
359
360 if [ $NUMREGRESS -eq 0 ] ; then
361 for LOG in $TESTLOGS ; do
362 NLOG=`basename $LOG`
363 if [ -f $1/$NLOG ] ; then
364 awk '/^PASS: / { print "'$NLOG'",$2; }' $1/$NLOG
365 fi
366 done | sort | uniq | comm -23 - $1/failed > $1/passes
367 echo "there are no regressions with your patch." >> $1/report
368 else
369 echo "with your patch there are $NUMREGRESS regressions." >> $1/report
370 echo "list of regressions with your patch:" >> $1/report
371 cat $1/regress >> $1/report
372 fi
373}
374
375contrib_compare_tests () {
376 report "comparing logs with contrib/compare_tests:"
377 for LOG in $TESTLOGS ; do
378 NLOG=`basename $LOG`
379 if [ -f $PRISTINE/$NLOG -a -f $PATCHED/$NLOG ]; then
380 $SOURCE/contrib/compare_tests $PRISTINE/$NLOG $PATCHED/$NLOG > $TESTING/compare_$NLOG
381 freport $TESTING/compare_$NLOG
382 fi
383 done
384}
385
386compare_passes () {
387 regtest $PRISTINE
388 cp $PRISTINE/passes $PATCHED
389 regtest $PATCHED
390 freport $PATCHED/report
391 report "FAILs with patched version:"
392 freport $PATCHED/failed
393 report "FAILs with pristine version:"
394 freport $PRISTINE/failed
395
396 # contrib_compare_tests
397}
398
399write_report () {
400 backup_patched=$STATE/patched/`now`
401 report "The files used for the validation of your patch are stored in $backup_patched on the tester machine."
402
403 EMAIL=`grep "^email:" $PATCH | sed -e "s/^email://g" -e "s/ //g"`
404 if [ x$EMAIL != x ]; then
405 mutt -s "[regtest] Results for `basename $PATCH` on $TARGET" -i $REPORT -a $PATCH $EMAIL
406 fi
407
408 mv $TESTING $backup_patched
409}
410
411announce () {
412 EMAIL=`grep "^email:" $PATCH | sed -e "s/^email://g" -e "s/ //g"`
413 if [ x$EMAIL != x ]; then
414
415 START_REPORT=$TESTING/start_report
416 echo "Hi, " >> $START_REPORT
417 echo "I'm the automatic tester running on $TARGET." >> $START_REPORT
418 echo "I just started to look at your patch `basename $PATCH`." >> $START_REPORT
419 echo "Bye, your automatic tester." >> $START_REPORT
420 mutt -s "[regtest] Starting bootstrap for `basename $PATCH` on $TARGET" -i $START_REPORT $EMAIL
421 fi
422}
423
424# After selfexec, $TESTING is already set up.
425if [ -d $TESTING ]; then
426 # The only file in $TESTING is the patch.
427 PATCH=`ls -rt -1 $TESTING | head -1`
428 PATCH=$TESTING/$PATCH
429 if [ -f $PATCH ]; then
430 bootntest_patched && bootntest_pristine && compare_passes
431 write_report
432 fi
433fi
434
435while true; do
436 PATCH=`ls -rt -1 $PATCHES | head -1`
437 if [ x$PATCH = x ]; then
438 sleep ${standby}m
439 else
440 sysload=`uptime | cut -d, -f 5`
441 if [[ $sysload > $watermark ]]; then
442 # Wait a bit when system load is too high.
443 sleep ${standby}m
444 else
445 mkdir -p $TESTING
446 mv $PATCHES/$PATCH $TESTING/
447 PATCH=$TESTING/$PATCH
448
449 announce
450 update && bootntest_patched && bootntest_pristine && compare_passes
451 write_report
452 fi
453 fi
454done