bin/mdw-build, etc.: Add option to suppress tests during Debian build.
[profile] / bin / mdw-build
1 #! /bin/bash
2 ###
3 ### Build script for Debian packages
4 ###
5 ### (c) 2008 Mark Wooding
6 ###
7
8 ###----- Licensing notice ---------------------------------------------------
9 ###
10 ### This program is free software; you can redistribute it and/or modify
11 ### it under the terms of the GNU General Public License as published by
12 ### the Free Software Foundation; either version 2 of the License, or
13 ### (at your option) any later version.
14 ###
15 ### This program is distributed in the hope that it will be useful,
16 ### but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 ### GNU General Public License for more details.
19 ###
20 ### You should have received a copy of the GNU General Public License
21 ### along with this program; if not, write to the Free Software Foundation,
22 ### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23
24 ###--------------------------------------------------------------------------
25 ### Conventions for build systems.
26 ###
27 ### This script is designed to work with a variety of `make'-based build
28 ### systems, but there are a number of conventions which must be followed if
29 ### this is going to work properly.
30 ###
31 ### * There must be a `configure.ac', `configure.in', or `.links' file, or
32 ### a `.git' directory in the project top-level, so that we can find it.
33 ###
34 ### * The following `make' variables must be assigned in the top-level
35 ### Makefile, after `mdw-build' has constructed it.
36 ###
37 ### distdir The name of the top-level project directory in the
38 ### source distribution, and the base name for
39 ### distribution archives; should be of the form
40 ### `PROJECT-VERSION'.
41 ###
42 ### The following `make' targets must be available in the top-level
43 ### Makefile.
44 ###
45 ### dist Write to $(distdir).tar.gz a source distribution of
46 ### the package.
47 ###
48 ### distcheck As for `dist', but also build and test the project.
49 ###
50 ### * The source distribution constructed by `make dist' must contain a file
51 ### $(distdir)/RELEASE containing the release name. This isn't currently
52 ### tested, but it might be later.
53
54 set -e
55
56 ###--------------------------------------------------------------------------
57 ### Configuration.
58
59 unset checkout checkoutrev
60 unset setup setupcmd
61 unset sign signkey
62 unset sbuild sbuildsrv
63 unset upload uploadpath
64 unset dput dputtarget
65 unset build distcheck debian clean vpath native
66 for i in \
67 "/etc/mdw-build.conf" \
68 "${XDG_CONFIG_HOME-$HOME/.config}/mdw-build.conf" \
69 "./.mdw-build.conf"
70 do
71 if [ -f "$i" ]; then . "$i"; fi
72 done
73 default_depends () {
74 var=$1 want=$2
75 eval "p=\${$var+t} q=\${$want+t}"
76 case $p,$q in t,*) ;; *,t) eval "$var=yes" ;; *) eval "$var=no" ;; esac
77 }
78 : ${checkout=yes} ${checkoutrev=HEAD}
79 : ${build=test}
80 : ${setup=yes} ${setupcmd=mdw-setup}
81 : ${distcheck=yes}
82 : ${debian=yes}
83 : ${clean=yes}
84 : ${vpath=yes}
85 : ${native=yes}
86 : ${make=make}
87 : ${test=yes}
88 default_depends sbuild sbuildsrv
89 default_depends sign signkey
90 default_depends upload uploadpath
91 default_depends dput dputtarget
92 : ${DEB_BUILD_OPTIONS=parallel=4}; export DEB_BUILD_OPTIONS
93
94 ###--------------------------------------------------------------------------
95 ### Parse options.
96
97 prog=${0##*/}
98
99 usage () {
100 cat <<EOF
101 Usage: $prog [-v] BUILDOPT
102
103 Build options:
104
105 [no]checkout[=REV]
106 [no]release
107 [no]setup[=RUNE]
108 [no]distcheck
109 [no]debian
110 [no]upload[=SERVER:PATH]
111 [no]dput[=TARGET]
112 [no]clean
113 [no]vpath
114 [no]sbuild[=SERVER]
115 [no]sign[=KEYID]
116 [no]test
117 [no]native
118 make=MAKE
119 EOF
120 }
121
122 ## Parse simple options.
123 verbose=no
124 while getopts "hv" opt; do
125 case "$opt" in
126 h) usage; exit 0 ;;
127 v) verbose=yes ;;
128 *) exit 1 ;;
129 esac
130 done
131 shift $((OPTIND - 1))
132
133 ## Parse the build options.
134 maybe_set () {
135 var=$1 want=$2
136 eval "p=\${$want+t}\${$want-nil}"
137 case $p in
138 t) eval $var=yes ;;
139 nil) echo >&2 "$prog: $want not set"; exit 1 ;;
140 esac
141 }
142 for opt; do
143 case "$opt" in
144 checkout) checkout=yes checkoutrev=HEAD ;;
145 checkout=*) checkout=yes checkoutrev=${opt#*=} ;;
146 release) build=release ;;
147 norelease) build=test ;;
148 setup) setup=yes setupcmd=mdw-setup ;;
149 setup=*) setup=yes setupcmd=${opt#*=} ;;
150 upload) maybe_set upload uploadpath ;;
151 upload=*) upload=yes uploadpath=${opt#*=} ;;
152 sign) maybe_set sign signkey ;;
153 sign=*) sign=yes signkey=${opt#*=} ;;
154 sbuild) maybe_set sbuild sbuildsrv ;;
155 sbuild=*) sbuild=yes sbuildsrv=${opt#*=} ;;
156 dput) maybe_set dput dputtarget ;;
157 dput=*) dput=yes dputtarget=${opt#*=} ;;
158 make=*) make=${opt#*=} ;;
159
160 distcheck | debian | clean | vpath | native | test)
161 eval "$opt=yes"
162 ;;
163 nocheckout | nosetup | nodistcheck | nodebian | \
164 noupload | nodput | noclean | novpath | nonative | notest | \
165 nosbuild | nosign )
166 eval "${opt#no}=no"
167 ;;
168 *)
169 usage >&2
170 exit 1
171 ;;
172 esac
173 done
174
175 ## Parse DEB_BUILD_OPTIONS.
176 jobs=1
177 set -- $DEB_BUILD_OPTIONS
178 for opt; do
179 case "$opt" in
180 parallel=*) jobs=${opt#*=} ;;
181 nocheck) test=no ;;
182 esac
183 done
184
185 makeopts=""
186 case $jobs in 1) ;; *) makeopts="$makeopts -j$jobs" ;; esac
187
188 ###--------------------------------------------------------------------------
189 ### Utility functions.
190
191 ## File descriptor assignments:
192 ##
193 ## 0 -- original stdin (never touched)
194 ## 1, 2 -- stdout, stderr, redirected to 3 while running comamnds
195 ## log -- logfile and original stderr (verbose), or logfile only (quiet);
196 ## captures command output
197 ## diag -- logfile; primary diagnostic output
198 ## term -- orginal stderr; secondary diagnostic output (with colours)
199
200 notify () {
201 colour=$1 message=$2
202 echo $message >&$diag
203 echo "$(tput bold; tput setaf $colour)$message$(tput sgr0; tput op)" >&$term
204 }
205
206 fail () {
207 notify 1 "!!! $*"
208 exit 1
209 }
210
211 warn () {
212 case $build in
213 release) fail "$*" ;;
214 *) notify 5 "??? $*" ;;
215 esac
216 }
217
218 info () {
219 notify 6 "--- $*"
220 }
221
222 assign () {
223 info "$1 = $2"
224 eval "$1=$2"
225 }
226
227 runx () {
228 notify 2 "+++ $*"
229 nice "$@" 2>&$log {log}>&- {diag}>&- {term}>&- || fail "$1: exit $?"
230 }
231
232 run () { runx "$@" >&$log; }
233
234 yesno () {
235 echo -n "(test $*)" >&$diag
236 if "$@" >&$diag 2>&$diag {log}>&- {diag}>&- {term}>&-; then
237 echo "(yes)" >&$diag
238 echo yes
239 else
240 echo "(no)" >&$diag
241 echo no
242 fi
243 }
244
245 ###--------------------------------------------------------------------------
246 ### Do the building.
247
248 ## Some preflight checks.
249 case $test,$build in
250 no,release) fail "refusing to make release build without testing" ;;
251 esac
252 case $test,$distcheck in
253 no,yes)
254 info "forcing \`distcheck' off because tsting disabled"
255 distcheck=no
256 ;;
257 esac
258
259 ## Find the top-level package directory.
260 while [ ! -f configure.ac -a ! -f configure.in -a \
261 ! -f .links -a ! -d .git ]; do
262 case "$(pwd)" in
263 /)
264 fail "couldn't find top-level directory"
265 ;;
266 esac
267 cd ..
268 done
269 toppath=$(pwd)
270
271 ## Build any necessary qualifiers.
272 qual= sep=.
273 case ${SBOX_SESSION_DIR+t},${DEB_BUILD_ARCH+t} in
274 t,t) qual=$qual$sep$DEB_BUILD_ARCH; sep=- ;;
275 t,*) fail "unknown build arch" ;;
276 esac
277
278 ## Construct the output directory.
279 releasepath=$toppath/dist-$build$qual
280 chmod -R +w $releasepath 2>/dev/null || :
281 rm -rf $releasepath 2>/dev/null || :
282 mkdir $releasepath
283 logfile=$releasepath/mdw-build.log
284 exec {term}>&2
285 exec {diag}>>$logfile || fail "Failed to create log."
286 case $verbose in
287 no) exec {log}>&$diag ;;
288 yes) exec {log}> >(tee -a $logfile >&$term {term}>&- {diag}>&-) ;;
289 esac
290
291 ## Repeat the earlier assignments for tbe benefit of the logfile.
292 assign toppath $toppath
293 assign releasepath $releasepath
294 assign logfile $logfile
295
296 ## Do we have a Git repository?
297 case "$checkout,$setup,$(yesno [ -d $toppath/.git ])" in
298 yes,no,*)
299 fail "Inconsistent options: can't check out without setup."
300 ;;
301 yes,yes,no)
302 info "No Git repository found."
303 checkout=no gitver=none
304 ;;
305 yes,yes,yes)
306 cd $toppath
307 [ "$(git ls-files -m)" = "" ] ||
308 warn "working tree has uncommitted changes"
309 ;;
310 esac
311
312 ## Is there Debian build equipment?
313 case "$debian,$(yesno [ -d $toppath/debian ])" in
314 yes,no)
315 info "No debian directory found."
316 debian=no debver=none
317 ;;
318 no,*)
319 debver=none
320 ;;
321 yes,yes)
322 debver=$(dpkg-parsechangelog | sed -n 's/^Version: //p')
323 debsrc=$(dpkg-parsechangelog | sed -n 's/^Source: //p')
324 debname=$(git config user.name) debemail=$(git config user.email)
325 ;;
326 esac
327
328 ## Maybe check out a copy of the source.
329 case "$checkout" in
330 yes)
331 cd $releasepath
332 run git clone -sn $toppath/.git _source
333 assign srcpath $releasepath/_source
334 cd $srcpath
335 run git update-ref refs/heads/mdw-build $checkoutrev ""
336 run git symbolic-ref HEAD refs/heads/mdw-build
337 run git read-tree --reset refs/heads/mdw-build
338 run git checkout-index -afu
339 assign gitversion "$(git describe --abbrev=4)"
340 ;;
341 no)
342 assign srcpath $toppath
343 ;;
344 esac
345
346 ## Check the version number.
347 hack_dch_p=no
348 case "$gitversion,$debver" in
349 none,* | *,none)
350 ;;
351 *)
352 dvref=$(echo "$debver" | tr '~' '_')
353 if [ "$gitversion" = "$dvref" ]; then
354 assign debversion "$debver"
355 else
356 warn "Git version $gitversion doesn't match Debian version $debver"
357 hack_dch=yes
358 dver=$(echo $gitversion | sed 's/-/+/; s/-/./g')
359 case $debver in *~) dver=$debver$dver ;; esac
360 assign debversion "$dver"
361 now=$(date -R)
362 fi
363 ;;
364 esac
365
366 ## Maybe refresh the build machinery.
367 case "$setup" in
368 yes)
369 run $setupcmd
370 ;;
371 esac
372
373 ## Initialize the build directory.
374 case "$vpath,$(yesno [ -e $srcpath/configure ])" in
375 yes,yes)
376 assign buildpath $releasepath/_build
377 mkdir $buildpath
378 cd $buildpath
379 run $srcpath/configure
380 ;;
381 no,yes)
382 info "VPATH build disabled"
383 assign buildpath $srcpath
384 distcheck=no
385 cd $srcpath
386 run ./configure
387 ;;
388 *,no)
389 info "no configure script"
390 assign buildpath $srcpath
391 cd $srcpath
392 ;;
393 esac
394
395 ## Discover the release name.
396 cat >find-distdir.mk <<'EOF'
397 include Makefile
398 print-distdir:
399 @echo >&$(fd) $(distdir)
400 EOF
401 assign distdir \
402 $({ $make -f find-distdir.mk print-distdir fd=$t >/dev/null 2>&1; } {t}>&1)
403
404 ## Get a tarball distribution.
405 case "$distcheck" in
406 yes)
407 run $make $makeopts distcheck
408 ;;
409 no)
410 run $make $makeopts dist
411 ;;
412 esac
413
414 cd $releasepath
415
416 case $native in
417 yes)
418 if ! tar tf $buildpath/$distdir.tar.gz 2>/dev/null | grep -q RELEASE
419 then
420 fail "missing RELEASE file in distribution"
421 fi
422 ;;
423 esac
424
425 run mv $buildpath/$distdir.tar.gz .
426 case $build,$sign in
427 release,yes)
428 run gpg -u$signkey -ab -o$distdir.tar.gz.gpg $distdir.tar.gz
429 ;;
430 esac
431
432 ## Maybe build the Debian packages.
433 case "$debian" in
434 yes)
435 run tar xvfz $distdir.tar.gz
436 cd $distdir
437 case $hack_dch in
438 yes)
439 cat - debian/changelog >debian/changelog.new <<EOF
440 $debsrc ($debversion) experimental; urgency=low
441
442 * Hacking in process, not intended for release.
443
444 -- $debname <$debemail> $now
445
446 EOF
447 mv debian/changelog.new debian/changelog
448 ;;
449 esac
450 sbuildargs=$sbuildsrv
451 case $sbuild,$build in
452 yes,release)
453 case $sign in yes) sbuildargs="-k$signkey $sbuildargs" ;; esac
454 ;;
455 yes,*)
456 if [ -d $toppath/dist-$build.pkgs ]; then
457 sbuildargs="-p$toppath/dist-$build.pkgs $sbuildargs"
458 fi
459 ;;
460 esac
461 case "$sbuild,$test, $DEB_BUILD_OPTIONS " in
462 yes,no,*) sbuildargs="-T $sbuildargs" ;;
463 *" nocheck "*) ;;
464 no,no,*)
465 DEB_BUILD_OPTIONS=${DEB_BUILD_OPTIONS+"$DEB_BUILD_OPTIONS nocheck"}
466 ;;
467 esac
468 case $sbuild,$build,$sign in
469 yes,*) run mdw-sbuild $sbuildargs ;;
470 no,release,yes) run dpkg-buildpackage -k$signkey ;;
471 no,*) run dpkg-buildpackage -us -uc ;;
472 esac
473 ;;
474 esac
475
476 ## Maybe upload Debian packages.
477 cd $releasepath
478 case "$upload,$build" in
479 yes,test) info "Test build: not uploading." ;;
480 yes,release) run rsync $distdir.tar.gz $distdir.tar.gz.gpg $uploadpath ;;
481 esac
482 case "$debian,$upload,$dput,$build" in
483 yes,yes,yes,release) run dput -f $dputtarget *.changes ;;
484 esac
485
486 ## Tidy up.
487 case "$clean" in
488 yes)
489 rm -rf $releasepath/$distdir
490 rm -rf $releasepath/_source
491 rm -rf $releasepath/_build
492 ;;
493 esac
494
495 ## Done.
496 info "All OK."
497
498 ###----- That's all, folks --------------------------------------------------