chiark / gitweb /
dot/emacs: Bind a key to `magit-toggle-buffer-lock'.
[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 ## 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=!guess ;;
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     case $setupcmd in
370       !guess)
371         if [ -f .links ]; then setupcmd=mdw-setup
372         elif [ -x autogen.sh ]; then setupcmd=./autogen.sh
373         elif [ -x setup ]; then setupcmd=./setup
374         elif [ -f configure.ac ]; then setupcmd="autoreconf -is"
375         else setupcmd=mdw-setup
376         fi
377         ;;
378     esac
379     run $setupcmd
380     ;;
381 esac
382
383 ## Initialize the build directory.
384 case "$vpath,$(yesno [ -e $srcpath/configure ])" in
385   yes,yes)
386     assign buildpath $releasepath/_build
387     mkdir $buildpath
388     cd $buildpath
389     run $srcpath/configure
390     ;;
391   no,yes)
392     info "VPATH build disabled"
393     assign buildpath $srcpath
394     distcheck=no
395     cd $srcpath
396     run ./configure
397     ;;
398   *,no)
399     info "no configure script"
400     assign buildpath $srcpath
401     cd $srcpath
402     ;;
403 esac
404
405 ## Discover the release name.
406 cat >find-distdir.mk <<'EOF'
407 include Makefile
408 print-distdir:
409         @echo >&$(fd) $(distdir)
410 EOF
411 assign distdir \
412   $({ $make -f find-distdir.mk print-distdir fd=$t >/dev/null 2>&1; } {t}>&1)
413
414 ## Get a tarball distribution.
415 case "$distcheck" in
416   yes)
417     run $make $makeopts distcheck
418     ;;
419   no)
420     run $make $makeopts dist
421     ;;
422 esac
423
424 cd $releasepath
425
426 case $native in
427   yes)
428     if ! tar tf $buildpath/$distdir.tar.gz 2>/dev/null | grep -q RELEASE
429     then
430       fail "missing RELEASE file in distribution"
431     fi
432     ;;
433 esac
434
435 run mv $buildpath/$distdir.tar.gz .
436 case $build,$sign in
437   release,yes)
438     run gpg -u$signkey -ab -o$distdir.tar.gz.gpg $distdir.tar.gz
439     ;;
440 esac
441
442 ## Maybe build the Debian packages.
443 case "$debian" in
444   yes)
445     run tar xvfz $distdir.tar.gz
446     cd $distdir
447     case $hack_dch in
448       yes)
449         cat - debian/changelog >debian/changelog.new <<EOF
450 $debsrc ($debversion) experimental; urgency=low
451
452   * Hacking in process, not intended for release.
453
454  -- $debname <$debemail>  $now
455
456 EOF
457         mv debian/changelog.new debian/changelog
458         ;;
459     esac
460     sbuildargs=$sbuildsrv
461     case $sbuild,$build in
462       yes,release)
463         case $sign in yes) sbuildargs="-k$signkey $sbuildargs" ;; esac
464         ;;
465       yes,*)
466         if [ -d $toppath/dist-$build.pkgs ]; then
467           sbuildargs="-p$toppath/dist-$build.pkgs $sbuildargs"
468         fi
469         ;;
470     esac
471     case "$sbuild,$test, $DEB_BUILD_OPTIONS " in
472       yes,no,*) sbuildargs="-T $sbuildargs" ;;
473       *" nocheck "*) ;;
474       no,no,*)
475         DEB_BUILD_OPTIONS=${DEB_BUILD_OPTIONS+"$DEB_BUILD_OPTIONS nocheck"}
476         ;;
477     esac
478     case $sbuild,$build,$sign in
479       yes,*) run mdw-sbuild $sbuildargs ;;
480       no,release,yes) run dpkg-buildpackage -k$signkey ;;
481       no,*) run dpkg-buildpackage -us -uc ;;
482     esac
483     ;;
484 esac
485
486 ## Maybe upload Debian packages.
487 cd $releasepath
488 case "$upload,$build" in
489   yes,test) info "Test build: not uploading." ;;
490   yes,release) run rsync $distdir.tar.gz $distdir.tar.gz.gpg $uploadpath ;;
491 esac
492 case "$debian,$upload,$dput,$build" in
493   yes,yes,yes,release) run dput -f $dputtarget *.changes ;;
494 esac
495
496 ## Tidy up.
497 case "$clean" in
498   yes)
499     rm -rf $releasepath/$distdir
500     rm -rf $releasepath/_source
501     rm -rf $releasepath/_build
502     ;;
503 esac
504
505 ## Done.
506 info "All OK."
507
508 ###----- That's all, folks --------------------------------------------------