chiark / gitweb /
bin/mdw-build: Delay official `assign' for `$toppath' and `$releasepath'.
[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 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 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 ## Repeat the earlier assignments for tbe benefit of the logfile.
279 assign toppath $toppath
280 assign releasepath $releasepath
281
282 ## Do we have a Git repository?
283 case "$checkout,$setup,$(yesno [ -d $toppath/.git ])" in
284   yes,no,*)
285     fail "Inconsistent options: can't check out without setup."
286     ;;
287   yes,yes,no)
288     info "No Git repository found."
289     checkout=no gitver=none
290     ;;
291   yes,yes,yes)
292     cd $toppath
293     [ "$(git ls-files -m)" = "" ] ||
294       warn "working tree has uncommitted changes"
295     ;;
296 esac
297
298 ## Is there Debian build equipment?
299 case "$debian,$(yesno [ -d $toppath/debian ])" in
300   yes,no)
301     info "No debian directory found."
302     debian=no debver=none
303     ;;
304   no,*)
305     debver=none
306     ;;
307   yes,yes)
308     debver=$(dpkg-parsechangelog | sed -n 's/^Version: //p')
309     debsrc=$(dpkg-parsechangelog | sed -n 's/^Source: //p')
310     debname=$(git config user.name) debemail=$(git config user.email)
311     ;;
312 esac
313
314 ## Maybe check out a copy of the source.
315 case "$checkout" in
316   yes)
317     cd $releasepath
318     run git clone -sn $toppath/.git _source
319     assign srcpath $releasepath/_source
320     cd $srcpath
321     run git update-ref refs/heads/mdw-build $checkoutrev ""
322     run git symbolic-ref HEAD refs/heads/mdw-build
323     run git read-tree --reset refs/heads/mdw-build
324     run git checkout-index -afu
325     assign gitversion "$(git describe --abbrev=4)"
326     ;;
327   no)
328     assign srcpath $toppath
329     ;;
330 esac
331
332 ## Check the version number.
333 hack_dch_p=no
334 case "$gitversion,$debver" in
335   none,* | *,none)
336     ;;
337   *)
338     dvref=$(echo "$debver" | tr '~' '_')
339     if [ "$gitversion" = "$dvref" ]; then
340       assign debversion "$debver"
341     else
342       warn "Git version $gitversion doesn't match Debian version $debver"
343       hack_dch=yes
344       dver=$(echo $gitversion | sed 's/-/+/; s/-/./g')
345       case $debver in *~) dver=$debver$dver ;; esac
346       assign debversion "$dver"
347       now=$(date -R)
348     fi
349     ;;
350 esac
351
352 ## Maybe refresh the build machinery.
353 case "$setup" in
354   yes)
355     run $setupcmd
356     ;;
357 esac
358
359 ## Initialize the build directory.
360 case "$vpath,$(yesno [ -e $srcpath/configure ])" in
361   yes,yes)
362     assign buildpath $releasepath/_build
363     mkdir $buildpath
364     cd $buildpath
365     run $srcpath/configure
366     ;;
367   no,yes)
368     info "VPATH build disabled"
369     assign buildpath $srcpath
370     distcheck=no
371     cd $srcpath
372     run ./configure
373     ;;
374   *,no)
375     info "no configure script"
376     assign buildpath $srcpath
377     cd $srcpath
378     ;;
379 esac
380
381 ## Discover the release name.
382 cat >find-distdir.mk <<'EOF'
383 include Makefile
384 print-distdir:
385         @echo >&$(fd) $(distdir)
386 EOF
387 assign distdir \
388   $({ $make -f find-distdir.mk print-distdir fd=$t >/dev/null 2>&1; } {t}>&1)
389
390 ## Get a tarball distribution.
391 case "$distcheck" in
392   yes)
393     run $make $makeopts distcheck
394     ;;
395   no)
396     run $make $makeopts dist
397     ;;
398 esac
399
400 cd $releasepath
401
402 case $native in
403   yes)
404     if ! tar tf $buildpath/$distdir.tar.gz 2>/dev/null | grep -q RELEASE
405     then
406       fail "missing RELEASE file in distribution"
407     fi
408     ;;
409 esac
410
411 run mv $buildpath/$distdir.tar.gz .
412 case $build,$sign in
413   release,yes)
414     run gpg -u$signkey -ab -o$distdir.tar.gz.gpg $distdir.tar.gz
415     ;;
416 esac
417
418 ## Maybe build the Debian packages.
419 case "$debian" in
420   yes)
421     run tar xvfz $distdir.tar.gz
422     cd $distdir
423     case $hack_dch in
424       yes)
425         cat - debian/changelog >debian/changelog.new <<EOF
426 $debsrc ($debversion) experimental; urgency=low
427
428   * Hacking in process, not intended for release.
429
430  -- $debname <$debemail>  $now
431
432 EOF
433         mv debian/changelog.new debian/changelog
434         ;;
435     esac
436     sbuildargs=$sbuildsrv
437     case $sbuild,$build in
438       yes,release)
439         case $sign in yes) sbuildargs="-k$signkey $sbuildargs" ;; esac
440         ;;
441       yes,*)
442         if [ -d $toppath/dist-$build.pkgs ]; then
443           sbuildargs="-p$toppath/dist-$build.pkgs $sbuildargs"
444         fi
445         ;;
446     esac
447     case $sbuild,$build,$sign in
448       yes,*) run mdw-sbuild $sbuildargs ;;
449       no,release,yes) run dpkg-buildpackage -k$signkey ;;
450       no,*) run dpkg-buildpackage -us -uc ;;
451     esac
452     ;;
453 esac
454
455 ## Maybe upload Debian packages.
456 cd $releasepath
457 case "$upload,$build" in
458   yes,test) info "Test build: not uploading." ;;
459   yes,release) run rsync $distdir.tar.gz $distdir.tar.gz.gpg $uploadpath ;;
460 esac
461 case "$debian,$upload,$dput,$build" in
462   yes,yes,yes,release) run dput -f $dputtarget *.changes ;;
463 esac
464
465 ## Tidy up.
466 case "$clean" in
467   yes)
468     rm -rf $releasepath/$distdir
469     rm -rf $releasepath/_source
470     rm -rf $releasepath/_build
471     ;;
472 esac
473
474 ## Done.
475 info "All OK."
476
477 ###----- That's all, folks --------------------------------------------------