chiark / gitweb /
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 --------------------------------------------------