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