chiark / gitweb /
bin/disorder-notify: Rewrite and take over the functionality of `media-keys'.
[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 --------------------------------------------------