|
1 #!/bin/sh |
|
2 |
|
3 ## Script to build and test the latest python from svn. It basically |
|
4 ## does this: |
|
5 ## svn up ; ./configure ; make ; make test ; make install ; cd Doc ; make |
|
6 ## |
|
7 ## Logs are kept and rsync'ed to the host. If there are test failure(s), |
|
8 ## information about the failure(s) is mailed. |
|
9 ## |
|
10 ## This script is run on the PSF's machine as user neal via crontab. |
|
11 ## |
|
12 ## Yes, this script would probably be easier in python, but then |
|
13 ## there's a bootstrap problem. What if Python doesn't build? |
|
14 ## |
|
15 ## This script should be fairly clean Bourne shell, ie not too many |
|
16 ## bash-isms. We should try to keep it portable to other Unixes. |
|
17 ## Even though it will probably only run on Linux. I'm sure there are |
|
18 ## several GNU-isms currently (date +%s and readlink). |
|
19 ## |
|
20 ## Perhaps this script should be broken up into 2 (or more) components. |
|
21 ## Building doc is orthogonal to the rest of the python build/test. |
|
22 ## |
|
23 |
|
24 ## FIXME: we should detect test hangs (eg, if they take more than 45 minutes) |
|
25 |
|
26 ## FIXME: we should run valgrind |
|
27 ## FIXME: we should run code coverage |
|
28 |
|
29 ## Utilities invoked in this script include: |
|
30 ## basename, date, dirname, expr, grep, readlink, uname |
|
31 ## cksum, make, mutt, rsync, svn |
|
32 |
|
33 ## remember where did we started from |
|
34 DIR=`dirname $0` |
|
35 if [ "$DIR" = "" ]; then |
|
36 DIR="." |
|
37 fi |
|
38 |
|
39 ## make directory absolute |
|
40 DIR=`readlink -f $DIR` |
|
41 FULLPATHNAME="$DIR/`basename $0`" |
|
42 ## we want Misc/.. |
|
43 DIR=`dirname $DIR` |
|
44 |
|
45 ## Configurable options |
|
46 |
|
47 FAILURE_SUBJECT="Python Regression Test Failures" |
|
48 #FAILURE_MAILTO="YOUR_ACCOUNT@gmail.com" |
|
49 FAILURE_MAILTO="python-checkins@python.org" |
|
50 #FAILURE_CC="optional--uncomment and set to desired address" |
|
51 |
|
52 REMOTE_SYSTEM="neal@dinsdale.python.org" |
|
53 REMOTE_DIR="/data/ftp.python.org/pub/www.python.org/doc/current" |
|
54 REMOTE_DIR_DIST="/data/ftp.python.org/pub/python/doc/current" |
|
55 RESULT_FILE="$DIR/build/index.html" |
|
56 INSTALL_DIR="/tmp/python-test-2.6/local" |
|
57 RSYNC_OPTS="-aC -e ssh" |
|
58 |
|
59 # Always run the installed version of Python. |
|
60 PYTHON=$INSTALL_DIR/bin/python |
|
61 |
|
62 # Python options and regression test program that should always be run. |
|
63 REGRTEST_ARGS="-E -tt $INSTALL_DIR/lib/python2.6/test/regrtest.py" |
|
64 |
|
65 REFLOG="build/reflog.txt.out" |
|
66 # These tests are not stable and falsely report leaks sometimes. |
|
67 # The entire leak report will be mailed if any test not in this list leaks. |
|
68 # Note: test_XXX (none currently) really leak, but are disabled |
|
69 # so we don't send spam. Any test which really leaks should only |
|
70 # be listed here if there are also test cases under Lib/test/leakers. |
|
71 LEAKY_TESTS="test_(asynchat|cmd_line|docxmlrpc|dumbdbm|file|ftplib|httpservers|imaplib|popen2|socket|smtplib|sys|telnetlib|threadedtempfile|threading|threadsignals|urllib2_localnet|xmlrpc)" |
|
72 |
|
73 # Skip these tests altogether when looking for leaks. These tests |
|
74 # do not need to be stored above in LEAKY_TESTS too. |
|
75 # test_compiler almost never finishes with the same number of refs |
|
76 # since it depends on other modules, skip it. |
|
77 # test_logging causes hangs, skip it. |
|
78 LEAKY_SKIPS="-x test_compiler test_logging" |
|
79 |
|
80 # Change this flag to "yes" for old releases to only update/build the docs. |
|
81 BUILD_DISABLED="yes" |
|
82 |
|
83 ## utility functions |
|
84 current_time() { |
|
85 date +%s |
|
86 } |
|
87 |
|
88 update_status() { |
|
89 now=`current_time` |
|
90 time=`expr $now - $3` |
|
91 echo "<li><a href=\"$2\">$1</a> <font size=\"-1\">($time seconds)</font></li>" >> $RESULT_FILE |
|
92 } |
|
93 |
|
94 place_summary_first() { |
|
95 testf=$1 |
|
96 sed -n '/^[0-9][0-9]* tests OK\./,$p' < $testf \ |
|
97 | egrep -v '\[[0-9]+ refs\]' > $testf.tmp |
|
98 echo "" >> $testf.tmp |
|
99 cat $testf >> $testf.tmp |
|
100 mv $testf.tmp $testf |
|
101 } |
|
102 |
|
103 count_failures () { |
|
104 testf=$1 |
|
105 n=`grep -ic " failed:" $testf` |
|
106 if [ $n -eq 1 ] ; then |
|
107 n=`grep " failed:" $testf | sed -e 's/ .*//'` |
|
108 fi |
|
109 echo $n |
|
110 } |
|
111 |
|
112 mail_on_failure() { |
|
113 if [ "$NUM_FAILURES" != "0" ]; then |
|
114 dest=$FAILURE_MAILTO |
|
115 # FAILURE_CC is optional. |
|
116 if [ "$FAILURE_CC" != "" ]; then |
|
117 dest="$dest -c $FAILURE_CC" |
|
118 fi |
|
119 if [ "x$3" != "x" ] ; then |
|
120 (echo "More important issues:" |
|
121 echo "----------------------" |
|
122 egrep -v "$3" < $2 |
|
123 echo "" |
|
124 echo "Less important issues:" |
|
125 echo "----------------------" |
|
126 egrep "$3" < $2) |
|
127 else |
|
128 cat $2 |
|
129 fi | mutt -s "$FAILURE_SUBJECT $1 ($NUM_FAILURES)" $dest |
|
130 fi |
|
131 } |
|
132 |
|
133 ## setup |
|
134 cd $DIR |
|
135 mkdir -p build |
|
136 rm -f $RESULT_FILE build/*.out |
|
137 rm -rf $INSTALL_DIR |
|
138 |
|
139 ## create results file |
|
140 TITLE="Automated Python Build Results" |
|
141 echo "<html>" >> $RESULT_FILE |
|
142 echo " <head>" >> $RESULT_FILE |
|
143 echo " <title>$TITLE</title>" >> $RESULT_FILE |
|
144 echo " <meta http-equiv=\"refresh\" content=\"43200\">" >> $RESULT_FILE |
|
145 echo " </head>" >> $RESULT_FILE |
|
146 echo "<body>" >> $RESULT_FILE |
|
147 echo "<h2>Automated Python Build Results</h2>" >> $RESULT_FILE |
|
148 echo "<table>" >> $RESULT_FILE |
|
149 echo " <tr>" >> $RESULT_FILE |
|
150 echo " <td>Built on:</td><td>`date`</td>" >> $RESULT_FILE |
|
151 echo " </tr><tr>" >> $RESULT_FILE |
|
152 echo " <td>Hostname:</td><td>`uname -n`</td>" >> $RESULT_FILE |
|
153 echo " </tr><tr>" >> $RESULT_FILE |
|
154 echo " <td>Platform:</td><td>`uname -srmpo`</td>" >> $RESULT_FILE |
|
155 echo " </tr>" >> $RESULT_FILE |
|
156 echo "</table>" >> $RESULT_FILE |
|
157 echo "<ul>" >> $RESULT_FILE |
|
158 |
|
159 ## update, build, and test |
|
160 ORIG_CHECKSUM=`cksum $FULLPATHNAME` |
|
161 F=svn-update.out |
|
162 start=`current_time` |
|
163 svn update >& build/$F |
|
164 err=$? |
|
165 update_status "Updating" "$F" $start |
|
166 if [ $err = 0 -a "$BUILD_DISABLED" != "yes" ]; then |
|
167 ## FIXME: we should check if this file has changed. |
|
168 ## If it has changed, we should re-run the script to pick up changes. |
|
169 if [ "$ORIG_CHECKSUM" != "$ORIG_CHECKSUM" ]; then |
|
170 exec $FULLPATHNAME $@ |
|
171 fi |
|
172 |
|
173 F=svn-stat.out |
|
174 start=`current_time` |
|
175 svn stat >& build/$F |
|
176 ## ignore some of the diffs |
|
177 NUM_DIFFS=`egrep -vc '^. (@test|db_home|Lib/test/(regrtest\.py|db_home))$' build/$F` |
|
178 update_status "svn stat ($NUM_DIFFS possibly important diffs)" "$F" $start |
|
179 |
|
180 F=configure.out |
|
181 start=`current_time` |
|
182 ./configure --prefix=$INSTALL_DIR --with-pydebug >& build/$F |
|
183 err=$? |
|
184 update_status "Configuring" "$F" $start |
|
185 if [ $err = 0 ]; then |
|
186 F=make.out |
|
187 start=`current_time` |
|
188 make >& build/$F |
|
189 err=$? |
|
190 warnings=`grep warning build/$F | egrep -vc "te?mpnam(_r|)' is dangerous,"` |
|
191 update_status "Building ($warnings warnings)" "$F" $start |
|
192 if [ $err = 0 ]; then |
|
193 ## make install |
|
194 F=make-install.out |
|
195 start=`current_time` |
|
196 make install >& build/$F |
|
197 update_status "Installing" "$F" $start |
|
198 |
|
199 if [ ! -x $PYTHON ]; then |
|
200 ln -s ${PYTHON}2.* $PYTHON |
|
201 fi |
|
202 |
|
203 ## make and run basic tests |
|
204 F=make-test.out |
|
205 start=`current_time` |
|
206 $PYTHON $REGRTEST_ARGS -u urlfetch >& build/$F |
|
207 NUM_FAILURES=`count_failures build/$F` |
|
208 place_summary_first build/$F |
|
209 update_status "Testing basics ($NUM_FAILURES failures)" "$F" $start |
|
210 mail_on_failure "basics" build/$F |
|
211 |
|
212 F=make-test-opt.out |
|
213 start=`current_time` |
|
214 $PYTHON -O $REGRTEST_ARGS -u urlfetch >& build/$F |
|
215 NUM_FAILURES=`count_failures build/$F` |
|
216 place_summary_first build/$F |
|
217 update_status "Testing opt ($NUM_FAILURES failures)" "$F" $start |
|
218 mail_on_failure "opt" build/$F |
|
219 |
|
220 ## run the tests looking for leaks |
|
221 F=make-test-refleak.out |
|
222 start=`current_time` |
|
223 ## ensure that the reflog exists so the grep doesn't fail |
|
224 touch $REFLOG |
|
225 $PYTHON $REGRTEST_ARGS -R 4:3:$REFLOG -u network,urlfetch $LEAKY_SKIPS >& build/$F |
|
226 LEAK_PAT="($LEAKY_TESTS|sum=0)" |
|
227 NUM_FAILURES=`egrep -vc "$LEAK_PAT" $REFLOG` |
|
228 place_summary_first build/$F |
|
229 update_status "Testing refleaks ($NUM_FAILURES failures)" "$F" $start |
|
230 mail_on_failure "refleak" $REFLOG "$LEAK_PAT" |
|
231 |
|
232 ## now try to run all the tests |
|
233 F=make-testall.out |
|
234 start=`current_time` |
|
235 ## skip curses when running from cron since there's no terminal |
|
236 ## skip sound since it's not setup on the PSF box (/dev/dsp) |
|
237 $PYTHON $REGRTEST_ARGS -uall -x test_curses test_linuxaudiodev test_ossaudiodev >& build/$F |
|
238 NUM_FAILURES=`count_failures build/$F` |
|
239 place_summary_first build/$F |
|
240 update_status "Testing all except curses and sound ($NUM_FAILURES failures)" "$F" $start |
|
241 mail_on_failure "all" build/$F |
|
242 fi |
|
243 fi |
|
244 fi |
|
245 |
|
246 |
|
247 ## make doc |
|
248 cd $DIR/Doc |
|
249 F="make-doc.out" |
|
250 start=`current_time` |
|
251 # XXX(nnorwitz): For now, keep the code that checks for a conflicted file until |
|
252 # after the first release of 2.6a1 or 3.0a1. At that point, it will be clear |
|
253 # if there will be a similar problem with the new doc system. |
|
254 |
|
255 # Doc/commontex/boilerplate.tex is expected to always have an outstanding |
|
256 # modification for the date. When a release is cut, a conflict occurs. |
|
257 # This allows us to detect this problem and not try to build the docs |
|
258 # which will definitely fail with a conflict. |
|
259 #CONFLICTED_FILE=commontex/boilerplate.tex |
|
260 #conflict_count=`grep -c "<<<" $CONFLICTED_FILE` |
|
261 conflict_count=0 |
|
262 if [ $conflict_count != 0 ]; then |
|
263 echo "Conflict detected in $CONFLICTED_FILE. Doc build skipped." > ../build/$F |
|
264 err=1 |
|
265 else |
|
266 make update html >& ../build/$F |
|
267 err=$? |
|
268 fi |
|
269 update_status "Making doc" "$F" $start |
|
270 if [ $err != 0 ]; then |
|
271 NUM_FAILURES=1 |
|
272 mail_on_failure "doc" ../build/$F |
|
273 fi |
|
274 |
|
275 F="make-doc-dist.out" |
|
276 start=`current_time` |
|
277 if [ $conflict_count == 0 ]; then |
|
278 make dist >& ../build/$F |
|
279 err=$? |
|
280 fi |
|
281 update_status "Making downloadable doc" "$F" $start |
|
282 if [ $err != 0 ]; then |
|
283 NUM_FAILURES=1 |
|
284 mail_on_failure "doc dist" ../build/$F |
|
285 fi |
|
286 |
|
287 echo "</ul>" >> $RESULT_FILE |
|
288 echo "</body>" >> $RESULT_FILE |
|
289 echo "</html>" >> $RESULT_FILE |
|
290 |
|
291 ## copy results |
|
292 rsync $RSYNC_OPTS build/html/* $REMOTE_SYSTEM:$REMOTE_DIR |
|
293 rsync $RSYNC_OPTS dist/* $REMOTE_SYSTEM:$REMOTE_DIR_DIST |
|
294 cd ../build |
|
295 rsync $RSYNC_OPTS index.html *.out $REMOTE_SYSTEM:$REMOTE_DIR/results/ |