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