bin/mdw-build: Reflow the negated options list.
[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 | \
163 nosbuild | nosign )
164 eval "${opt#no}=no"
165 ;;
166 *)
167 usage >&2
168 exit 1
169 ;;
170 esac
171 done
172
173 ## Parse DEB_BUILD_OPTIONS.
174 jobs=1
175 set -- $DEB_BUILD_OPTIONS
176 for opt; do
177 case "$opt" in
178 parallel=*) jobs=${opt#*=} ;;
179 esac
180 done
181
182 makeopts=""
183 case $jobs in 1) ;; *) makeopts="$makeopts -j$jobs" ;; esac
184
185 ###--------------------------------------------------------------------------
186 ### Utility functions.
187
188 ## File descriptor assignments:
189 ##
190 ## 0 -- original stdin (never touched)
191 ## 1, 2 -- stdout, stderr, redirected to 3 while running comamnds
192 ## log -- logfile and original stderr (verbose), or logfile only (quiet);
193 ## captures command output
194 ## diag -- logfile; primary diagnostic output
195 ## term -- orginal stderr; secondary diagnostic output (with colours)
196
197 notify () {
198 colour=$1 message=$2
199 echo $message >&$diag
200 echo "$(tput bold; tput setaf $colour)$message$(tput sgr0; tput op)" >&$term
201 }
202
203 fail () {
204 notify 1 "!!! $*"
205 exit 1
206 }
207
208 warn () {
209 case $build in
210 release) fail "$*" ;;
211 *) notify 5 "??? $*" ;;
212 esac
213 }
214
215 info () {
216 notify 6 "--- $*"
217 }
218
219 assign () {
220 info "$1 = $2"
221 eval "$1=$2"
222 }
223
224 runx () {
225 notify 2 "+++ $*"
226 nice "$@" 2>&$log {log}>&- {diag}>&- {term}>&- || fail "$1: exit $?"
227 }
228
229 run () { runx "$@" >&$log; }
230
231 yesno () {
232 echo -n "(test $*)" >&$diag
233 if "$@" >&$diag 2>&$diag {log}>&- {diag}>&- {term}>&-; then
234 echo "(yes)" >&$diag
235 echo yes
236 else
237 echo "(no)" >&$diag
238 echo no
239 fi
240 }
241
242 ###--------------------------------------------------------------------------
243 ### Do the building.
244
245 ## Find the top-level package directory.
246 while [ ! -f configure.ac -a ! -f configure.in -a \
247 ! -f .links -a ! -d .git ]; do
248 case "$(pwd)" in
249 /)
250 fail "couldn't find top-level directory"
251 ;;
252 esac
253 cd ..
254 done
255 toppath=$(pwd)
256
257 ## Build any necessary qualifiers.
258 qual= sep=.
259 case ${SBOX_SESSION_DIR+t},${DEB_BUILD_ARCH+t} in
260 t,t) qual=$qual$sep$DEB_BUILD_ARCH; sep=- ;;
261 t,*) fail "unknown build arch" ;;
262 esac
263
264 ## Construct the output directory.
265 releasepath=$toppath/dist-$build$qual
266 chmod -R +w $releasepath 2>/dev/null || :
267 rm -rf $releasepath 2>/dev/null || :
268 mkdir $releasepath
269 logfile=$releasepath/mdw-build.log
270 exec {term}>&2
271 exec {diag}>>$logfile || fail "Failed to create log."
272 case $verbose in
273 no) exec {log}>&$diag ;;
274 yes) exec {log}> >(tee -a $logfile >&$term {term}>&- {diag}>&-) ;;
275 esac
276
277 ## Repeat the earlier assignments for tbe benefit of the logfile.
278 assign toppath $toppath
279 assign releasepath $releasepath
280 assign logfile $logfile
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 --------------------------------------------------