chiark / gitweb /
bin/xduplic-terminal: New script: start terminals for `xduplic-copier'.
[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   "$@" 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 assign srcpath $toppath
258
259 ## Build any necessary qualifiers.
260 qual= sep=.
261 case ${SBOX_SESSION_DIR+t},${DEB_BUILD_ARCH+t} in
262   t,t) qual=$qual$sep$DEB_BUILD_ARCH; sep=- ;;
263   t,*) fail "unknown build arch" ;;
264 esac
265
266 ## Construct the output directory.
267 assign releasepath $srcpath/dist-$build$qual
268 chmod -R +w $releasepath 2>/dev/null || :
269 rm -rf $releasepath 2>/dev/null || :
270 mkdir $releasepath
271 case $verbose in
272   no)
273     exec {log}>&- {diag}>&-
274     exec {diag}>$releasepath/mdw-build.log {log}>&$diag ||
275       fail "Failed to create log."
276     ;;
277 esac
278
279 ## Do we have a Git repository?
280 case "$checkout,$setup,$(yesno [ -d $srcpath/.git ])" in
281   yes,no,*)
282     fail "Inconsistent options: can't check out without setup."
283     ;;
284   yes,yes,no)
285     info "No Git repository found."
286     checkout=no gitver=none
287     ;;
288   yes,yes,yes)
289     cd $srcpath
290     [ "$(git ls-files -m)" = "" ] ||
291       warn "working tree has uncommitted changes"
292     ;;
293 esac
294
295 ## Is there Debian build equipment?
296 case "$debian,$(yesno [ -d $srcpath/debian ])" in
297   yes,no)
298     info "No debian directory found."
299     debian=no debver=none
300     ;;
301   no,*)
302     debver=none
303     ;;
304   yes,yes)
305     debver=$(dpkg-parsechangelog | sed -n 's/^Version: //p')
306     debsrc=$(dpkg-parsechangelog | sed -n 's/^Source: //p')
307     debname=$(git config user.name) debemail=$(git config user.email)
308     ;;
309 esac
310
311 ## Maybe check out a copy of the source.
312 case "$checkout" in
313   yes)
314     cd $releasepath
315     run git clone -sn $srcpath/.git _source
316     assign srcpath $releasepath/_source
317     cd $srcpath
318     run git update-ref refs/heads/mdw-build $checkoutrev ""
319     run git symbolic-ref HEAD refs/heads/mdw-build
320     run git read-tree --reset refs/heads/mdw-build
321     run git checkout-index -afu
322     assign gitversion "$(git describe --abbrev=4)"
323     ;;
324 esac
325
326 ## Check the version number.
327 hack_dch_p=no
328 case "$gitversion,$debver" in
329   none,* | *,none)
330     ;;
331   *)
332     dvref=$(echo "$debver" | tr '~' '_')
333     if [ "$gitversion" = "$dvref" ]; then
334       assign debversion "$debver"
335     else
336       warn "Git version $gitversion doesn't match Debian version $debver"
337       hack_dch=yes
338       dver=$(echo $gitversion | sed 's/-/+/; s/-/./g')
339       case $debver in *~) dver=$debver$dver ;; esac
340       assign debversion "$dver"
341       now=$(date -R)
342     fi
343     ;;
344 esac
345
346 ## Maybe refresh the build machinery.
347 case "$setup" in
348   yes)
349     run $setupcmd
350     ;;
351 esac
352
353 ## Initialize the build directory.
354 case "$vpath,$(yesno [ -e $srcpath/configure ])" in
355   yes,yes)
356     assign buildpath $releasepath/_build
357     mkdir $buildpath
358     cd $buildpath
359     run $srcpath/configure
360     ;;
361   no,yes)
362     info "VPATH build disabled"
363     assign buildpath $srcpath
364     distcheck=no
365     cd $srcpath
366     run ./configure
367     ;;
368   *,no)
369     info "no configure script"
370     assign buildpath $srcpath
371     cd $srcpath
372     ;;
373 esac
374
375 ## Discover the release name.
376 cat >find-distdir.mk <<'EOF'
377 include Makefile
378 print-distdir:
379         @echo >&$(fd) $(distdir)
380 EOF
381 assign distdir \
382   $({ $make -f find-distdir.mk print-distdir fd=$t >/dev/null 2>&1; } {t}>&1)
383
384 ## Get a tarball distribution.
385 case "$distcheck" in
386   yes)
387     run $make $makeopts distcheck
388     ;;
389   no)
390     run $make $makeopts dist
391     ;;
392 esac
393
394 cd $releasepath
395
396 case $native in
397   yes)
398     if ! tar tf $buildpath/$distdir.tar.gz 2>/dev/null | grep -q RELEASE
399     then
400       fail "missing RELEASE file in distribution"
401     fi
402     ;;
403 esac
404
405 run mv $buildpath/$distdir.tar.gz .
406 case $build,$sign in
407   release,yes)
408     run gpg -u$signkey -ab -o$distdir.tar.gz.gpg $distdir.tar.gz
409     ;;
410 esac
411
412 ## Maybe build the Debian packages.
413 case "$debian" in
414   yes)
415     run tar xvfz $distdir.tar.gz
416     cd $distdir
417     case $hack_dch in
418       yes)
419         cat - debian/changelog >debian/changelog.new <<EOF
420 $debsrc ($debversion) experimental; urgency=low
421
422   * Hacking in process, not intended for release.
423
424  -- $debname <$debemail>  $now
425
426 EOF
427         mv debian/changelog.new debian/changelog
428         ;;
429     esac
430     sbuildargs=$sbuildsrv
431     case $sbuild,$build in
432       yes,release)
433         case $sign in yes) sbuildargs="-k$signkey $sbuildargs" ;; esac
434         ;;
435       yes,*)
436         if [ -d $toppath/dist-$build.pkgs ]; then
437           sbuildargs="-p$toppath/dist-$build.pkgs $sbuildargs"
438         fi
439         ;;
440     esac
441     case $sbuild,$build,$sign in
442       yes,*) run mdw-sbuild $sbuildargs ;;
443       no,release,yes) run dpkg-buildpackage -k$signkey ;;
444       no,*) run dpkg-buildpackage -us -uc ;;
445     esac
446     ;;
447 esac
448
449 ## Maybe upload Debian packages.
450 cd $releasepath
451 case "$upload,$build" in
452   yes,test) info "Test build: not uploading." ;;
453   yes,release) run rsync $distdir.tar.gz $distdir.tar.gz.gpg $uploadpath ;;
454 esac
455 case "$debian,$upload,$dput,$build" in
456   yes,yes,yes,release) run dput -f $dputtarget *.changes ;;
457 esac
458
459 ## Tidy up.
460 case "$clean" in
461   yes)
462     rm -rf $releasepath/$distdir
463     rm -rf $releasepath/_source
464     rm -rf $releasepath/_build
465     ;;
466 esac
467
468 ## Done.
469 info "All OK."
470
471 ###----- That's all, folks --------------------------------------------------