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