diff --git a/contrib/ChangeLog b/contrib/ChangeLog index e2f4ededaa1..197221d67b1 100644 --- a/contrib/ChangeLog +++ b/contrib/ChangeLog @@ -1,3 +1,8 @@ +2007-12-15 Sebastian Pop + + * patch_tester.sh: New. + * prepare_patch.sh: New. + 2007-11-26 Alexandre Oliva * compare-debug: Introduce -p flag to preserve .stripped files. diff --git a/contrib/patch_tester.sh b/contrib/patch_tester.sh new file mode 100755 index 00000000000..9866b3d0d3e --- /dev/null +++ b/contrib/patch_tester.sh @@ -0,0 +1,454 @@ +#!/bin/sh + +# Tests a set of patches from a directory. +# Copyright (C) 2007 Free Software Foundation, Inc. +# Contributed by Sebastian Pop + +# 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 2 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 <] [-standby N] [-watermark N] [-savecompilers] [-nogpg] + [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}. Note that the comparison + is done in lexicographical order, so don't forget the leading 0. + + SAVECOMPILERS copies the compilers in the same directory as the + test results for the non patched version. Default is not copy. + + NOGPG can be used to avoid checking the GPG signature of patches. + + 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 +} + +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 + ;; + -nogpg) + nogpg=true; 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 ] || mkdir -p $PATCHES +[ -d $STATE ] || mkdir -p $STATE +[ -d $STATE/patched ] || mkdir -p $STATE/patched +[ -d $SOURCE ] || mkdir -p $SOURCE +[ -f $SOURCE/config.guess ] || { + cd $SOURCE + svn -q co svn://gcc.gnu.org/svn/gcc/trunk . +} + +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 "Checker: (`now`): $@" >> $REPORT +} + +freport () { + if [ -s $1 ]; then + report "(cat $1" + cat $1 >> $REPORT + report "tac)" + fi +} + +cleanup () { + cd $SOURCE + + # FORNOW: Until this script is not committed to trunk, save and restore it. + mv $SOURCE/contrib/patch_tester.sh $STATE + svn cleanup && svn revert -R . && svn st | cut -d' ' -f5- | xargs rm -v + mv $STATE/patch_tester.sh $SOURCE/contrib/ +} + +selfexec () { + exec ${CONFIG_SHELL-/bin/sh} $SOURCE/contrib/patch_tester.sh $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 svn://gcc.gnu.org/svn/gcc/trunk &> $TESTING/svn ; then + report "failed to update svn sources with" + report "svn switch -r $svn_revision svn://gcc.gnu.org/svn/gcc/trunk" + freport $TESTING/svn + return 1 + fi + ;; + + svn://gcc.gnu.org/svn/gcc/*) + 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 svn://gcc.gnu.org/svn/gcc/branches/$svn_branch &> $TESTING/svn ; then + report "failed to update svn sources with" + report "svn switch -r $svn_revision svn://gcc.gnu.org/svn/gcc/branches/$svn_branch" + freport $TESTING/svn + return 1 + fi + ;; + esac + + 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 + + # Detect if the patch was created in toplev GCC. + grep "^Index: " $PATCH | grep "gcc/" + if [ $? = 0 ]; then + cd $SOURCE + if ! patch -p0 < $PATCH &> $TESTING/patching ; then + report "your patch failed to apply:" + freport $TESTING/patching + return 1 + fi + else + cd $SOURCE/gcc + if ! patch -p0 < $PATCH &> $TESTING/patching ; then + report "your patch failed to apply:" + freport $TESTING/patching + return 1 + fi + fi +} + +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"` + if ! $SOURCE/configure $CONFIG_OPTIONS &> $1/configure ; then + report "configure failed with:" + freport $1/configure + return 1 + fi + + if ! make $dashj `grep "^make:" $PATCH | sed -e "s/^make://g"` bootstrap &> $1/bootstrap ; then + report "bootstrap 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"` + make $dashj $CHECK_OPTIONS -k check &> $1/check + + 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/svn:\/\/gcc.gnu.org\/svn\/gcc\///g"` + current_version=`svn info $SOURCE | grep "^Revision:" | sed -e "s/^Revision://g" -e "s/ //g"` + PRISTINE=$STATE/$current_branch/$current_version + + 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 + +while true; do + PATCH=`ls -rt -1 $PATCHES | head -1` + if [ x$PATCH = x ]; then + sleep ${standby}m + else + 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 diff --git a/contrib/prepare_patch.sh b/contrib/prepare_patch.sh new file mode 100755 index 00000000000..5770e5ade9d --- /dev/null +++ b/contrib/prepare_patch.sh @@ -0,0 +1,96 @@ +#!/bin/sh +#set -x + +# Prepares a patch for the patch tester. +# Copyright (C) 2007 Free Software Foundation, Inc. +# Contributed by Sebastian Pop + +# 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 2 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 + +usage() { + cat < [patches_dir] + + SOURCE_DIR is the directory containing GCC's toplevel configure. + + PATCHES_DIR is the directory where the patch will be copied to. + Default is SOURCE_DIR/patches. + +EOF + exit 1 +} + +test $# -eq 0 && usage + +SOURCE=$1 +PATCHES= + +if [[ "$#" < 2 ]]; then + PATCHES=$SOURCE/patches +else + PATCHES=$2 +fi + +[ -f $SOURCE/config.guess ] || usage +[ -d $PATCHES ] || mkdir -p $PATCHES + +echo "Enter a name for this patch: " +read name +PATCH=$PATCHES/`TZ=UTC date +"%Y_%m_%d_%H_%M_%S"`_$name.diff + +echo "Enter the email where the report should be sent: " +read email +echo "email:$email" >> $PATCH + +branch=`svn info $SOURCE | grep URL: | sed -e "s/^URL: //g"` +echo "Enter svn branch (svn info in $SOURCE reports $branch, default is trunk): " +read svn_branch +if [ x$svn_branch = x ]; then + svn_branch=trunk +fi +echo "branch:$svn_branch" >> $PATCH + +revision=`svn info $SOURCE | grep Revision: | sed -e "s/^Revision: //g"` +echo "Enter svn revision (svn info in $SOURCE reports $revision, default is HEAD): " +read svn_revision +if [ x$svn_revision = x ]; then + svn_revision=HEAD +fi +echo "revision:$svn_revision" >> $PATCH + +echo "Enter configure options: " +read configure_options +echo "configure:$configure_options" >> $PATCH + +echo "Enter make options: " +read make_options +echo "make:$make_options" >> $PATCH + +echo "Enter make check options: " +read check_options +echo "check:$check_options" >> $PATCH + +echo "" >> $PATCH + +svn diff $SOURCE | tee -a $PATCH + +cat <