chiark / gitweb /
el/dot-emacs.el (mdw-fontify-rust): Fix integer literal syntax.
[profile] / bin / mdw-sbuild-server
1 #! /bin/sh -e
2 ###
3 ### Build a Debian package on supported architectures
4 ###
5 ### (c) 2016 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 ### Configuration.
26
27 unset buildroot default_targets parallel
28 for i in \
29   "/etc/mdw-sbuild.conf" \
30   "${XDG_CONFIG_HOME-$HOME/.config}/mdw-sbuild.conf"
31 do
32   if [ -f "$i" ]; then . "$i"; fi
33 done
34 : ${buildroot=$HOME/build}
35 : ${default_targets="wheezy-amd64 wheezy-i386"}
36 : ${parallel=-j3}
37 : ${DEB_BUILD_OPTIONS=parallel=4}; export DEB_BUILD_OPTIONS
38
39 ###--------------------------------------------------------------------------
40 ### Some utilities.
41
42 prog=${0##*/}
43
44 fail () { echo >&2 "$prog: $*"; exit 1; }
45 usage () { echo "usage: $prog [-ain] [-t TARGET] COMMAND [ARGUMENTS ...]"; }
46 fail_usage () { usage >&2; exit 1; }
47
48 want_1 () {
49   what=$1 pat=$2 type=$3; shift 3
50   for i in "$@"; do
51     [ $type "$i" ] || fail "$what not found: \`$i'"
52   done
53   case $# in
54     1) ;;
55     *) fail "expected exactly one $what matching \`$pat', but found $#" ;;
56   esac
57   echo "$1"
58 }
59
60 ###--------------------------------------------------------------------------
61 ### Parse options.
62
63 bogusp=nil archp=nil indepp=nil makeopts=""
64 unset targets
65
66 while getopts "haint:" opt; do
67   case $opt in
68     h)
69       usage
70       cat <<EOF
71
72 Options:
73         -h              Show this help text.
74         -a              Build only architecture-dependent packages.
75         -i              Build only architecture-neutral packages.
76         -n              Don't actually do the build.
77         -t TARGET       Build in TARGET build environment.
78
79 Commands available:
80
81         dir PROJECT/VERSION
82                 Return a freshly-made directory for the source code to
83                 go in.
84
85         build BUILDDIR
86                 Build the package placed in BUILDDIR, which should contain
87                 exactly one \`.dsc' file, and whatever source archive files
88                 are necessary.
89 EOF
90       exit
91       ;;
92     a) archp=t ;;
93     i) indepp=t ;;
94     n) makeopts="${makeopts+$makeopts }-n" ;;
95     t) targets="${targets+$targets }$OPTARG" ;;
96     *) bogusp=nil ;;
97   esac
98 done
99 shift $(( $OPTIND - 1 ))
100
101 case $bogusp in t) fail_usage ;; esac
102 case $archp,$indepp in nil,nil) archp=t indepp=t ;; esac
103 case ${targets+t} in t) ;; *) targets=$default_targets ;; esac
104
105 ###--------------------------------------------------------------------------
106 ### Main work.
107
108 case "$#,$1" in
109   0,*) fail_usage ;;
110   *,*,*) fail "bad command name \`$1'" ;;
111
112   2,dir)
113     ## dirname PROJECT/VERSION
114
115     ## Try to create a fresh build directory.
116     dist=$2
117     case "$dist" in */*/*) fail "bad distribution name \`$dist'" ;; esac
118     proj=${dist%/*} ver=${dist#*/}
119     cd "$buildroot"
120     mkdir -p "$proj"
121     cd "$proj"
122     i=0
123     winp=nil
124     while [ $i -lt 50 ]; do
125       i=$(( $i + 1 ))
126
127       ## Find a sequence number different from all of the existing builds of
128       ## this version.
129       nn=1
130       for j in "$ver#"*; do
131         case "$j" in "$ver#*") break ;; esac
132         n=${j##*\#}
133         if [ $nn -le $n ]; then nn=$(( $n + 1 )); fi
134       done
135
136       ## Try to make the build directory.  This might not work if we're
137       ## racing with another process, but that's why we're trying in a loop.
138       if mkdir "$ver#$nn" >/dev/null 2>&1; then
139         winp=t
140         cd "$ver#$nn"
141         break
142       fi
143
144       ## Make sure it actually failed because a directory appeared, rather
145       ## than for some other reason.
146       [ -e "$ver#$nn" ] || \
147         fail "unexpectedly couldn't create \`$buildroot/$dist#$nn'"
148     done
149
150     ## Make sure we actually succeeded.
151     case $winp in t) ;; *) fail "failed to create build directory" ;; esac
152
153     ## Make an empty directory for dependency packages.
154     mkdir -p pkgs/
155
156     ## Done.
157     echo "$buildroot/$dist#$nn"
158     ;;
159
160   *,dir)
161     echo >&2 "usage: $prog dir PROJECT/VERSION"; exit 1 ;;
162
163   2,build)
164     ## build BUILDDIR
165
166     ## Track down the build directory.
167     builddir=$2
168     cd "$builddir"
169     dsc=$(want_1 "file" "*.dsc" -f *.dsc)
170
171     ## Figure out which targets need building.  If the `.dsc' file isn't
172     ## telling, assume it needs building everywhere and let sbuild(1) sort
173     ## out the mess.
174     os=$(dpkg-architecture -qDEB_HOST_ARCH_OS)
175     unset first rest; anyp=nil depp=nil allp=nil
176     wantarchs=$(sed -n '/^[Aa]rchitecture:/ s/^[^:]*: *//p' "$dsc")
177     : ${wantarchs:=any}
178     unset buildarchs buildarchs_seen=:
179
180     ## Work through the available targets assigning builds to them.  This is
181     ## actually a little tricky.
182     for t in $targets; do
183
184       ## Dissect the target name.
185       suite=${t%%-*} archs=${t#*-}
186       case $archs in
187         */*) target=${archs%/*} host=${archs#*/} ;;
188         *) target=$archs host=$archs; t=$suite-$target/$host ;;
189       esac
190       case $buildarchs_seen in
191         *:$target:*)
192           ;;
193         *)
194           buildarchs=${buildarchs+$buildarchs }$target
195           buildarchs_seen=$buildarchs_seen$target:
196           ;;
197       esac
198
199       ## Work through the architectures which we can build.
200       for arch in $wantarchs; do
201         case $arch in
202           all)
203             ## Package suitable for all architectures.
204
205             ## If we don't want to build architecture-neutral packages then
206             ## there's nothing to do.
207             case $indepp in nil) continue ;; esac
208
209             ## Pick this up if nobody has volunteered.  However, we should be
210             ## ready to let some other architecture build this if it's going
211             ## to build some architecture-dependent package too.
212             case $anyp in nil) first=$t anyp=t allp=t ;; esac
213             ;;
214           *)
215             ## There's at least one architecture-specific package.
216
217             ## If we don't want to build architecture-specific packages then
218             ## there's nothing to do.
219             case $archp in nil) continue ;; esac
220
221             ## If we can't build it then we shouldn't try.
222             if ! dpkg-architecture -a"$os-$target" -i"$arch"; then
223               continue
224             fi
225
226             ## Decide whether we should take responsibility for the
227             ## architecture-neutral packages.  If nobody's claimed them yet,
228             ## or the previous claimant wasn't building architecture-specific
229             ## packages, we should take over.
230             case $depp in
231               nil) first=$t depp=t anyp=t ;;
232               t) rest="${rest+$rest }$t" ;;
233             esac
234             ;;
235         esac
236       done
237     done
238
239     ## If we never found a match then we can't do anything.
240     case $anyp in nil) echo "$prog: no packages to build"; exit 0 ;; esac
241
242     ## Figure out the right options to use.
243     case $indepp in
244       t) firstopt="--arch-all" ;;
245       nil) firstopt="--no-arch-all" ;;
246     esac
247     case $archp in
248       t) ;;
249       nil) firstopt="$firstopt --debbuildopt=-A" ;;
250     esac
251
252     ## Sort out the additional packages.  This is rather annoying, because
253     ## sbuild(1) does this in a really stupid way.
254     rm -rf pkgs.*
255     for a in $buildarchs; do
256       mkdir pkgs.$a
257       for f in $(dpkg-scanpackages -a$a pkgs/ |
258                     sed -n '/^Filename: /s///p')
259       do
260         ln $f pkgs.$a/
261       done
262     done
263
264     ## Build a cheesy makefile to run these in parallel.
265     cat >build.mk <<EOF
266 ### -*-makefile-*-
267 DSC = $dsc
268 FIRST = $first
269 REST = $rest
270 sbuild-wrap = \\
271         t=\$@; \\
272         host=\$\${t\#\#*/} full=\$\${t%/*}; \\
273         suite=\$\${full%%-*} target=\$\${full\#*-}; \\
274         { echo started >build-status.\$\$full; \\
275           sbuild \\
276                 --extra-package=pkgs.\$\$target/ \\
277                 --dist=\$\$suite --build=\$\$host --host=\$\$target \\
278                 --chroot=\$\$suite-\$\$host --verbose \$1 \$(DSC); \\
279           rc=\$\$?; case \$\$rc in \\
280             0) echo ok >build-status.\$\$full ;; \\
281             *) echo failed rc=\$\$rc >build-status.\$\$full ;; \\
282           esac; } | \\
283         while IFS= read -r line; do \\
284           printf "%s: %s\n" "\$\$full" "\$\$line"; \\
285         done; \\
286         read st _ <build-status.\$\$full && \\
287         case \$\$st in ok) exit 0 ;; *) exit 1 ;; esac
288 all: \$(FIRST) \$(REST)
289 \$(FIRST):; \$(call sbuild-wrap,$firstopt)
290 \$(REST):; \$(call sbuild-wrap,--no-arch-all)
291 EOF
292
293     ## Make some marker files to say things are in progress.
294     for i in $first $rest; do echo "starting" >build-status.${i%/*}; done
295
296     ## And we're ready to go.
297     mkfifo pipeout
298     cat pipeout& catpid=$!
299     set +e; make -fbuild.mk $parallel $makeopts -k all >pipeout
300     rc=$?; set -e
301     wait $!
302     rm build.mk pipeout build-status.*
303     find . -maxdepth 1 -type l -exec rm {} \;
304     exit $rc
305     ;;
306   build,*)
307     echo >&2 "usage: $prog build BUILDDIR"; exit 1 ;;
308
309   *)
310     fail "unknown command \`$1'"
311     ;;
312 esac
313
314 ###----- That's all, folks --------------------------------------------------