chiark / gitweb /
bin/mdw-build: Delete spurious space.
[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=!guess}
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 ## Initialize qualifiers.
123 qual= qualsep=.
124
125 ## Parse simple options.
126 verbose=no
127 while getopts "hv" opt; do
128   case "$opt" in
129     h) usage; exit 0 ;;
130     v) verbose=yes ;;
131     *) exit 1 ;;
132   esac
133 done
134 shift $((OPTIND - 1))
135
136 ## Parse the build options.
137 maybe_set () {
138   var=$1 want=$2
139   eval "p=\${$want+t}\${$want-nil}"
140   case $p in
141     t) eval $var=yes ;;
142     nil) echo >&2 "$prog: $want not set"; exit 1 ;;
143   esac
144 }
145 for opt; do
146   case "$opt" in
147     checkout)   checkout=yes checkoutrev=HEAD ;;
148     checkout=*) checkout=yes checkoutrev=${opt#*=} ;;
149     release)    build=release ;;
150     norelease)  build=test ;;
151     setup)      setup=yes setupcmd=!guess ;;
152     setup=*)    setup=yes setupcmd=${opt#*=} ;;
153     upload)     maybe_set upload uploadpath ;;
154     upload=*)   upload=yes uploadpath=${opt#*=} ;;
155     sign)       maybe_set sign signkey ;;
156     sign=*)     sign=yes signkey=${opt#*=} ;;
157     sbuild)     maybe_set sbuild sbuildsrv ;;
158     sbuild=*)   sbuild=yes sbuildsrv=${opt#*=} ;;
159     dput)       maybe_set dput dputtarget ;;
160     dput=*)     dput=yes dputtarget=${opt#*=} ;;
161     make=*)     make=${opt#*=} ;;
162
163     distcheck | debian | clean | vpath | native | test)
164       eval "$opt=yes"
165       ;;
166     nocheckout | nosetup | nodistcheck | nodebian | \
167       noupload | nodput | noclean | novpath | nonative | notest | \
168       nosbuild | nosign)
169       eval "${opt#no}=no"
170       ;;
171     *)
172       usage >&2
173       exit 1
174       ;;
175   esac
176 done
177
178 ## Parse DEB_BUILD_OPTIONS.
179 jobs=1
180 set -- $DEB_BUILD_OPTIONS
181 for opt; do
182   case "$opt" in
183     parallel=*) jobs=${opt#*=} ;;
184     nocheck) test=no ;;
185   esac
186 done
187
188 makeopts=""
189 case $jobs in 1) ;; *) makeopts="$makeopts -j$jobs" ;; esac
190
191 ###--------------------------------------------------------------------------
192 ### Utility functions.
193
194 ## File descriptor assignments:
195 ##
196 ##    0 -- original stdin (never touched)
197 ## 1, 2 -- stdout, stderr, redirected to 3 while running comamnds
198 ##  log -- logfile and original stderr (verbose), or logfile only (quiet);
199 ##              captures command output
200 ## diag -- logfile; primary diagnostic output
201 ## term -- orginal stderr; secondary diagnostic output (with colours)
202
203 notify () {
204   colour=$1 message=$2
205   echo $message >&$diag
206   echo "$(tput bold; tput setaf $colour)$message$(tput sgr0; tput op)" >&$term
207 }
208
209 fail () {
210   notify 1 "!!! $*"
211   exit 1
212 }
213
214 warn () {
215   case $build in
216     release) fail "$*" ;;
217     *) notify 5 "??? $*" ;;
218   esac
219 }
220
221 info () {
222   notify 6 "--- $*"
223 }
224
225 assign () {
226   info "$1 = $2"
227   eval "$1=$2"
228 }
229
230 runx () {
231   notify 2 "+++ $*"
232   nice "$@" 2>&$log {log}>&- {diag}>&- {term}>&- || fail "$1: exit $?"
233 }
234
235 run () { runx "$@" >&$log; }
236
237 yesno () {
238   echo -n "(test $*)" >&$diag
239   if "$@" >&$diag 2>&$diag {log}>&- {diag}>&- {term}>&-; then
240     echo "(yes)" >&$diag
241     echo yes
242   else
243     echo "(no)" >&$diag
244     echo no
245   fi
246 }
247
248 ###--------------------------------------------------------------------------
249 ### Do the building.
250
251 ## Some preflight checks.
252 case $test,$build in
253   no,release) fail "refusing to make release build without testing" ;;
254 esac
255 case $test,$distcheck in
256   no,yes)
257     info "forcing \`distcheck' off because tsting disabled"
258     distcheck=no
259     ;;
260 esac
261
262 ## Find the top-level package directory.
263 while [ ! -f configure.ac -a ! -f configure.in -a \
264         ! -f .links -a ! -d .git ]; do
265   case "$(pwd)" in
266     /)
267       fail "couldn't find top-level directory"
268       ;;
269   esac
270   cd ..
271 done
272 toppath=$(pwd)
273
274 ## Add a qualifier for Scratchbox builds.
275 case ${SBOX_SESSION_DIR+t},${DEB_BUILD_ARCH+t} in
276   t,t) qual=$qual$qualsep$DEB_BUILD_ARCH; qualsep=- ;;
277   t,*) fail "unknown build arch" ;;
278 esac
279
280 ## Construct the output directory.
281 releasepath=$toppath/dist-$build$qual
282 chmod -R +w $releasepath 2>/dev/null || :
283 rm -rf $releasepath 2>/dev/null || :
284 mkdir $releasepath
285 logfile=$releasepath/mdw-build.log
286 exec {term}>&2
287 exec {diag}>>$logfile || fail "Failed to create log."
288 case $verbose in
289   no) exec {log}>&$diag ;;
290   yes) exec {log}> >(tee -a $logfile >&$term {term}>&- {diag}>&-) ;;
291 esac
292
293 ## Repeat the earlier assignments for tbe benefit of the logfile.
294 assign toppath $toppath
295 assign releasepath $releasepath
296 assign logfile $logfile
297
298 ## Do we have a Git repository?
299 case "$checkout,$setup,$(yesno [ -d $toppath/.git ])" in
300   yes,no,*)
301     fail "Inconsistent options: can't check out without setup."
302     ;;
303   yes,yes,no)
304     info "No Git repository found."
305     checkout=no gitver=none
306     ;;
307   yes,yes,yes)
308     cd $toppath
309     [ "$(git ls-files -m)" = "" ] ||
310       warn "working tree has uncommitted changes"
311     ;;
312 esac
313
314 ## Is there Debian build equipment?
315 case "$debian,$(yesno [ -d $toppath/debian ])" in
316   yes,no)
317     info "No debian directory found."
318     debian=no debver=none
319     ;;
320   no,*)
321     debver=none
322     ;;
323   yes,yes)
324     debver=$(dpkg-parsechangelog | sed -n 's/^Version: //p')
325     debsrc=$(dpkg-parsechangelog | sed -n 's/^Source: //p')
326     debname=$(git config user.name) debemail=$(git config user.email)
327     ;;
328 esac
329
330 ## Maybe check out a copy of the source.
331 case "$checkout" in
332   yes)
333     cd $releasepath
334     run git clone -sn $toppath/.git _source
335     assign srcpath $releasepath/_source
336     cd $srcpath
337     run git update-ref refs/heads/mdw-build $checkoutrev ""
338     run git symbolic-ref HEAD refs/heads/mdw-build
339     run git read-tree --reset refs/heads/mdw-build
340     run git checkout-index -afu
341     assign gitversion "$(git describe --abbrev=4)"
342     ;;
343   no)
344     assign srcpath $toppath
345     ;;
346 esac
347
348 ## Check the version number.
349 hack_dch_p=no
350 case "$gitversion,$debver" in
351   none,* | *,none)
352     ;;
353   *)
354     dvref=$(echo "$debver" | tr '~' '_')
355     if [ "$gitversion" = "$dvref" ]; then
356       assign debversion "$debver"
357     else
358       warn "Git version $gitversion doesn't match Debian version $debver"
359       hack_dch=yes
360       dver=$(echo $gitversion | sed 's/-/+/; s/-/./g')
361       case $debver in *~) dver=$debver$dver ;; esac
362       assign debversion "$dver"
363       now=$(date -R)
364     fi
365     ;;
366 esac
367
368 ## Maybe refresh the build machinery.
369 case "$setup" in
370   yes)
371     case $setupcmd in
372       !guess)
373         if [ -f .links ]; then setupcmd=mdw-setup
374         elif [ -x autogen.sh ]; then setupcmd=./autogen.sh
375         elif [ -x setup ]; then setupcmd=./setup
376         elif [ -f configure.ac ]; then setupcmd="autoreconf -is"
377         else setupcmd=mdw-setup
378         fi
379         ;;
380     esac
381     run $setupcmd
382     ;;
383 esac
384
385 ## Initialize the build directory.
386 case "$vpath,$(yesno [ -e $srcpath/configure ])" in
387   yes,yes)
388     assign buildpath $releasepath/_build
389     mkdir $buildpath
390     cd $buildpath
391     run $srcpath/configure
392     ;;
393   no,yes)
394     info "VPATH build disabled"
395     assign buildpath $srcpath
396     distcheck=no
397     cd $srcpath
398     run ./configure
399     ;;
400   *,no)
401     info "no configure script"
402     assign buildpath $srcpath
403     cd $srcpath
404     ;;
405 esac
406
407 ## Discover the release name.
408 cat >find-distdir.mk <<'EOF'
409 include Makefile
410 print-distdir:
411         @bash -c 'echo >&$(fd) $(distdir)'
412 EOF
413 assign distdir \
414   $({ $make -f find-distdir.mk print-distdir fd=$t >/dev/null 2>&1; } {t}>&1)
415
416 ## Get a tarball distribution.
417 case "$distcheck" in
418   yes)
419     run $make $makeopts distcheck
420     ;;
421   no)
422     run $make $makeopts dist
423     ;;
424 esac
425
426 cd $releasepath
427
428 case $native in
429   yes)
430     if ! tar tf $buildpath/$distdir.tar.gz 2>/dev/null | grep -q RELEASE
431     then
432       fail "missing RELEASE file in distribution"
433     fi
434     ;;
435 esac
436
437 run mv $buildpath/$distdir.tar.gz .
438 case $build,$sign in
439   release,yes)
440     run gpg -u$signkey -ab -o$distdir.tar.gz.gpg $distdir.tar.gz
441     ;;
442 esac
443
444 ## Maybe build the Debian packages.
445 case "$debian" in
446   yes)
447     run tar xvfz $distdir.tar.gz
448     cd $distdir
449     case $hack_dch in
450       yes)
451         cat - debian/changelog >debian/changelog.new <<EOF
452 $debsrc ($debversion) experimental; urgency=low
453
454   * Hacking in process, not intended for release.
455
456  -- $debname <$debemail>  $now
457
458 EOF
459         mv debian/changelog.new debian/changelog
460         ;;
461     esac
462     sbuildargs=$sbuildsrv
463     case $sbuild,$build in
464       yes,release)
465         case $sign in yes) sbuildargs="-k$signkey $sbuildargs" ;; esac
466         ;;
467       yes,*)
468         if [ -d $toppath/dist-$build.pkgs ]; then
469           sbuildargs="-p$toppath/dist-$build.pkgs $sbuildargs"
470         fi
471         ;;
472     esac
473     case "$sbuild,$test, $DEB_BUILD_OPTIONS " in
474       yes,no,*) sbuildargs="-T $sbuildargs" ;;
475       *" nocheck "*) ;;
476       no,no,*)
477         DEB_BUILD_OPTIONS=${DEB_BUILD_OPTIONS+"$DEB_BUILD_OPTIONS nocheck"}
478         ;;
479     esac
480     case $sbuild,$build,$sign in
481       yes,*) run mdw-sbuild $sbuildargs ;;
482       no,release,yes) run dpkg-buildpackage -k$signkey ;;
483       no,*) run dpkg-buildpackage -us -uc ;;
484     esac
485     ;;
486 esac
487
488 ## Maybe upload Debian packages.
489 cd $releasepath
490 case "$upload,$build" in
491   yes,test) info "Test build: not uploading." ;;
492   yes,release) run rsync $distdir.tar.gz $distdir.tar.gz.gpg $uploadpath ;;
493 esac
494 case "$debian,$upload,$dput,$build" in
495   yes,yes,yes,release) run dput -f $dputtarget *.changes ;;
496 esac
497
498 ## Tidy up.
499 case "$clean" in
500   yes)
501     rm -rf $releasepath/$distdir
502     rm -rf $releasepath/_source
503     rm -rf $releasepath/_build
504     ;;
505 esac
506
507 ## Done.
508 info "All OK."
509
510 ###----- That's all, folks --------------------------------------------------