bin/mdw-build: Support interim Debian package versions.
[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 default_depends sbuild sbuildsrv
87 default_depends sign signkey
88 default_depends upload uploadpath
89 default_depends dput dputtarget
90 : ${DEB_BUILD_OPTIONS=parallel=4}; export DEB_BUILD_OPTIONS
91
92 ###--------------------------------------------------------------------------
93 ### Parse options.
94
95 prog=${0##*/}
96
97 usage () {
98 cat <<EOF
99 Usage: $prog [-v] BUILDOPT
100
101 Build options:
102
103 [no]checkout[=REV]
104 [no]release
105 [no]setup[=RUNE]
106 [no]distcheck
107 [no]debian
108 [no]upload[=SERVER:PATH]
109 [no]dput[=TARGET]
110 [no]clean
111 [no]vpath
112 [no]sbuild[=SERVER]
113 [no]sign[=KEYID]
114 [no]native
115 EOF
116 }
117
118 ## Parse simple options.
119 verbose=no
120 while getopts "hv" opt; do
121 case "$opt" in
122 h) usage; exit 0 ;;
123 v) verbose=yes ;;
124 *) exit 1 ;;
125 esac
126 done
127 shift $((OPTIND - 1))
128
129 ## Parse the build options.
130 maybe_set () {
131 var=$1 want=$2
132 eval "p=\${$want+t}\${$want-nil}"
133 case $p in
134 t) eval $var=yes ;;
135 nil) echo >&2 "$prog: $want not set"; exit 1 ;;
136 esac
137 }
138 for opt; do
139 case "$opt" in
140 checkout) checkout=yes checkoutrev=HEAD ;;
141 checkout=*) checkout=yes checkoutrev=${opt#*=} ;;
142 release) build=release ;;
143 norelease) build=test ;;
144 setup) setup=yes setupcmd=mdw-setup ;;
145 setup=*) setup=yes setupcmd=${opt#*=} ;;
146 upload) maybe_set upload uploadpath ;;
147 upload=*) upload=yes uploadpath=${opt#*=} ;;
148 sign) maybe_set sign signkey ;;
149 sign=*) sign=yes signkey=${opt#*=} ;;
150 sbuild) maybe_set sbuild sbuildsrv ;;
151 sbuild=*) sbuild=yes sbuildsrv=${opt#*=} ;;
152 dput) maybe_set dput dputtarget ;;
153 dput=*) dput=yes dputtarget=${opt#*=} ;;
154
155 distcheck | debian | clean | vpath | native)
156 eval "$opt=yes"
157 ;;
158 nocheckout | nosetup | nodistcheck | nodebian | \
159 noupload | nodput | noclean | novpath | nonative | nosbuild | nosign)
160 eval "${opt#no}=no"
161 ;;
162 *)
163 usage >&2
164 exit 1
165 ;;
166 esac
167 done
168
169 ## Parse DEB_BUILD_OPTIONS.
170 jobs=1
171 set -- $DEB_BUILD_OPTIONS
172 for opt; do
173 case "$opt" in
174 parallel=*) jobs=${opt#*=} ;;
175 esac
176 done
177
178 makeopts=""
179 case $jobs in 1) ;; *) makeopts="$makeopts -j$jobs" ;; esac
180
181 ###--------------------------------------------------------------------------
182 ### Utility functions.
183
184 exec 3>&2 4>/dev/null 5>&2
185
186 notify () {
187 colour=$1 message=$2
188 echo $message >&4
189 echo "$(tput bold; tput setaf $colour)$message$(tput sgr0; tput op)" >&5
190 }
191
192 fail () {
193 notify 1 "!!! $*"
194 exit 1
195 }
196
197 warn () {
198 case $build in
199 release) fail "$*" ;;
200 *) notify 5 "??? $*" ;;
201 esac
202 }
203
204 info () {
205 notify 6 "--- $*"
206 }
207
208 assign () {
209 info "$1 = $2"
210 eval "$1=$2"
211 }
212
213 runx () {
214 notify 2 "+++ $*"
215 "$@" 2>&3 || fail "$1: exit $?"
216 }
217
218 run () { runx "$@" >&3; }
219
220 yesno () {
221 echo -n "(test $*)" >&4
222 if "$@" >&4 2>&4; then
223 echo "(yes)" >&4
224 echo yes
225 else
226 echo "(no)" >&4
227 echo no
228 fi
229 }
230
231 ###--------------------------------------------------------------------------
232 ### Do the building.
233
234 ## Find the top-level package directory.
235 while [ ! -f configure.ac -a ! -f configure.in -a \
236 ! -f .links -a ! -d .git ]; do
237 case "$(pwd)" in
238 /)
239 fail "couldn't find top-level directory"
240 ;;
241 esac
242 cd ..
243 done
244 assign toppath $(pwd)
245 assign srcpath $toppath
246
247 ## Build any necessary qualifiers.
248 qual= sep=.
249 case ${SBOX_SESSION_DIR+t},${DEB_BUILD_ARCH+t} in
250 t,t) qual=$qual$sep$DEB_BUILD_ARCH; sep=- ;;
251 t,*) fail "unknown build arch" ;;
252 esac
253
254 ## Construct the output directory.
255 assign releasepath $srcpath/dist-$build$qual
256 chmod -R +w $releasepath 2>/dev/null || :
257 rm -rf $releasepath 2>/dev/null || :
258 mkdir $releasepath
259 case $verbose in
260 no)
261 exec 4>$releasepath/mdw-build.log 3>&4 ||
262 fail "Failed to create log."
263 ;;
264 esac
265
266 ## Do we have a Git repository?
267 case "$checkout,$setup,$(yesno [ -d $srcpath/.git ])" in
268 yes,no,*)
269 fail "Inconsistent options: can't check out without setup."
270 ;;
271 yes,yes,no)
272 info "No Git repository found."
273 checkout=no gitver=none
274 ;;
275 yes,yes,yes)
276 cd $srcpath
277 [ "$(git ls-files -m)" = "" ] ||
278 warn "working tree has uncommitted changes"
279 ;;
280 esac
281
282 ## Is there Debian build equipment?
283 case "$debian,$(yesno [ -d $srcpath/debian ])" in
284 yes,no)
285 info "No debian directory found."
286 debian=no debver=none
287 ;;
288 no,*)
289 debver=none
290 ;;
291 yes,yes)
292 debver=$(dpkg-parsechangelog | sed -n 's/^Version: //p')
293 debsrc=$(dpkg-parsechangelog | sed -n 's/^Source: //p')
294 debname=$(git config user.name) debemail=$(git config user.email)
295 ;;
296 esac
297
298 ## Maybe check out a copy of the source.
299 case "$checkout" in
300 yes)
301 cd $releasepath
302 run git clone -sn $srcpath/.git _source
303 assign srcpath $releasepath/_source
304 cd $srcpath
305 run git checkout -b mdw-build $checkoutrev
306 assign gitversion "$(git describe --abbrev=4)"
307 ;;
308 esac
309
310 ## Check the version number.
311 hack_dch_p=no
312 case "$gitversion,$debver" in
313 none,* | *,none)
314 ;;
315 *)
316 dvref=$(echo "$debver" | tr '~' '_')
317 if [ "$gitversion" = "$dvref" ]; then
318 assign debversion "$debver"
319 else
320 warn "Git version $gitversion doesn't match Debian version $debver"
321 hack_dch=yes
322 dver=$(echo $gitversion | sed 's/-/+/; s/-/./g')
323 case $debver in *~) dver=$debver$dver ;; esac
324 assign debversion "$dver"
325 now=$(date -R)
326 fi
327 ;;
328 esac
329
330 ## Maybe refresh the build machinery.
331 case "$setup" in
332 yes)
333 run $setupcmd
334 ;;
335 esac
336
337 ## Initialize the build directory.
338 case "$vpath,$(yesno [ -e $srcpath/configure ])" in
339 yes,yes)
340 assign buildpath $releasepath/_build
341 mkdir $buildpath
342 cd $buildpath
343 run $srcpath/configure
344 ;;
345 no,yes)
346 info "VPATH build disabled"
347 assign buildpath $srcpath
348 distcheck=no
349 cd $srcpath
350 run ./configure
351 ;;
352 *,no)
353 info "no configure script"
354 assign buildpath $srcpath
355 cd $srcpath
356 ;;
357 esac
358
359 ## Discover the release name.
360 cat >find-distdir.mk <<'EOF'
361 include Makefile
362 print-distdir:
363 @echo >&3 $(distdir)
364 EOF
365 assign distdir \
366 $({ make -f find-distdir.mk print-distdir >/dev/null 2>&1; } 3>&1)
367
368 ## Get a tarball distribution.
369 case "$distcheck" in
370 yes)
371 run make $makeopts distcheck
372 ;;
373 no)
374 run make $makeopts dist
375 ;;
376 esac
377
378 cd $releasepath
379
380 case $native in
381 yes)
382 if ! tar tf $buildpath/$distdir.tar.gz 2>/dev/null | grep -q RELEASE
383 then
384 fail "missing RELEASE file in distribution"
385 fi
386 ;;
387 esac
388
389 run mv $buildpath/$distdir.tar.gz .
390 case $build,$sign in
391 release,yes)
392 run gpg -u$signkey -ab -o$distdir.tar.gz.gpg $distdir.tar.gz
393 ;;
394 esac
395
396 ## Maybe build the Debian packages.
397 case "$debian" in
398 yes)
399 run tar xvfz $distdir.tar.gz
400 cd $distdir
401 case $hack_dch in
402 yes)
403 cat - debian/changelog >debian/changelog.new <<EOF
404 $debsrc ($debversion) experimental; urgency=low
405
406 * Hacking in process, not intended for release.
407
408 -- $debname <$debemail> $now
409
410 EOF
411 mv debian/changelog.new debian/changelog
412 ;;
413 esac
414 sbuildargs=$sbuildsrv
415 case $sbuild,$build in
416 yes,release)
417 case $sign in yes) sbuildargs="-k$signkey $sbuildargs" ;; esac
418 ;;
419 yes,*)
420 if [ -d $toppath/dist-$build.pkgs ]; then
421 sbuildargs="-p$toppath/dist-$build.pkgs $sbuildargs"
422 fi
423 ;;
424 esac
425 case $sbuild,$build,$sign in
426 yes,*) run mdw-sbuild $sbuildargs ;;
427 no,release,yes) run dpkg-buildpackage -k$signkey ;;
428 no,*) run dpkg-buildpackage -us -uc ;;
429 esac
430 ;;
431 esac
432
433 ## Maybe upload Debian packages.
434 cd $releasepath
435 case "$upload,$build" in
436 yes,test) info "Test build: not uploading." ;;
437 yes,release) run rsync $distdir.tar.gz $distdir.tar.gz.gpg $uploadpath ;;
438 esac
439 case "$debian,$upload,$dput,$build" in
440 yes,yes,yes,release) run dput -f $dputtarget *.changes ;;
441 esac
442
443 ## Tidy up.
444 case "$clean" in
445 yes)
446 rm -rf $releasepath/$distdir
447 rm -rf $releasepath/_source
448 rm -rf $releasepath/_build
449 ;;
450 esac
451
452 ## Done.
453 info "All OK."
454
455 ###----- That's all, folks --------------------------------------------------