#! /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"} : ${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" } run () { case $notreally in t) echo "+ $*" ;; nil) nice "$@" ;; esac } decor () { tag=$1 marker=$2 while IFS= read -r line; do printf "%-21s %c %s\n" "$tag" "$marker" "$line" done } ###-------------------------------------------------------------------------- ### Parse options. bogusp=nil archp=nil indepp=nil keepon=nil notreally=nil unset targets dbpargs while getopts "haint:A:" 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 --no-arch-any" ;; 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 the builds sequentially. Tests can conflict with each other, ## e.g., over port numbers. rc=0 buildopt=$firstopt for t in $first $rest; do host=${t##*/} full=${t%/*} suite=${full%%-*} target=${full#*-} ## And we're ready to go. exec 3>&1 thisrc=$( { { { { set +e run sbuild --extra-package=$pkgs.$target \ --dist=$suite --build=$host --host=$target \ --chroot=$suite-$host --verbose $buildopt $dsc \ ${dbpargs+--debbuildopts="$dbpargs"} \ 3>&- 4>&- 5>&- echo $? >&5 } | decor "$full" "|" >&4; } 2>&1 | decor "$full" "*" >&4; } 4>&1 | cat -u >&3; } 5>&1 &- case $thisrc in 0) ;; *) echo failed rc=$thisrc >$stat; rc=$thisrc case $keepon in nil) break ;; esac ;; esac buildopt=--no-arch-all done exit $rc ;; build,*) echo >&2 "usage: $prog build BUILDDIR"; exit 1 ;; *) fail "unknown command \`$1'" ;; esac ###----- That's all, folks --------------------------------------------------