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