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 --------------------------------------------------