]> git.ipfire.org Git - thirdparty/gcc.git/blame - contrib/patch_tester.sh
tuple: 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
0f55312a 181 svn cleanup && svn revert -R . && svn st | cut -d' ' -f5- | xargs rm -v
0f55312a
SP
182}
183
184selfexec () {
185 exec ${CONFIG_SHELL-/bin/sh} $SOURCE/contrib/patch_tester.sh $args
186}
187
188update () {
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
240apply_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
268save_compilers () {
269 for COMPILER in $COMPILERS ; do
270 if [ -f $BUILD/$COMPILER ]; then
271 cp $BUILD/$COMPILER $PRISTINE
272 fi
273 done
274}
275
276bootntest () {
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
311bootntest_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.
319bootntest_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
340regtest () {
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
371contrib_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
382compare_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
395write_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
407announce () {
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.
421if [ -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
429fi
430
431while 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
450done