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