chiark / gitweb /
bin/mdw-build: Allow overriding the `make' program.
[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 exec 3>&2 4>/dev/null 5>&2
188
189 notify () {
190   colour=$1 message=$2
191   echo $message >&4
192   echo "$(tput bold; tput setaf $colour)$message$(tput sgr0; tput op)" >&5
193 }
194
195 fail () {
196   notify 1 "!!! $*"
197   exit 1
198 }
199
200 warn () {
201   case $build in
202     release) fail "$*" ;;
203     *) notify 5 "??? $*" ;;
204   esac
205 }
206
207 info () {
208   notify 6 "--- $*"
209 }
210
211 assign () {
212   info "$1 = $2"
213   eval "$1=$2"
214 }
215
216 runx () {
217   notify 2 "+++ $*"
218   "$@" 2>&3 || fail "$1: exit $?"
219 }
220
221 run () { runx "$@" >&3; }
222
223 yesno () {
224   echo -n "(test $*)" >&4
225   if "$@" >&4 2>&4; then
226     echo "(yes)" >&4
227     echo yes
228   else
229     echo "(no)" >&4
230     echo no
231   fi
232 }
233
234 ###--------------------------------------------------------------------------
235 ### Do the building.
236
237 ## Find the top-level package directory.
238 while [ ! -f configure.ac -a ! -f configure.in -a \
239         ! -f .links -a ! -d .git ]; do
240   case "$(pwd)" in
241     /)
242       fail "couldn't find top-level directory"
243       ;;
244   esac
245   cd ..
246 done
247 assign toppath $(pwd)
248 assign srcpath $toppath
249
250 ## Build any necessary qualifiers.
251 qual= sep=.
252 case ${SBOX_SESSION_DIR+t},${DEB_BUILD_ARCH+t} in
253   t,t) qual=$qual$sep$DEB_BUILD_ARCH; sep=- ;;
254   t,*) fail "unknown build arch" ;;
255 esac
256
257 ## Construct the output directory.
258 assign releasepath $srcpath/dist-$build$qual
259 chmod -R +w $releasepath 2>/dev/null || :
260 rm -rf $releasepath 2>/dev/null || :
261 mkdir $releasepath
262 case $verbose in
263   no)
264     exec 4>$releasepath/mdw-build.log 3>&4 ||
265       fail "Failed to create log."
266     ;;
267 esac
268
269 ## Do we have a Git repository?
270 case "$checkout,$setup,$(yesno [ -d $srcpath/.git ])" in
271   yes,no,*)
272     fail "Inconsistent options: can't check out without setup."
273     ;;
274   yes,yes,no)
275     info "No Git repository found."
276     checkout=no gitver=none
277     ;;
278   yes,yes,yes)
279     cd $srcpath
280     [ "$(git ls-files -m)" = "" ] ||
281       warn "working tree has uncommitted changes"
282     ;;
283 esac
284
285 ## Is there Debian build equipment?
286 case "$debian,$(yesno [ -d $srcpath/debian ])" in
287   yes,no)
288     info "No debian directory found."
289     debian=no debver=none
290     ;;
291   no,*)
292     debver=none
293     ;;
294   yes,yes)
295     debver=$(dpkg-parsechangelog | sed -n 's/^Version: //p')
296     debsrc=$(dpkg-parsechangelog | sed -n 's/^Source: //p')
297     debname=$(git config user.name) debemail=$(git config user.email)
298     ;;
299 esac
300
301 ## Maybe check out a copy of the source.
302 case "$checkout" in
303   yes)
304     cd $releasepath
305     run git clone -sn $srcpath/.git _source
306     assign srcpath $releasepath/_source
307     cd $srcpath
308     run git update-ref refs/heads/mdw-build $checkoutrev ""
309     run git symbolic-ref HEAD refs/heads/mdw-build
310     run git read-tree --reset refs/heads/mdw-build
311     run git checkout-index -afu
312     assign gitversion "$(git describe --abbrev=4)"
313     ;;
314 esac
315
316 ## Check the version number.
317 hack_dch_p=no
318 case "$gitversion,$debver" in
319   none,* | *,none)
320     ;;
321   *)
322     dvref=$(echo "$debver" | tr '~' '_')
323     if [ "$gitversion" = "$dvref" ]; then
324       assign debversion "$debver"
325     else
326       warn "Git version $gitversion doesn't match Debian version $debver"
327       hack_dch=yes
328       dver=$(echo $gitversion | sed 's/-/+/; s/-/./g')
329       case $debver in *~) dver=$debver$dver ;; esac
330       assign debversion "$dver"
331       now=$(date -R)
332     fi
333     ;;
334 esac
335
336 ## Maybe refresh the build machinery.
337 case "$setup" in
338   yes)
339     run $setupcmd
340     ;;
341 esac
342
343 ## Initialize the build directory.
344 case "$vpath,$(yesno [ -e $srcpath/configure ])" in
345   yes,yes)
346     assign buildpath $releasepath/_build
347     mkdir $buildpath
348     cd $buildpath
349     run $srcpath/configure
350     ;;
351   no,yes)
352     info "VPATH build disabled"
353     assign buildpath $srcpath
354     distcheck=no
355     cd $srcpath
356     run ./configure
357     ;;
358   *,no)
359     info "no configure script"
360     assign buildpath $srcpath
361     cd $srcpath
362     ;;
363 esac
364
365 ## Discover the release name.
366 cat >find-distdir.mk <<'EOF'
367 include Makefile
368 print-distdir:
369         @echo >&3 $(distdir)
370 EOF
371 assign distdir \
372         $({ $make -f find-distdir.mk print-distdir >/dev/null 2>&1; } 3>&1)
373
374 ## Get a tarball distribution.
375 case "$distcheck" in
376   yes)
377     run $make $makeopts distcheck
378     ;;
379   no)
380     run $make $makeopts dist
381     ;;
382 esac
383
384 cd $releasepath
385
386 case $native in
387   yes)
388     if ! tar tf $buildpath/$distdir.tar.gz 2>/dev/null | grep -q RELEASE
389     then
390       fail "missing RELEASE file in distribution"
391     fi
392     ;;
393 esac
394
395 run mv $buildpath/$distdir.tar.gz .
396 case $build,$sign in
397   release,yes)
398     run gpg -u$signkey -ab -o$distdir.tar.gz.gpg $distdir.tar.gz
399     ;;
400 esac
401
402 ## Maybe build the Debian packages.
403 case "$debian" in
404   yes)
405     run tar xvfz $distdir.tar.gz
406     cd $distdir
407     case $hack_dch in
408       yes)
409         cat - debian/changelog >debian/changelog.new <<EOF
410 $debsrc ($debversion) experimental; urgency=low
411
412   * Hacking in process, not intended for release.
413
414  -- $debname <$debemail>  $now
415
416 EOF
417         mv debian/changelog.new debian/changelog
418         ;;
419     esac
420     sbuildargs=$sbuildsrv
421     case $sbuild,$build in
422       yes,release)
423         case $sign in yes) sbuildargs="-k$signkey $sbuildargs" ;; esac
424         ;;
425       yes,*)
426         if [ -d $toppath/dist-$build.pkgs ]; then
427           sbuildargs="-p$toppath/dist-$build.pkgs $sbuildargs"
428         fi
429         ;;
430     esac
431     case $sbuild,$build,$sign in
432       yes,*) run mdw-sbuild $sbuildargs ;;
433       no,release,yes) run dpkg-buildpackage -k$signkey ;;
434       no,*) run dpkg-buildpackage -us -uc ;;
435     esac
436     ;;
437 esac
438
439 ## Maybe upload Debian packages.
440 cd $releasepath
441 case "$upload,$build" in
442   yes,test) info "Test build: not uploading." ;;
443   yes,release) run rsync $distdir.tar.gz $distdir.tar.gz.gpg $uploadpath ;;
444 esac
445 case "$debian,$upload,$dput,$build" in
446   yes,yes,yes,release) run dput -f $dputtarget *.changes ;;
447 esac
448
449 ## Tidy up.
450 case "$clean" in
451   yes)
452     rm -rf $releasepath/$distdir
453     rm -rf $releasepath/_source
454     rm -rf $releasepath/_build
455     ;;
456 esac
457
458 ## Done.
459 info "All OK."
460
461 ###----- That's all, folks --------------------------------------------------