bin/mdw-sbuild-server: Align `sbuild' output in the log spew.
[profile] / bin / mdw-sbuild-server
CommitLineData
b94830d9
MW
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
27unset buildroot default_targets parallel
28for i in \
29 "/etc/mdw-sbuild.conf" \
30 "${XDG_CONFIG_HOME-$HOME/.config}/mdw-sbuild.conf"
31do
32 if [ -f "$i" ]; then . "$i"; fi
33done
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
42prog=${0##*/}
43
44fail () { echo >&2 "$prog: $*"; exit 1; }
45usage () { echo "usage: $prog [-ain] [-t TARGET] COMMAND [ARGUMENTS ...]"; }
46fail_usage () { usage >&2; exit 1; }
47
48want_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
63bogusp=nil archp=nil indepp=nil makeopts=""
64unset targets
65
66while getopts "haint:" opt; do
67 case $opt in
68 h)
69 usage
70 cat <<EOF
71
72Options:
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
79Commands 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.
89EOF
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
98done
99shift $(( $OPTIND - 1 ))
100
101case $bogusp in t) fail_usage ;; esac
102case $archp,$indepp in nil,nil) archp=t indepp=t ;; esac
103case ${targets+t} in t) ;; *) targets=$default_targets ;; esac
104
105###--------------------------------------------------------------------------
106### Main work.
107
108case "$#,$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
6cf97414 140 cd "$ver#$nn"
b94830d9
MW
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
6cf97414
MW
153 ## Make an empty directory for dependency packages.
154 mkdir -p pkgs/
155
b94830d9
MW
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}
be0b4ef6 178 unset buildarchs buildarchs_seen=:
b94830d9
MW
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
92b05c85
MW
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
be0b4ef6
MW
190 case $buildarchs_seen in
191 *:$target:*)
192 ;;
193 *)
194 buildarchs=${buildarchs+$buildarchs }$target
195 buildarchs_seen=$buildarchs_seen$target:
196 ;;
197 esac
92b05c85 198
b94830d9
MW
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
28f48d70 217 ## If we don't want to build architecture-specific packages then
b94830d9
MW
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.
92b05c85 222 if ! dpkg-architecture -a"$os-$target" -i"$arch"; then
b94830d9
MW
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) ;;
ea91eab4 249 nil) firstopt="$firstopt --no-arch-any" ;;
b94830d9
MW
250 esac
251
6cf97414
MW
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
b94830d9
MW
264 ## Build a cheesy makefile to run these in parallel.
265 cat >build.mk <<EOF
266### -*-makefile-*-
267DSC = $dsc
268FIRST = $first
269REST = $rest
01c036af
MW
270sbuild-wrap = \\
271 t=\$@; \\
92b05c85
MW
272 host=\$\${t\#\#*/} full=\$\${t%/*}; \\
273 suite=\$\${full%%-*} target=\$\${full\#*-}; \\
274 { echo started >build-status.\$\$full; \\
01c036af 275 sbuild \\
6cf97414 276 --extra-package=pkgs.\$\$target/ \\
92b05c85
MW
277 --dist=\$\$suite --build=\$\$host --host=\$\$target \\
278 --chroot=\$\$suite-\$\$host --verbose \$1 \$(DSC); \\
01c036af 279 rc=\$\$?; case \$\$rc in \\
92b05c85
MW
280 0) echo ok >build-status.\$\$full ;; \\
281 *) echo failed rc=\$\$rc >build-status.\$\$full ;; \\
01c036af
MW
282 esac; } | \\
283 while IFS= read -r line; do \\
b1f23788 284 printf "%-21s | %s\n" "\$\$full" "\$\$line"; \\
01c036af 285 done; \\
92b05c85 286 read st _ <build-status.\$\$full && \\
01c036af 287 case \$\$st in ok) exit 0 ;; *) exit 1 ;; esac
b94830d9 288all: \$(FIRST) \$(REST)
01c036af
MW
289\$(FIRST):; \$(call sbuild-wrap,$firstopt)
290\$(REST):; \$(call sbuild-wrap,--no-arch-all)
b94830d9
MW
291EOF
292
01c036af 293 ## Make some marker files to say things are in progress.
92b05c85 294 for i in $first $rest; do echo "starting" >build-status.${i%/*}; done
01c036af 295
b94830d9
MW
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 $!
01c036af 302 rm build.mk pipeout build-status.*
b94830d9
MW
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 ;;
312esac
313
314###----- That's all, folks --------------------------------------------------