123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519 |
- #!/bin/sh
- # Tests a set of patches from a directory.
- # Copyright (C) 2007, 2008, 2011 Free Software Foundation, Inc.
- # Contributed by Sebastian Pop <sebastian.pop@amd.com>
- # This program is free software; you can redistribute it and/or modify
- # it under the terms of the GNU General Public License as published by
- # the Free Software Foundation; either version 3 of the License, or
- # (at your option) any later version.
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
- # You should have received a copy of the GNU General Public License
- # along with this program; if not, write to the Free Software
- # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- cat <<EOF
- WARNING: This script should only be fed with patches from known
- authorized and trusted sources. Don't even think about
- hooking it up to a raw feed from the gcc-patches list or
- you'll regret it.
- EOF
- args=$@
- svnpath=svn://gcc.gnu.org/svn/gcc
- dashj=
- default_standby=1
- standby=$default_standby
- default_watermark=0.60
- watermark=$default_watermark
- savecompilers=false
- nopristinecache=false
- nogpg=false
- stop=false
- usage() {
- cat <<EOF
- patch_tester.sh [-j<N>] [-standby N] [-watermark N] [-savecompilers] [-nogpg]
- [-svnpath URL] [-stop] [-nopristinecache]
- <source_dir> [patches_dir [state_dir [build_dir]]]
- J is the flag passed to make. Default is empty string.
- STANDBY is the number of minutes between checks for new patches in
- PATCHES_DIR. Default is ${default_standby} minutes.
- WATERMARK is the 5 minute average system charge under which a new
- compile can start. Default is ${default_watermark}.
- SAVECOMPILERS copies the compilers in the same directory as the
- test results for the non patched version. Default is not copy.
- NOPRISTINECACHE prevents use of cached test results from any earlier
- test runs on the pristine version of the branch and revision under
- test (the default behaviour). This should be used when testing the
- same revision and patch with multiple sets of configure options, as
- these may affect the set of baseline failures.
- NOGPG can be used to avoid checking the GPG signature of patches.
- URL is the location of the GCC SVN repository. The default is
- ${svnpath}.
- STOP exits when PATCHES_DIR is empty.
- SOURCE_DIR is the directory containing GCC's toplevel configure.
- PATCHES_DIR is the directory containing the patches to be tested.
- Default is SOURCE_DIR/patches.
- STATE_DIR is where the tester maintains its internal state.
- Default is SOURCE_DIR/state.
- BUILD_DIR is the build tree, a temporary directory that this
- script will delete and recreate. Default is SOURCE_DIR/obj.
- EOF
- exit 1
- }
- makedir () {
- DIRNAME=$1
- mkdir -p $DIRNAME
- if [ $? -ne 0 ]; then
- echo "ERROR: could not make directory $DIRNAME"
- exit 1
- fi
- }
- while [ $# -ne 0 ]; do
- case $1 in
- -j*)
- dashj=$1; shift
- ;;
- -standby)
- [[ $# > 2 ]] || usage
- standby=$2; shift; shift
- ;;
- -watermark)
- [[ $# > 2 ]] || usage
- watermark=$2; shift; shift
- ;;
- -savecompilers)
- savecompilers=true; shift
- ;;
- -nopristinecache)
- nopristinecache=true; shift
- ;;
- -nogpg)
- nogpg=true; shift
- ;;
- -stop)
- stop=true; shift
- ;;
- -svnpath)
- svnpath=$2; shift; shift
- ;;
- -*)
- echo "Invalid option: $1"
- usage
- ;;
- *)
- break
- ;;
- esac
- done
- test $# -eq 0 && usage
- SOURCE=$1
- PATCHES=
- STATE=
- BUILD=
- if [[ $# < 2 ]]; then
- PATCHES=$SOURCE/patches
- else
- PATCHES=$2
- fi
- if [[ $# < 3 ]]; then
- STATE=$SOURCE/state
- else
- STATE=$3
- fi
- if [[ $# < 4 ]]; then
- BUILD=$SOURCE/obj
- else
- BUILD=$4
- fi
- [ -d $PATCHES ] || makedir $PATCHES
- [ -d $STATE ] || makedir $STATE
- [ -d $STATE/patched ] || makedir $STATE/patched
- [ -d $SOURCE ] || makedir $SOURCE
- [ -f $SOURCE/config.guess ] || {
- cd $SOURCE
- svn -q co $svnpath/trunk .
- if [ $? -ne 0 ]; then
- echo "ERROR: initial svn checkout failed"
- exit 1
- fi
- }
- # This can contain required local settings:
- # default_config configure options, always passed
- # default_make make bootstrap options, always passed
- # default_check make check options, always passed
- [ -f $STATE/defaults ] && . $STATE/defaults
- VERSION=`svn info $SOURCE | grep "^Revision:" | sed -e "s/^Revision://g" -e "s/ //g"`
- exec >> $STATE/tester.log 2>&1 || exit 1
- set -x
- TESTING=$STATE/testing
- REPORT=$TESTING/report
- PRISTINE=$TESTING/pristine
- PATCHED=$TESTING/patched
- PATCH=
- TARGET=`$SOURCE/config.guess || exit 1`
- TESTLOGS="gcc/testsuite/gcc/gcc.sum
- gcc/testsuite/gfortran/gfortran.sum
- gcc/testsuite/g++/g++.sum
- gcc/testsuite/objc/objc.sum
- $TARGET/libstdc++-v3/testsuite/libstdc++.sum
- $TARGET/libffi/testsuite/libffi.sum
- $TARGET/libjava/testsuite/libjava.sum
- $TARGET/libgomp/testsuite/libgomp.sum
- $TARGET/libmudflap/testsuite/libmudflap.sum"
- COMPILERS="gcc/cc1
- gcc/cc1obj
- gcc/cc1plus
- gcc/f951
- gcc/jc1
- gcc/gnat1
- gcc/tree1"
- now () {
- echo `TZ=UTC date +"%Y_%m_%d_%H_%M_%S"`
- }
- report () {
- echo "$@" >> $REPORT
- }
- freport () {
- if [ -s $1 ]; then
- report "(cat $1"
- cat $1 >> $REPORT
- report "tac)"
- fi
- }
- cleanup () {
- cd $SOURCE
- svn cleanup && svn revert -R . && svn st | cut -d' ' -f5- | xargs rm -v
- }
- selfexec () {
- exec ${CONFIG_SHELL-/bin/sh} $0 $args
- }
- update () {
- svn_branch=`grep "^branch:" $PATCH | sed -e "s/^branch://g" -e "s/ //g"`
- if [ x$svn_branch = x ]; then
- svn_branch=trunk
- fi
- svn_revision=`grep "^revision:" $PATCH | sed -e "s/^revision://g" -e "s/ //g"`
- if [ x$svn_revision = x ]; then
- svn_revision=HEAD
- fi
- cleanup
- cd $SOURCE
- case $svn_branch in
- trunk)
- if ! svn switch -r $svn_revision $svnpath/trunk &> $TESTING/svn ; then
- report "failed to update svn sources with"
- report "svn switch -r $svn_revision $svnpath/trunk"
- freport $TESTING/svn
- return 1
- fi
- ;;
- ${svnpath}*)
- if ! svn switch -r $svn_revision $svn_branch &> $TESTING/svn ; then
- report "failed to update svn sources with"
- report "svn switch -r $svn_revision $svn_branch"
- freport $TESTING/svn
- return 1
- fi
- ;;
- *)
- if ! svn switch -r $svn_revision $svnpath/branches/$svn_branch &> $TESTING/svn ; then
- report "failed to update svn sources with"
- report "svn switch -r $svn_revision $svnpath/branches/$svn_branch"
- freport $TESTING/svn
- return 1
- fi
- ;;
- esac
- contrib/gcc_update --touch
- current_version=`svn info $SOURCE | grep "^Revision:" | sed -e "s/^Revision://g" -e "s/ //g"`
- if [[ $VERSION < $current_version ]]; then
- if [ -f $SOURCE/contrib/patch_tester.sh ]; then
- selfexec
- fi
- fi
- return 0
- }
- apply_patch () {
- if [ $nogpg = false ]; then
- if ! gpg --batch --verify $PATCH &> $TESTING/gpgverify ; then
- report "your patch failed to verify:"
- freport $TESTING/gpgverify
- return 1
- fi
- fi
- cd $SOURCE
- if ! patch -p0 < $PATCH &> $TESTING/patching ; then
- report "your patch failed to apply:"
- report "(check that the patch was created at the top level)"
- freport $TESTING/patching
- return 1
- fi
- # Just assume indexes for now -- not really great, but svn always
- # makes them.
- grep "^Index: " $PATCH | sed -e 's/Index: //' | while read file; do
- # If the patch resulted in an empty file, delete it.
- # This is how svn reports deletions.
- if [ ! -s $file ]; then
- rm -f $file
- report "Deleting empty file $file"
- fi
- done
- }
- save_compilers () {
- for COMPILER in $COMPILERS ; do
- if [ -f $BUILD/$COMPILER ]; then
- cp $BUILD/$COMPILER $PRISTINE
- fi
- done
- }
- bootntest () {
- rm -rf $BUILD
- mkdir $BUILD
- cd $BUILD
- CONFIG_OPTIONS=`grep "^configure:" $PATCH | sed -e "s/^configure://g"`
- CONFIG_OPTIONS="$default_config $CONFIG_OPTIONS"
- if ! eval $SOURCE/configure $CONFIG_OPTIONS &> $1/configure ; then
- report "configure with `basename $1` version failed with:"
- freport $1/configure
- return 1
- fi
- MAKE_ARGS=`grep "^make:" $PATCH | sed -e "s/^make://g"`
- MAKE_ARGS="$default_make $MAKE_ARGS"
- if ! eval make $dashj $MAKE_ARGS &> $1/bootstrap ; then
- report "bootstrap with `basename $1` version failed with last lines:"
- tail -30 $1/bootstrap > $1/last_bootstrap
- freport $1/last_bootstrap
- report "grep --context=20 Error bootstrap:"
- grep --context=20 Error $1/bootstrap > $1/bootstrap_error
- freport $1/bootstrap_error
- return 1
- fi
- CHECK_OPTIONS=`grep "^check:" $PATCH | sed -e "s/^check://g"`
- CHECK_OPTIONS="$default_check $CHECK_OPTIONS"
- eval make $dashj $CHECK_OPTIONS -k check &> $1/check
- SUITESRUN="`grep 'Summary ===' $1/check | cut -d' ' -f 2 | sort`"
- if [ x$SUITESRUN = x ]; then
- report "check with `basename $1` version failed, no testsuites were run"
- return 1
- fi
- for LOG in $TESTLOGS ; do
- if [ -f $BUILD/$LOG ]; then
- mv $BUILD/$LOG $1
- mv `echo "$BUILD/$LOG" | sed -e "s/\.sum/\.log/g"` $1
- fi
- done
- return 0
- }
- bootntest_patched () {
- cleanup
- mkdir -p $PATCHED
- apply_patch && bootntest $PATCHED
- return $?
- }
- # Build the pristine tree with exactly the same options as the patch under test.
- bootntest_pristine () {
- cleanup
- current_branch=`svn info $SOURCE | grep "^URL:" | sed -e "s/URL: //g" -e "s,${svnpath},,g"`
- current_version=`svn info $SOURCE | grep "^Revision:" | sed -e "s/^Revision://g" -e "s/ //g"`
- PRISTINE=$STATE/$current_branch/$current_version
- if [ $nopristinecache = true ]; then
- rm -rf $PRISTINE
- fi
- if [ -d $PRISTINE ]; then
- ln -s $PRISTINE $TESTING/pristine
- return 0
- else
- mkdir -p $PRISTINE
- ln -s $PRISTINE $TESTING/pristine
- bootntest $PRISTINE
- RETVAL=$?
- if [ $RETVAL = 0 -a $savecompilers = true ]; then
- save_compilers
- fi
- return $RETVAL
- fi
- }
- regtest () {
- touch $1/report
- touch $1/passes
- touch $1/failed
- touch $1/regress
- for LOG in $TESTLOGS ; do
- NLOG=`basename $LOG`
- if [ -f $1/$NLOG ]; then
- awk '/^FAIL: / { print "'$NLOG'",$2; }' $1/$NLOG
- fi
- done | sort | uniq > $1/failed
- comm -12 $1/failed $1/passes >> $1/regress
- NUMREGRESS=`wc -l < $1/regress | tr -d ' '`
- if [ $NUMREGRESS -eq 0 ] ; then
- for LOG in $TESTLOGS ; do
- NLOG=`basename $LOG`
- if [ -f $1/$NLOG ] ; then
- awk '/^PASS: / { print "'$NLOG'",$2; }' $1/$NLOG
- fi
- done | sort | uniq | comm -23 - $1/failed > $1/passes
- echo "there are no regressions with your patch." >> $1/report
- else
- echo "with your patch there are $NUMREGRESS regressions." >> $1/report
- echo "list of regressions with your patch:" >> $1/report
- cat $1/regress >> $1/report
- fi
- }
- contrib_compare_tests () {
- report "comparing logs with contrib/compare_tests:"
- for LOG in $TESTLOGS ; do
- NLOG=`basename $LOG`
- if [ -f $PRISTINE/$NLOG -a -f $PATCHED/$NLOG ]; then
- $SOURCE/contrib/compare_tests $PRISTINE/$NLOG $PATCHED/$NLOG > $TESTING/compare_$NLOG
- freport $TESTING/compare_$NLOG
- fi
- done
- }
- compare_passes () {
- regtest $PRISTINE
- cp $PRISTINE/passes $PATCHED
- regtest $PATCHED
- freport $PATCHED/report
- report "FAILs with patched version:"
- freport $PATCHED/failed
- report "FAILs with pristine version:"
- freport $PRISTINE/failed
- # contrib_compare_tests
- }
- write_report () {
- backup_patched=$STATE/patched/`now`
- report "The files used for the validation of your patch are stored in $backup_patched on the tester machine."
- EMAIL=`grep "^email:" $PATCH | sed -e "s/^email://g" -e "s/ //g"`
- if [ x$EMAIL != x ]; then
- mutt -s "[regtest] Results for `basename $PATCH` on $TARGET" -i $REPORT -a $PATCH $EMAIL
- fi
- mv $TESTING $backup_patched
- }
- announce () {
- EMAIL=`grep "^email:" $PATCH | sed -e "s/^email://g" -e "s/ //g"`
- if [ x$EMAIL != x ]; then
- START_REPORT=$TESTING/start_report
- echo "Hi, " >> $START_REPORT
- echo "I'm the automatic tester running on $TARGET." >> $START_REPORT
- echo "I just started to look at your patch `basename $PATCH`." >> $START_REPORT
- echo "Bye, your automatic tester." >> $START_REPORT
- mutt -s "[regtest] Starting bootstrap for `basename $PATCH` on $TARGET" -i $START_REPORT $EMAIL
- fi
- }
- # After selfexec, $TESTING is already set up.
- if [ -d $TESTING ]; then
- # The only file in $TESTING is the patch.
- PATCH=`ls -rt -1 $TESTING | head -1`
- PATCH=$TESTING/$PATCH
- if [ -f $PATCH ]; then
- bootntest_patched && bootntest_pristine && compare_passes
- write_report
- fi
- fi
- firstpatch=true
- while true; do
- PATCH=`ls -rt -1 $PATCHES | head -1`
- if [ x$PATCH = x ]; then
- if [ $stop = true ]; then
- if [ $firstpatch = true ]; then
- echo "No patches ready to test, quitting."
- exit 1
- else
- echo "No more patches to test."
- exit 0
- fi
- fi
- sleep ${standby}m
- else
- firstpatch=false
- sysload=`uptime | cut -d, -f 5`
- if [[ $sysload > $watermark ]]; then
- # Wait a bit when system load is too high.
- sleep ${standby}m
- else
- mkdir -p $TESTING
- mv $PATCHES/$PATCH $TESTING/
- PATCH=$TESTING/$PATCH
- announce
- update && bootntest_patched && bootntest_pristine && compare_passes
- write_report
- fi
- fi
- done
|