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>
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.
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.
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
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
34 standby=$default_standby
35 default_watermark=0.60
36 watermark=$default_watermark
42 patch_tester.sh [-j<N>] [-standby N] [-watermark N] [-savecompilers] [-nogpg]
43 <source_dir> [patches_dir [state_dir [build_dir]]]
45 J is the flag passed to make. Default is empty string.
47 STANDBY is the number of minutes between checks for new patches in
48 PATCHES_DIR. Default is ${default_standby} minutes.
50 WATERMARK is the 5 minute average system charge under which a new
51 compile can start. Default is ${default_watermark}.
53 SAVECOMPILERS copies the compilers in the same directory as the
54 test results for the non patched version. Default is not copy.
56 NOGPG can be used to avoid checking the GPG signature of patches.
58 SOURCE_DIR is the directory containing GCC's toplevel configure.
60 PATCHES_DIR is the directory containing the patches to be tested.
61 Default is SOURCE_DIR/patches.
63 STATE_DIR is where the tester maintains its internal state.
64 Default is SOURCE_DIR/state.
66 BUILD_DIR is the build tree, a temporary directory that this
67 script will delete and recreate. Default is SOURCE_DIR/obj.
73 while [ $# -ne 0 ]; do
80 standby=$2; shift; shift
84 watermark=$2; shift; shift
87 savecompilers=true; shift
93 echo "Invalid option: $1"
102 test $# -eq 0 && usage
109 if [[ $# < 2 ]]; then
110 PATCHES=$SOURCE/patches
114 if [[ $# < 3 ]]; then
119 if [[ $# < 4 ]]; then
125 [ -d $PATCHES ] || mkdir -p $PATCHES
126 [ -d $STATE ] || mkdir -p $STATE
127 [ -d $STATE/patched ] || mkdir -p $STATE/patched
128 [ -d $SOURCE ] || mkdir -p $SOURCE
129 [ -f $SOURCE/config.guess ] || {
131 svn -q co svn://gcc.gnu.org/svn/gcc/trunk .
134 # This can contain required local settings:
135 # default_config configure options, always passed
136 # default_make make bootstrap options, always passed
137 # default_check make check options, always passed
138 [ -f $STATE/defaults ] && . $STATE/defaults
140 VERSION=`svn info $SOURCE | grep "^Revision:" | sed -e "s/^Revision://g" -e "s/ //g"`
142 exec >> $STATE/tester.log 2>&1 || exit 1
145 TESTING=$STATE/testing
146 REPORT=$TESTING/report
147 PRISTINE=$TESTING/pristine
148 PATCHED=$TESTING/patched
150 TARGET=`$SOURCE/config.guess || exit 1`
151 TESTLOGS="gcc/testsuite/gcc/gcc.sum
152 gcc/testsuite/gfortran/gfortran.sum
153 gcc/testsuite/g++/g++.sum
154 gcc/testsuite/objc/objc.sum
155 $TARGET/libstdc++-v3/testsuite/libstdc++.sum
156 $TARGET/libffi/testsuite/libffi.sum
157 $TARGET/libjava/testsuite/libjava.sum
158 $TARGET/libgomp/testsuite/libgomp.sum
159 $TARGET/libmudflap/testsuite/libmudflap.sum"
169 echo `TZ=UTC date +"%Y_%m_%d_%H_%M_%S"`
173 echo "Checker: (`now`): $@" >> $REPORT
186 svn cleanup && svn revert -R . && svn st | cut -d' ' -f5- | xargs rm -v
190 exec ${CONFIG_SHELL-/bin/sh} $0 $args
194 svn_branch=`grep "^branch:" $PATCH | sed -e "s/^branch://g" -e "s/ //g"`
195 if [ x$svn_branch = x ]; then
199 svn_revision=`grep "^revision:" $PATCH | sed -e "s/^revision://g" -e "s/ //g"`
200 if [ x$svn_revision = x ]; then
208 if ! svn switch -r $svn_revision svn://gcc.gnu.org/svn/gcc/trunk &> $TESTING/svn ; then
209 report "failed to update svn sources with"
210 report "svn switch -r $svn_revision svn://gcc.gnu.org/svn/gcc/trunk"
216 svn://gcc.gnu.org/svn/gcc/*)
217 if ! svn switch -r $svn_revision $svn_branch &> $TESTING/svn ; then
218 report "failed to update svn sources with"
219 report "svn switch -r $svn_revision $svn_branch"
226 if ! svn switch -r $svn_revision svn://gcc.gnu.org/svn/gcc/branches/$svn_branch &> $TESTING/svn ; then
227 report "failed to update svn sources with"
228 report "svn switch -r $svn_revision svn://gcc.gnu.org/svn/gcc/branches/$svn_branch"
235 current_version=`svn info $SOURCE | grep "^Revision:" | sed -e "s/^Revision://g" -e "s/ //g"`
236 if [[ $VERSION < $current_version ]]; then
237 if [ -f $SOURCE/contrib/patch_tester.sh ]; then
246 if [ $nogpg = false ]; then
247 if ! gpg --batch --verify $PATCH &> $TESTING/gpgverify ; then
248 report "your patch failed to verify:"
249 freport $TESTING/gpgverify
254 # Detect if the patch was created in toplev GCC.
255 grep "^Index: " $PATCH | grep "gcc/"
258 if ! patch -p0 < $PATCH &> $TESTING/patching ; then
259 report "your patch failed to apply:"
260 freport $TESTING/patching
265 if ! patch -p0 < $PATCH &> $TESTING/patching ; then
266 report "your patch failed to apply:"
267 freport $TESTING/patching
272 # Just assume indexes for now -- not really great, but svn always
274 grep "^Index: " $PATCH | sed -e 's/Index: //' | while read file; do
275 # If the patch resulted in an empty file, delete it.
276 # This is how svn reports deletions.
277 if [ ! -s $file ]; then
279 report "Deleting empty file $file"
285 for COMPILER in $COMPILERS ; do
286 if [ -f $BUILD/$COMPILER ]; then
287 cp $BUILD/$COMPILER $PRISTINE
297 CONFIG_OPTIONS=`grep "^configure:" $PATCH | sed -e "s/^configure://g"`
298 CONFIG_OPTIONS="$default_config $CONFIG_OPTIONS"
299 if ! $SOURCE/configure $CONFIG_OPTIONS &> $1/configure ; then
300 report "configure failed with:"
305 MAKE_ARGS=`grep "^make:" $PATCH | sed -e "s/^make://g"`
306 MAKE_ARGS="$default_make $MAKE_ARGS"
307 if ! make $dashj $MAKE_ARGS bootstrap &> $1/bootstrap ; then
308 report "bootstrap failed with last lines:"
309 tail -30 $1/bootstrap > $1/last_bootstrap
310 freport $1/last_bootstrap
311 report "grep --context=20 Error bootstrap:"
312 grep --context=20 Error $1/bootstrap > $1/bootstrap_error
313 freport $1/bootstrap_error
317 CHECK_OPTIONS=`grep "^check:" $PATCH | sed -e "s/^check://g"`
318 CHECK_OPTIONS="$default_check $CHECK_OPTIONS"
319 make $dashj $CHECK_OPTIONS -k check &> $1/check
321 for LOG in $TESTLOGS ; do
322 if [ -f $BUILD/$LOG ]; then
324 mv `echo "$BUILD/$LOG" | sed -e "s/\.sum/\.log/g"` $1
331 bootntest_patched () {
334 apply_patch && bootntest $PATCHED
338 # Build the pristine tree with exactly the same options as the patch under test.
339 bootntest_pristine () {
341 current_branch=`svn info $SOURCE | grep "^URL:" | sed -e "s/URL: //g" -e "s/svn:\/\/gcc.gnu.org\/svn\/gcc\///g"`
342 current_version=`svn info $SOURCE | grep "^Revision:" | sed -e "s/^Revision://g" -e "s/ //g"`
343 PRISTINE=$STATE/$current_branch/$current_version
345 if [ -d $PRISTINE ]; then
346 ln -s $PRISTINE $TESTING/pristine
350 ln -s $PRISTINE $TESTING/pristine
353 if [ $RETVAL = 0 -a $savecompilers = true ]; then
366 for LOG in $TESTLOGS ; do
368 if [ -f $1/$NLOG ]; then
369 awk '/^FAIL: / { print "'$NLOG'",$2; }' $1/$NLOG
371 done | sort | uniq > $1/failed
373 comm -12 $1/failed $1/passes >> $1/regress
374 NUMREGRESS=`wc -l < $1/regress | tr -d ' '`
376 if [ $NUMREGRESS -eq 0 ] ; then
377 for LOG in $TESTLOGS ; do
379 if [ -f $1/$NLOG ] ; then
380 awk '/^PASS: / { print "'$NLOG'",$2; }' $1/$NLOG
382 done | sort | uniq | comm -23 - $1/failed > $1/passes
383 echo "there are no regressions with your patch." >> $1/report
385 echo "with your patch there are $NUMREGRESS regressions." >> $1/report
386 echo "list of regressions with your patch:" >> $1/report
387 cat $1/regress >> $1/report
391 contrib_compare_tests () {
392 report "comparing logs with contrib/compare_tests:"
393 for LOG in $TESTLOGS ; do
395 if [ -f $PRISTINE/$NLOG -a -f $PATCHED/$NLOG ]; then
396 $SOURCE/contrib/compare_tests $PRISTINE/$NLOG $PATCHED/$NLOG > $TESTING/compare_$NLOG
397 freport $TESTING/compare_$NLOG
404 cp $PRISTINE/passes $PATCHED
406 freport $PATCHED/report
407 report "FAILs with patched version:"
408 freport $PATCHED/failed
409 report "FAILs with pristine version:"
410 freport $PRISTINE/failed
412 # contrib_compare_tests
416 backup_patched=$STATE/patched/`now`
417 report "The files used for the validation of your patch are stored in $backup_patched on the tester machine."
419 EMAIL=`grep "^email:" $PATCH | sed -e "s/^email://g" -e "s/ //g"`
420 if [ x$EMAIL != x ]; then
421 mutt -s "[regtest] Results for `basename $PATCH` on $TARGET" -i $REPORT -a $PATCH $EMAIL
424 mv $TESTING $backup_patched
428 EMAIL=`grep "^email:" $PATCH | sed -e "s/^email://g" -e "s/ //g"`
429 if [ x$EMAIL != x ]; then
431 START_REPORT=$TESTING/start_report
432 echo "Hi, " >> $START_REPORT
433 echo "I'm the automatic tester running on $TARGET." >> $START_REPORT
434 echo "I just started to look at your patch `basename $PATCH`." >> $START_REPORT
435 echo "Bye, your automatic tester." >> $START_REPORT
436 mutt -s "[regtest] Starting bootstrap for `basename $PATCH` on $TARGET" -i $START_REPORT $EMAIL
440 # After selfexec, $TESTING is already set up.
441 if [ -d $TESTING ]; then
442 # The only file in $TESTING is the patch.
443 PATCH=`ls -rt -1 $TESTING | head -1`
444 PATCH=$TESTING/$PATCH
445 if [ -f $PATCH ]; then
446 bootntest_patched && bootntest_pristine && compare_passes
452 PATCH=`ls -rt -1 $PATCHES | head -1`
453 if [ x$PATCH = x ]; then
456 sysload=`uptime | cut -d, -f 5`
457 if [[ $sysload > $watermark ]]; then
458 # Wait a bit when system load is too high.
462 mv $PATCHES/$PATCH $TESTING/
463 PATCH=$TESTING/$PATCH
466 update && bootntest_patched && bootntest_pristine && compare_passes