#!/bin/bash
#
# pbuilder-ssh - builds Debian packages by calling pbuilder on remote
#                machines. Source package upload and retrieval of build results
#                is handled automatically. It also supports backport
#                version mangling.
#
# Copyright (C) 2006-2007 by
# Michael Hanke        michael.hanke@gmail.com
#
# 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.
#
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ###
# SVN version control block - do not edit manually
PBSSH_SVN_ID='$Id: pbuilder-ssh 155 2007-01-12 07:59:29Z mhanke-guest $'
PBSSH_SVN_REV='$Rev: 155 $'
PBSSH_SVN_DATE='$Date: 2007-01-12 08:59:29 +0100 (Fr, 12 Jan 2007) $'
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ###

# enables extglob for RE pattern matching
shopt -s extglob

############
# Defaults #
############

# pbuilder-ssh version
pbssh_version=0.4
# backport target; could be a distname.
# Default standard www.backports.org suffix
backport_targetname=${PBSSH_BACKPORT_TARGETNAME:-bpo}
# default version suffix for backports
backport_version=${PBSSH_BACKPORT_VERSION:-1}
# do not apply backport patches
apply_backport_patches_flag=1
# whether backporting is attempted
backport_flag=0
# flag whether to include the dist name in the version string
# of a backport (will replace previous $backport_targetname
dist_in_version_flag=0
# default pbuilder command
pbuilder_cmd=${PBSSH_PBUILDER_CMD:-pbuilder}
# default command to escalate priviliges, eg sudo
escalate_cmd=${PBSSH_ESCALATE_CMD:-}
# this command is used to export the package source tree from the SVN
# working copy
svn_export_cmd=${PBSSH_SVN_EXPORT_CMD:-svn-buildpackage --svn-export --svn-ignore-new}
# username that is used to perform all non-privileged operations on
# the build-machines
build_user=$USER
# username to perform privileged operation on the build-machines
build_superuser=root
# local path where the results pbuilder should endup
build_results_path=
# update basetgz before build
update_basetgz_flag=0
# pbuilder config file
pbuilderrc=${HOME}/.pbuilderrc
# path on the buildmachine where pbuilder puts the results
pbuilder_result_path=/var/cache/pbuilder/result
# wrap the pbuilder call with the linux32 command to support
# i386 builds on amd64
pbuilder_linux32_flag=0
# set the pbuilder binary-arch option
binary_arch_flag=0
# produce fancy colored output
color_output_flag=0
# patchlist filename
patch_list=
# processing a native Debian package
native_pkg_flag=0
# auto-generate a pbuilder A-hook for an interactive shell
prebuild_login_flag=0
# auto-generate a pbuilder C-hook for an interactive shell
buildfailure_login_flag=0
# to-be-uploaded-hookdir path
pbssh_hookdir=
# directory content is up- and downloaded and bindmounted into
# the build chroot
pbssh_exchangedir=
# enables addtional status message if set to true
verbose_flag=0

##############################
# Source configuration files #
##############################

# source config files
if [ -f /etc/pbuilder-ssh.rc ]; then
    . /etc/pbuilder-ssh.conf
fi

# source user config files
if [ -f $HOME/.pbuilder-ssh.rc ]; then
    . $HOME/.pbuilder-ssh.rc
fi

#########################
# Some helper functions #
#########################

# cleanup function
clean_pbssh_upload_path()
{
    if [ -d "$pbssh_upload_path" ]; then
		if [ $verbose_flag == 1 ]; then
			printf "${yellow}Remove temporary working dir on local machine.\n${NC}"
		fi
    	rm -r $pbssh_upload_path
	fi
}

get_version_info()
{
    if [ ! -f $srcpkg_export_path/debian/changelog ]; then
        printf "${Red}Internal logic error. No debian/changelog in exported source tree available. This is a bug, sorry!\n${NC}"
        exit 200
    fi

    # get versions from debian/changelog
    srcpkg_name=$(dpkg-parsechangelog -l$srcpkg_export_path/debian/changelog | awk '/^Source:/{ print $2 }')
    full_version=$(dpkg-parsechangelog -l$srcpkg_export_path/debian/changelog | awk '/^Version:/{ print $2 }')
    upstream_version=${full_version%-*}

    # check for native packages
    if [ "$upstream_version" == "$full_version" ]; then
        deb_version=
        native_pkg_flag=1
		if [ $verbose_flag == 1 ]; then
        	printf "${yellow}Processing native Debian package.\n${NC}"
		fi
    else
        deb_version=${full_version##*-}
        native_pkg_flag=0
    fi

	orig_src_file=$(ls -1 $pbssh_upload_path/${srcpkg_name}_${upstream_version}*.tar.gz)
    
	if [ ! -e $orig_src_file ]; then
       	printf "${yellow}No upstream source tarball '$orig_src_file' found.\n${NC}"
		orig_src_file=
	fi
}


finalize_buildmachine()
{
	if [ $pbssh_exchangedir ]; then
		printf "${Yellow}Download exchange directory content.\n${NC}"
		scp -r ${build_user}@${build_machine}:$foreign_src_dir/exchange/* $pbssh_exchangedir
	fi

	if [ $verbose_flag == 1 ]; then
    	printf "${yellow}Clean temporary working directory on build machine.\n${NC}"
	fi
    ssh -l ${build_user} ${build_machine} rm -rf $foreign_src_dir
}

get_build_results_path()
{
    if [ -z "$build_results_path" ]; then
        if [ $svn_wcopy_flag == 1 ]; then
            if [ "$srcpkg_path" == "$(pwd)" ]; then
                build_results_path="$srcpkg_path/.."
            else
                build_results_path=$(pwd)
            fi
        fi
        if [ $dsc_file_flag == 1 ]; then
            build_results_path="$(dirname $srcpkg_path)"
        fi
    fi
}

print_version()
{
cat << EOT
pbuilder-ssh $pbssh_version

Copyright (C) 2006 Michael Hanke <michael.hanke@gmail.com>

Licensed under GNU Public License version 2 or later.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

EOT
}

print_short_description()
{
cat << EOT
pbuilder-ssh builds Debian packages by calling pbuilder on remote machines via
ssh. Besides Debian source packages it also handles exporting from
svn-buildpackage working copies. Source package upload and retrieval of build
results are handled automatically.

pbuilder-ssh aids backporting by appropriately mangling the version before 
uploading the package to the build machine. It supports distribution
specific patches in the source packages and applies them automatically if
present.

EOT
}

print_additional_description()
{
cat << EOT
Distribution specific patches can be included in a Debian source package by 
putting them into "debian/backports/". When pbuilder-ssh is called with 
--backport-distribution "somedist" it will look for a file at 
"debian/backports/00list.somedist". This is a textfile that lists patches 
(one per line) that will be applied in order of appearance. The name of each
applied patch is listed in a new changelog entry for the backport.

Best practice:

pbuilder-ssh requires an installation of pbuilder on a machine that is 
accessible via ssh. For usability reasons ssh should be setup for 
password-less access (otherwise the password has to be entered multiple times
during a single run).

The easiest way to use pbuilder-ssh is to provide it with a pbuilder config 
file. First, one modifies the default pbuilder configuration on the remote 
machine to match all relevant requirements (distribution, additional mirrors).
Second, pbuilders create command is called with this configuration to create 
the base.tgz (perhaps using a different name to support multiple 
distributions). Third, the configuration is downloaded to the local machine.
Now this config file is fed to pbuilder-ssh using the --configfile option on 
each call. pbuilder-ssh will extract all relevant information from that file, 
reuploads it to the build machine and passes it to pbuilder.

EOT
}

print_help()
{
cat << EOT
Usage:  pbuilder-ssh [OPTIONS] <dsc-file|svn-buildpkg-wcopy> [<buildmachine>]

Options:
-h, --help
  Print short description and usage summary.

--verbose-help
  Print more verbose description than --help.

--version
  Print version information and exit.

--verbose
  Enable addtional status messages.

--configfile <file>
  Read custom parameters from a given file. This file will be sourced after
  global (/etc/pbuilder-ssh.conf) and user configuration 
  (~/.pbuilder-ssh.conf).

-c <file>, --pbuilder-configfile <file>
  Read pbuilder configuration from a local file. This should be a valid
  pbuilderc file. The file will get uploaded to the buildmachine and is fed
  to pbuilder using its --configfile option.

-b "<options>", --debbuildopts "<options>"
  Options for dpkg-buildpackage. This is passed to pbuilder using its
  --debbuildopts option.

-o "<options>", --pbuilder-opts "<options>"
  Use this to pass arbitrary options to pbuilder.

-a, --binary-arch
  Convenience option to set pbuilder's --binary-arch option. The same can be
  achieved with: --pbuilder-option "--binary-arch"

-r <path>, --buildresult <path>
  Path where to buildresults shall be stored on the local machine. By default
  this will be the directory containing the (packed) source package or the
  path containing the root directory of the svn-buildpackage working copy.

-e <command>, --escalate-cmd <command>
  Command to escalate user permissions (e.g. sudo, empty by default).

-p <command>, --pbuilder-cmd <command>
  Command to run pbuilder (default pbuilder, don't define to "sudo pbuilder",
  use --escalate-cmd option instead). Default: $pbuilder_cmd

--pbuilder-buildresult <path>
  Path where pbuilder should store the buildresults on the foreign machine.
  Set this if no pbuilder configuration containing this information is
  uploaded to the buildmachine (--pbuilder-configfile). If nothing is
  specified $pbuilder_result_path (pbuilder default) is assumed.

--update-basetgz
  If set pbuilder will perform an update of the base.tgz of the desired
  distribution before attempting to build the package.

-u <username>, --build-user <username>
  User that is used to perform non-priveleged operations on the buildmachine.
  Defaults to the user that is running pbuilder-ssh.

-s <username>, --build-superuser <username>
  User that is used to invoke pbuilder on the buildmachine. Default:
  $build_superuser

-d <distname>, --backport-distribution <distname>
  Setting this option to some arbitrary distribution name will add a new
  changelog entry for the backport version. The Debian package version is
  decreased by one to allow for smooth upgrades to the nonbackported package.
  By default 'bpo' plus some backport version is appended to generate a version
  string as requested by backports.org. See --distversion if you want 
  distribution specific versions strings or --backport-targetname for complete 
  customization.

--backport-targetname <string>
  Override the default version suffix of backports by this string. Use this if 
  you neither like the default 'bpo' nor the distribution name you would get by 
  setting --distversion

-v <string>, --backport-version <string>
  String that will be appended to the backport-version.

--linux32
  Wrap the pbuilder call on the buildmachine with the linux32 command. The
  make it possible to build packages for the i386 arch on an amd64 system.

--distversion
  If set the name of the target distribution of a backport will be included
  in the version string (e.g. x.y.z-Xsarge1). Default is to generate a version
  string suiteable for www.backports.org (e.g. x.y.z-Xbpo1).

--colored
  If set pbuilder-ssh will colorize its status message on the console.
  Default: off

--no-backport-patches
  If set pbuilder-ssh will not apply distribution specific patches for
  backports contained in the source package.

--patchlist <filename>
  Applies all patches listed in <filename> to the source package. The patches
  are applied after any optional backport patches contained in the source
  package were applied. A changelog entry with the filenames of all patches
  is automatically appended.

--prebuild-login
  If set pbuilder-ssh provides an interactive shell inside the chroot on the
  build machine. The chroot will contain the unpacked source package and has all 
  build dependencies installed. This corresponds to (and uses) a pbuilder A-hook 
  (more precisely the A10shell hook).

--buildfailure-login
  If set pbuilder-ssh provides an interactive shell inside the chroot on the
  build machine in case that building the package fails. This can be used to 
  examine the build failure.
  This corresponds to (and uses) a pbuilder C-hook (more precisely the C10shell 
  hook).

--hookdir <path>
  The content of this directory is uploaded to the build machine and is passed
  to pbuilder using its --hookdir option. These custom hooks might be 
  overridden by hooks provided by --prebuild-login and --buildfailure-login.
  Note, that this option could also be abused to upload additional stuff to the 
  build machine (see --exchangedir). 

--exchangedir <path>
  The content of this path is uploaded to the build machine and mounted into 
  the build chroot via pbuilders --bindmounts option. After pbuilder has 
  finished the content of this directory is downloaded to the local machine.
  This can be used to get access to e.g. patches created with any of the 
  interactive shells inside the build chroot.

EOT
}

################################
# Commandline options handling #
################################

# Parse commandline options (taken from the getopt examples from the Debian util-linux package)
# Note that we use `"$@"' to let each command-line parameter expand to a
# separate word. The quotes around `$@' are essential!
# We need CLOPTS as the `eval set --' would nuke the return value of getopt.
CLOPTS=`getopt -o h,a,c:,b:,e:,p:,o:,d:,v:,u:,s:,r: --long help,configfile:,patchlist:,version,backport-distribution:,backport-version:,build-user:,build-superuser:,buildresult:,debbuildopts:,escalate-cmd:,pbuilder-opts:,pbuilder-cmd:,pbuilder-buildresult:,pbuilder-configfile:,update-basetgz,linux32,binary-arch,distversion,colored,no-backport-patches,prebuild-login,backport-targetname:,hookdir:,buildfailure-login,exchangedir:,verbose,verbose-help -n 'pbuilder-ssh' -- "$@"`

if [ $? != 0 ] ; then
  echo "Terminating..." >&2
  exit 1
fi

# Note the quotes around `$CLOPTS': they are essential!
eval set -- "$CLOPTS"

while true ; do
  case "$1" in
	  --configfile) shift;
		  [ ! -f $1 ] && printf "${Red}Can't find $1 file.${NC}\n" && exit 1;
		  . $1; shift;;
	  --patchlist) shift;
		  [ ! -f $1 ] && printf "${Red}Can't find $1 file.${NC}\n" && exit 1;
		  patch_list=`readlink -f $1`; shift;;
	  -d|--backport-distribution) shift; build_dist=$1; backport_flag=1; shift;;
	  -v|--backport-version) shift; backport_version=$1; shift;;
	  --backport-targetname) shift; backport_targetname=$1; shift;;
	  -u|--build-user) shift; build_user=$1; shift;;
	  -s|--build-superuser) shift; build_superuser=$1; shift;;
	  -r|--buildresult) shift; build_results_path=$1; shift;;
	  -b|--debbuildopts) shift; pbuilder_debbuildopts=$1; shift;;
	  -e|--escalate-cmd) shift; escalate_cmd=$1; shift;;
	  -p|--pbuilder-cmd) shift; pbuilder_cmd=$1; shift;;
	  -o|--pbuilder-opts) shift; pbuilder_opts=$1; shift;;
	  --pbuilder-buildresult) shift; pbuilder_result_path=$1; shift;;
	  -c|--pbuilder-configfile) shift; 
		  [ ! -f $1 ] && printf "${Red}Can't find $1 file.${NC}\n" && exit 1;
		  pbuilderrc=$1; shift;;
	  --hookdir) shift; 
		  [ ! -d $1 ] && printf "${Red}Can't find directory '$1'.${NC}\n" && exit 1;
		  pbssh_hookdir=$1; shift;;
	  --exchangedir) shift; 
		  [ ! -d $1 ] && printf "${Red}Can't find directory '$1'.${NC}\n" && exit 1;
		  pbssh_exchangedir=$1; shift;;
	  --update-basetgz) update_basetgz_flag=1; shift;;
	  --prebuild-login) prebuild_login_flag=1; shift;;
	  --buildfailure-login) bauil_failure_login_flag=1; shift;;
	  --linux32) pbuilder_linux32_flag=1; shift;;
	  -a|--binary-arch) binary_arch_flag=1; shift;;
	  --distversion) dist_in_version_flag=1; shift;;
	  --no-backport-patches) apply_backport_patches_flag=0; shift;;
	  --colored) color_output_flag=1; shift;;
	  --verbose) verbose_flag=1; shift;;
	  -h|--help) print_short_description; print_help; exit 0;;
	  --verbose-help) print_short_description; print_additional_description; print_help; exit 0;;
	  --version) print_version; exit 0;;
	  --) shift ; break ;;
	  *) echo "Internal error! ($1)"; exit 1;;
  esac
done

if [ $# -gt 2 ]; then
  printf "Too many arguments: $@.\n\n"
  print_help
  exit 1
fi

# get build target
srcpkg_path=$1
build_machine=$2

if [ -z "$build_machine" ]; then
	build_machine="localhost"
fi

#################
# Sanity checks #
#################


if [ -z "$srcpkg_path" ]; then
	# yoh: I think it is better just to assume current directory by default
	srcpkg_path="$PWD"
	if [ $verbose_flag == 1 ]; then
		printf "${yellow}No source package specified: using current directory '$srcpkg_path'.${NC}\n"
	fi
fi

if [ ! -e "$srcpkg_path" ]; then
  printf "${Red}'$srcpkg_path' is not a valid source package.${NC}\n"
  exit 1
fi

# get pbuilder result path from config file if present
if [ -f "$pbuilderrc" ]; then

  # get pbuilder result path from pbuilderrc if set
  eval $(grep "BUILDRESULT" $pbuilderrc)

  if [ -n "$BUILDRESULT" ]; then
	  pbuilder_result_path=$BUILDRESULT
  fi
else
# clear pbuilderrc to prevent errors when uploading a non-existing file
  pbuilderrc=
fi

# colorful output requested?
if [ $color_output_flag = 1 ]; then
  black='\e[0;30m'
  Black='\e[1;30m'
  red='\e[0;31m'
  Red='\e[1;31m'
  green='\e[0;32m'
  Green='\e[1;32m'
  yellow='\e[0;33m'
  Yellow='\e[1;33m'
  blue='\e[0;34m'
  Blue='\e[1;34m'
  cyan='\e[0;36m'
  Cyan='\e[1;36m'
  white='\e[0;37m'
  White='\e[1;37m'
  NC='\e[0m' #no color
fi

##########################################################
# Prepare the source package for upload to build machine #
##########################################################

if [ -f $srcpkg_path/.svn/deb-layout ]; then
  if [ $verbose_flag == 1 ]; then
    printf "${Yellow}Using package source from svn-buildpackage working copy.${NC}\n"
  fi
  svn_wcopy_flag=1
else
  svn_wcopy_flag=0
fi

if [ "${srcpkg_path##*.}" == "dsc" ]; then
  if [ $verbose_flag == 1 ]; then
    printf "${Yellow}Using Debian source package from '$srcpkg_path'.${NC}\n"
  fi
  dsc_file_flag=1
else
  dsc_file_flag=0
fi

if [ $dsc_file_flag == 0 -a $svn_wcopy_flag == 0 ]; then
  printf "${Red}'$srcpkg_path' is neither a Debian source package nor a svn-buildpackage working copy.\n${NC}"
  exit 1
fi

get_build_results_path

###############################################################
# Create path that will contain everything that gets uploaded #
# to the build machine.                                       # 
###############################################################

pbssh_upload_path=$(mktemp -d -t pbuilder-ssh.XXXXXX)
if [ $verbose_flag == 1 ]; then
	printf "${yellow}Using temporary working dir '$pbssh_upload_path'.\n${NC}"
fi

# add pbuilder config file to upload path
if [ -f "$pbuilderrc" ]; then
  cp $pbuilderrc $pbssh_upload_path
fi

mkdir $pbssh_upload_path/hookdir

# copy the hookdir content into the upload path
if [ "$pbssh_hookdir" ]; then 
	cp -r $pbssh_hookdir/* $pbssh_upload_path/hookdir
fi

# copy the exchangedir content into the upload path
if [ "$pbssh_exchangedir" ]; then 
	mkdir $pbssh_upload_path/exchange
	cp -r $pbssh_exchangedir/* $pbssh_upload_path/exchange
fi

##############################################
# Export the source tree if necessary.       #
# (backporting, repackaging, patches, etc... #
##############################################

if [[ $backport_flag == 1 || $svn_wcopy_flag == 1 || "$patch_list" != "" ]]; then
	if [ $verbose_flag == 1 ]; then
   		printf "${Yellow}Exporting source tree ...\n${NC}"
	fi

    # handle export from svn wcopy
    if [ $svn_wcopy_flag == 1 ]; then
        srcpkg_export_path=`LC=C cd $srcpkg_path && $svn_export_cmd --svn-override buildArea=$pbssh_upload_path | tail -n1`
        srcpkg_export_path=${srcpkg_export_path##* }

        if [ ! -d $srcpkg_export_path ]; then
            printf "${Red}SVN export via '$svn_export_cmd' failed.\n${NC}"
            clean_srcpkg_build_path
            exit 1
        fi
    fi

    # handle export from source package
    if [ $dsc_file_flag == 1 ]; then
        srcpkg_export_path="$pbssh_upload_path/pkg_src_tree"
        dpkg-source -x $srcpkg_path $srcpkg_export_path

        if [ "$?" != 0 ]; then
            printf "${Red}Extracting source package from '$srcpkg_path' failed.\n${NC}"
            clean_pbssh_upload_path
            exit 1
        fi

        orig_src_file=$(awk '/\.tar\.gz$/{ print $3 }' $srcpkg_path)
        cp $(dirname $srcpkg_path)/$orig_src_file $pbssh_upload_path
    fi

    # sanity test
    if [ -z "$srcpkg_export_path" ]; then
        printf "${Red}Internal logic error. No exported source tree available. This is a bug, sorry!\n${NC}"
        exit 200
    fi
	
	if [ $verbose_flag == 1 ]; then
    	printf "${Yellow}Exported package source tree to '$srcpkg_export_path'.\n${NC}"
	fi

    # read version info from changelog
    get_version_info

    # handle version mangling and backports patches
    if [ $backport_flag == 1 ]; then

        if [ $apply_backport_patches_flag == 1 ]; then
            # looking for dist specific patches
            dist_patchlist="$srcpkg_export_path/debian/backports/00list.${build_dist}"

            if [ -f "$dist_patchlist" ]; then
				if [ $verbose_flag == 1 ]; then
                	printf "${Yellow}Found patch list for '$build_dist' backport.\n${NC}"
				fi

                dist_patches=$(egrep "^[^#]" $dist_patchlist)

                for patch in $dist_patches; do
                    printf "${Yellow}Applying '$patch'.\n${NC}"
                    patch -p1 -d $srcpkg_export_path -i $srcpkg_export_path/debian/backports/${patch}

                    if [ $? != 0 ]; then
                        printf "${Red}Applying patches for '$build_dist' distribution failed. Exit!\n${NC}"
                        clean_pbssh_upload_path
                        exit 1;
                    fi
                done
            fi
        fi

        # construct backport version string
        if [ $dist_in_version_flag == 1 ]; then
            backport_targetname=${build_dist}
        fi

		###########################################################
		# The next block is obsolete because DAK now supports '~' #
		###########################################################

		## if native Debian package
		#if [ $native_pkg_flag == 1 ]; then
		#    version_tmp=$upstream_version
		#else
		#    version_tmp=$deb_version
		#fi
		#
		#ver_substr=${version_tmp##*+([!1-9])}
		#dec_ver_substr=$(($ver_substr - 1))
		#base_ver_substr=${version_tmp%$ver_substr}
		#dist_version=${base_ver_substr}${dec_ver_substr}
		#
		#printf "${Yellow}Detected version is '$version_tmp' --> decrease to '$dist_version'\n${NC}"
		#
		## if native Debian package
		#if [ $native_pkg_flag == 1 ]; then
		#    dist_deb_version="${dist_version}${backport_targetname}${backport_version}"
		#else
		#    dist_deb_version="$upstream_version-${dist_version}${backport_targetname}${backport_version}"
		#fi
		#
		
		dist_deb_version="${full_version}~${backport_targetname}${backport_version}"
        
		if [ $verbose_flag == 1 ]; then
			printf "${Yellow}Backport package version is: '$dist_deb_version'.\n"
		fi

        # create default changelog entry
        if [ -z "$changelog_entry" ]; then
            changelog_entry="Backported build of Debian package version $full_version by pbuilder-ssh (v.$pbssh_version)."
        fi

		# echo necessary to dismiss debchange warning 
        echo | debchange --noconf --force-bad-version -c $srcpkg_export_path/debian/changelog -D $build_dist \
            -b -v $dist_deb_version $changelog_entry

        if [ "$dist_patches" ]; then
            debchange --noconf -c $srcpkg_export_path/debian/changelog \
                "The following patches were applied for this ${build_dist} backport: $dist_patches."
        fi
    fi

    if [ -f "$patch_list" ]; then
		if [ $verbose_flag == 1 ]; then
        	printf "${Yellow}Parsing provided patch list from '$patch_list'.\n${NC}"
		fi

        patches=$(egrep "^[^#]" $patch_list)
        patches_dir=$(dirname $patch_list)

        for patch in $patches; do
			if [ $verbose_flag == 1 ]; then
            	printf "${Yellow}Applying '$patch'.\n${NC}"
			fi

            patch -p1 -d $srcpkg_export_path -i ${patches_dir}/${patch}

            if [ $? != 0 ]; then
                printf "${Red}Applying patch from '${patches_dir}/${patch}' failed. Exit!\n${NC}"
                clean_pbssh_upload_path
                exit 1;
            fi
        done

        debchange --noconf -c $srcpkg_export_path/debian/changelog \
            "The following additional patches were applied by pbuilder-ssh: $patches."
    fi

    # repack source package
    printf "${Yellow}Build source package in '$pbssh_upload_path'.\n${NC}"

    # if native Debian package
    if [ $native_pkg_flag == 1 ]; then
        # clean old sources
        rm -f $orig_src_file
        bash -c "cd $pbssh_upload_path && dpkg-source -sn -b $(basename $srcpkg_export_path)"
		orig_src_file=$(ls -1 $pbssh_upload_path/${srcpkg_name}_${upstream_version}*.tar.gz)
    else
        bash -c "cd $pbssh_upload_path && dpkg-source -b $(basename $srcpkg_export_path) $(basename $orig_src_file)"
    fi

    if [ $? != 0 ]; then
        printf "${Red}Building source package failed. Exit!\n${NC}"; clean_pbssh_upload_path; exit 1;
    fi

    # clean exported source tree
    rm -rf $srcpkg_export_path

    # done rebuilding the source package
	#    dsc_file="$(ls -1 $srcpkg_build_path/*.dsc)"
	#    orig_src_file="$(ls -1 $srcpkg_build_path/*.tar.gz)"

	#if [ $native_pkg_flag != 1 ]; then
	#	diff_file="$(ls -1 $srcpkg_build_path/*.diff.gz)"
	#fi

else # simple rebuilding of a source package is requested
	dsc_file=$srcpkg_path
    orig_src_file=$(dirname $srcpkg_path)/$(awk '/\.tar\.gz$/{ print $3 }' $srcpkg_path)
	
	# check for native package
	if [ -z "$(awk '/\.diff\.gz/' $srcpkg_path)" ]; then
    	diff_file=
		native_pkg_flag=1
	else
		diff_file=$(dirname $srcpkg_path)/$(awk '/\.diff\.gz/{ print $3 }' $srcpkg_path)
	fi

	cp $dsc_file $diff_file $orig_src_file $pbssh_upload_path
fi


##########################################
# Upload everything to the build machine #
##########################################

# create working dir on build machine
foreign_src_dir=`ssh -l ${build_user} ${build_machine} "mktemp -d -t pbuilder-ssh.XXXXXX"`

if [ $? != 0 ]; then
	printf "${Red}Could not create temporary working directory on buildmachine.\n${NC}"
	clean_pbssh_upload_path
	exit 1
fi

# add hooks for interactive shells
if [ $prebuild_login_flag == 1 ]; then
	cat << EOT > $pbssh_upload_path/hookdir/A10shell
#!/bin/sh
printf "\n\nThis is an interactive shell inside the build chroot.\n"
printf "Location of the source package: '/tmp/buildd/'\n"
if [ -d "$foreign_src_dir/exchange" ]; then
	printf "Bind-mounted exchange dir: '$foreign_src_dir/exchange'\n"
fi
printf "To prevent pbuilder from building the source package exit this shell with\na return value different from zero (e.g. 'exit 1').\n"
sh < /dev/tty > /dev/tty 
	
EOT
	chmod +x $pbssh_upload_path/hookdir/A10shell
fi

if [ $buildfailure_login_flag == 1 ]; then
	cat << EOT > $pbssh_upload_path/hookdir/C10shell
#!/bin/sh
printf "\n\nThis is an interactive shell inside the build chroot.\n"
printf "Location of the source package: '/tmp/buildd/'\n"
if [ -d "$foreign_src_dir/exchange" ]; then
	printf "Bind-mounted exchange dir: '$foreign_src_dir/exchange'\n"
fi
sh < /dev/tty > /dev/tty 
	
EOT
	chmod +x $pbssh_upload_path/hookdir/C10shell
fi


# get the name of the dsc file
dsc_file=$(ls -1 $pbssh_upload_path/*.dsc)
dsc_file=$(basename $dsc_file)

printf "${Yellow}Upload source package and pbuilder config to '$build_machine' ($foreign_src_dir).\n${NC}"
scp -r $pbssh_upload_path/* ${build_user}@${build_machine}:$foreign_src_dir
printf "${Yellow}Done uploading source package to build machine.\n${NC}"

#################################
# Clean local working directory #
#################################

clean_pbssh_upload_path

###########################################################
# pbuilder cmd (with support for 32bit on 64bit machines) #
###########################################################

if [ $pbuilder_linux32_flag = 1 ]; then
    pbuilder_cmd="linux32 ${pbuilder_cmd}"
fi

##########################
# Build pbuilder options #
##########################

pbuilder_options=

if [ -n "$pbuilderrc" ]; then
    pbuilder_options="--configfile $foreign_src_dir/$(basename $pbuilderrc)"
fi

if [ -n "$pbuilder_debbuildopts" ]; then
    pbuilder_options="$pbuilder_options --debbuildopts \"$pbuilder_debbuildopts\""
fi

if [ -n "$pbuilder_result_path" ]; then
    pbuilder_options="$pbuilder_options --buildresult $pbuilder_result_path"
fi

if [ $binary_arch_flag = 1 ]; then
    pbuilder_opts="$pbuilder_opts --binary-arch"
fi

if [ $pbssh_exchangedir ]; then
    pbuilder_opts="$pbuilder_opts --bindmounts $foreign_src_dir/exchange"
	printf "${Yellow}Exchange directory is available in '$foreign_src_dir/exchange' inside the build chroot.${NC}\n"
fi

# and now all together (always point to the uploaded hookdir)
pbuilder_options="$pbuilder_options $pbuilder_opts --hookdir $foreign_src_dir/hookdir"

if [ $verbose_flag == 1 ]; then
	printf "${yellow}Setting pbuilder options: $pbuilder_options\n${NC}"
fi

########################################
# Update pbuilder basetgz if requested #
########################################

if [ $update_basetgz_flag == 1 ]; then
    printf "${Yellow}Invoking pbuilder update on '$build_machine' ...\n${NC}"

    ssh -l ${build_superuser} ${build_machine} ${escalate_cmd} ${pbuilder_cmd} update $pbuilder_options

    if [ $? != 0 ]; then
        printf "${Red}Updating pbuilder basetgz  on '${build_machine}' failed!\n${NC}"
        exit 1
    fi

fi

###########################################
# Build uploaded package on build machine #
###########################################

# build the packages with pbuilder
printf "${Yellow}Invoking pbuilder on '$build_machine' ...\n${NC}"

echo "ssh -l ${build_superuser} ${build_machine} ${escalate_cmd} ${pbuilder_cmd} build $pbuilder_options $foreign_src_dir/$dsc_file)"
# add -t to enable interactive shells
ssh -t -l ${build_superuser} ${build_machine} ${escalate_cmd} ${pbuilder_cmd} build \
    $pbuilder_options $foreign_src_dir/$dsc_file

if [ $? != 0 ]; then
    printf "${Red}Building on '${build_machine}' failed! Giving up.\n${NC}"
    finalize_buildmachine
    exit 1
fi

printf "${Green}Build was completed sucessfully.\n${NC}"

#########################
# Download buildresults #
#########################

# figure out the name of the changes file of the build
filename_stub=$dsc_file
filename_stub="$pbuilder_result_path/${filename_stub%.dsc}"
chgs_file_id="${filename_stub}_*.changes"
diff_file_id="${filename_stub}.diff.gz"
dsc_file_id="${filename_stub}.dsc"

# generate the list of to be downloaded files
bpkg_temp_list=$(ssh -l ${build_user} ${build_machine} "awk '/(.deb|.diff.gz|.dsc)$/{ print \$5 }' $chgs_file_id")
for p in $bpkg_temp_list; do
    bpkg_list="$bpkg_list $pbuilder_result_path/$p"
done
bpkg_list="$bpkg_list $chgs_file_id"

# need to download src tarball of a native package
# because it will be modified by backporting
native_orig_src=
if [ $native_pkg_flag == 1 ]; then
  native_orig_src=$pbuilder_result_path/$(basename $orig_src_file)
fi

printf "${Yellow}Download binary packages from build machine.\n${NC}"
scp ${build_user}@${build_machine}:"$bpkg_list $native_orig_src" $build_results_path

# include diff and dsc file for cleanup (needed for binary-arch-only build
# where those are not listed on the changes file
bpkg_list="$bpkg_list $diff_file_id $dsc_file_id"

#cleanup build results
if [ $verbose_flag == 1 ]; then
	printf "${yellow}Clean build results on remote machine.\n${NC}"
fi
ssh -l ${build_superuser} ${build_machine} ${escalate_cmd} rm -f $bpkg_list \
    $pbuilder_result_path/$(basename $orig_src_file)

finalize_buildmachine

if [ $verbose_flag == 1 ]; then
	printf "${Green}Finished building on '${build_machine}'.\n${NC}"
fi

exit 0
