#! /bin/sh -e ### ### Build a Debian package on supported architectures ### ### (c) 2016 Mark Wooding ### ###----- Licensing notice --------------------------------------------------- ### ### This program is free software; you can redistribute it and/or modify ### it under the terms of the GNU General Public License as published by ### the Free Software Foundation; either version 2 of the License, or ### (at your option) any later version. ### ### This program is distributed in the hope that it will be useful, ### but WITHOUT ANY WARRANTY; without even the implied warranty of ### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ### GNU General Public License for more details. ### ### You should have received a copy of the GNU General Public License ### along with this program; if not, write to the Free Software Foundation, ### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ###-------------------------------------------------------------------------- ### Configuration. unset buildroot default_targets parallel for i in \ "/etc/mdw-sbuild.conf" \ "${XDG_CONFIG_HOME-$HOME/.config}/mdw-sbuild.conf" do if [ -f "$i" ]; then . "$i"; fi done : ${buildroot=$HOME/build} : ${default_targets="wheezy-amd64 wheezy-i386"} : ${parallel=-j3} : ${DEB_BUILD_OPTIONS=parallel=4}; export DEB_BUILD_OPTIONS ###-------------------------------------------------------------------------- ### Some utilities. prog=${0##*/} fail () { echo >&2 "$prog: $*"; exit 1; } usage () { echo "usage: $prog [-ain] [-t TARGET] COMMAND [ARGUMENTS ...]"; } fail_usage () { usage >&2; exit 1; } want_1 () { what=$1 pat=$2 type=$3; shift 3 for i in "$@"; do [ $type "$i" ] || fail "$what not found: \`$i'" done case $# in 1) ;; *) fail "expected exactly one $what matching \`$pat', but found $#" ;; esac echo "$1" } ###-------------------------------------------------------------------------- ### Parse options. bogusp=nil archp=nil indepp=nil makeopts="" unset targets while getopts "haint:" opt; do case $opt in h) usage cat </dev/null 2>&1; then winp=t cd "$ver#$nn" break fi ## Make sure it actually failed because a directory appeared, rather ## than for some other reason. [ -e "$ver#$nn" ] || \ fail "unexpectedly couldn't create \`$buildroot/$dist#$nn'" done ## Make sure we actually succeeded. case $winp in t) ;; *) fail "failed to create build directory" ;; esac ## Make an empty directory for dependency packages. mkdir -p pkgs/ ## Done. echo "$buildroot/$dist#$nn" ;; *,dir) echo >&2 "usage: $prog dir PROJECT/VERSION"; exit 1 ;; 2,build) ## build BUILDDIR ## Track down the build directory. builddir=$2 cd "$builddir" dsc=$(want_1 "file" "*.dsc" -f *.dsc) ## Figure out which targets need building. If the `.dsc' file isn't ## telling, assume it needs building everywhere and let sbuild(1) sort ## out the mess. os=$(dpkg-architecture -qDEB_HOST_ARCH_OS) unset first rest; anyp=nil depp=nil allp=nil wantarchs=$(sed -n '/^[Aa]rchitecture:/ s/^[^:]*: *//p' "$dsc") : ${wantarchs:=any} unset buildarchs buildarchs_seen=: ## Work through the available targets assigning builds to them. This is ## actually a little tricky. for t in $targets; do ## Dissect the target name. suite=${t%%-*} archs=${t#*-} case $archs in */*) target=${archs%/*} host=${archs#*/} ;; *) target=$archs host=$archs; t=$suite-$target/$host ;; esac case $buildarchs_seen in *:$target:*) ;; *) buildarchs=${buildarchs+$buildarchs }$target buildarchs_seen=$buildarchs_seen$target: ;; esac ## Work through the architectures which we can build. for arch in $wantarchs; do case $arch in all) ## Package suitable for all architectures. ## If we don't want to build architecture-neutral packages then ## there's nothing to do. case $indepp in nil) continue ;; esac ## Pick this up if nobody has volunteered. However, we should be ## ready to let some other architecture build this if it's going ## to build some architecture-dependent package too. case $anyp in nil) first=$t anyp=t allp=t ;; esac ;; *) ## There's at least one architecture-specific package. ## If we don't want to build architecture-specific packages then ## there's nothing to do. case $archp in nil) continue ;; esac ## If we can't build it then we shouldn't try. if ! dpkg-architecture -a"$os-$target" -i"$arch"; then continue fi ## Decide whether we should take responsibility for the ## architecture-neutral packages. If nobody's claimed them yet, ## or the previous claimant wasn't building architecture-specific ## packages, we should take over. case $depp in nil) first=$t depp=t anyp=t ;; t) rest="${rest+$rest }$t" ;; esac ;; esac done done ## If we never found a match then we can't do anything. case $anyp in nil) echo "$prog: no packages to build"; exit 0 ;; esac ## Figure out the right options to use. case $indepp in t) firstopt="--arch-all" ;; nil) firstopt="--no-arch-all" ;; esac case $archp in t) ;; nil) firstopt="$firstopt --debbuildopt=-A" ;; esac ## Sort out the additional packages. This is rather annoying, because ## sbuild(1) does this in a really stupid way. rm -rf pkgs.* for a in $buildarchs; do mkdir pkgs.$a for f in $(dpkg-scanpackages -a$a pkgs/ | sed -n '/^Filename: /s///p') do ln $f pkgs.$a/ done done ## Build a cheesy makefile to run these in parallel. cat >build.mk <build-status.\$\$full; \\ sbuild \\ --extra-package=pkgs.\$\$target/ \\ --dist=\$\$suite --build=\$\$host --host=\$\$target \\ --chroot=\$\$suite-\$\$host --verbose \$1 \$(DSC); \\ rc=\$\$?; case \$\$rc in \\ 0) echo ok >build-status.\$\$full ;; \\ *) echo failed rc=\$\$rc >build-status.\$\$full ;; \\ esac; } | \\ while IFS= read -r line; do \\ printf "%s: %s\n" "\$\$full" "\$\$line"; \\ done; \\ read st _ build-status.${i%/*}; done ## And we're ready to go. mkfifo pipeout cat pipeout& catpid=$! set +e; make -fbuild.mk $parallel $makeopts -k all >pipeout rc=$?; set -e wait $! rm build.mk pipeout build-status.* find . -maxdepth 1 -type l -exec rm {} \; exit $rc ;; build,*) echo >&2 "usage: $prog build BUILDDIR"; exit 1 ;; *) fail "unknown command \`$1'" ;; esac ###----- That's all, folks --------------------------------------------------