#!/bin/zsh

set -x
set -e
set -u
setopt null_glob

export DEB_BUILD_OPTIONS=parallel=4
export DH_VERBOSE=1

gcc_dir="/home/dkogan/debianstuff/gcc"
cross_gcc_dir="/home/dkogan/debianstuff/cross-gcc"
build_subdir="build"

# for git commits and debian/changelog entries written by dch
export DEBEMAIL=dima@secretsauce.net
export DEBFULLNAME="${0:t} script by Dima Kogan"
export DEBSIGN_KEYID=dima@secretsauce.net

typeset -a arches
arches=($(cat $cross_gcc_dir/debian/targetlist))



function get_cross_gcc_dev_latest_pkgfile
{
    echo ${cross_gcc_dir}/${build_subdir}/cross-gcc-dev_*_all.deb(om[1])
}

function get_cross_gcc_dev_latest_pkgfile_version
{
    local filename=$(get_cross_gcc_dev_latest_pkgfile)
    [ -z ${filename:-} ] && return 0;

    filename=${filename:t}
    [ -z $filename ] && return 0;

    echo $filename | awk -F_ '{print $2}'
    true
}

function get_triplet
{
    dpkg-architecture -a${arch} -qDEB_HOST_GNU_TYPE -f 2>/dev/null
}

function latest_available_version
{
    rmadison -u debian $* | tac | awk -F' *\\| *' '$2 {print $2; exit}'
}

function gcc_debian_release_for_SHA
{
    local sha=$1

    pushd $gcc_dir
    local tag=`git describe --abbrev=0 $sha`
    if [ -z $tag ]; then
        echo "Error: couldn't get debian release of sha $sha"
        exit 1
    fi

    # tag is debian/version. I strip out the "debian/" and print
    echo ${tag/debian\//}
    popd
}

function patches_are_up_to_date
{
    local version=$1

    local first_patch=`awk '{print $2; exit}' $cross_gcc_dir/patches/0001*`
    if [ -z $first_patch ]; then
        echo "Error: couldn't get SHA of first patch"
        exit 1
    fi

    local patches_at_release=$(gcc_debian_release_for_SHA $first_patch)


    if dpkg --compare-versions $version '>>' $patches_at_release; then
        echo "Patches are out of date; need updating"
        return 1 # patches out of date
    elif dpkg --compare-versions $version '<<' $patches_at_release; then
        echo "Patches live in the future... Giving up"
        exit 1;
    else
        echo "Patches are up-to-date"
        return 0
    fi

    true
}

function update_patches
{

    local version=$1

    patches_are_up_to_date $version && return 0

    # ok. patches are out of date, so we need to update them
    pushd

    # import new sources, if needed
    local version_in_tree=$(gcc_debian_release_for_SHA master)
    cd $gcc_dir
    if dpkg --compare-versions $version '>>' $version_in_tree; then

        echo "gcc tree needs to import the latest sources"

        sudo apt-get update
        gbp import-dsc --download gcc-4.9-source

        # make sure the sources are actually new (APT server maybe isn't in
        # sync)
        local version_imported=$(gcc_debian_release_for_SHA master)
        if [ $version_imported != $version ]; then
            echo "Imported source version '$version_imported' does not match build version '$version'. Giving up"
            exit 1
        fi

    elif dpkg --compare-versions $version '<<' $version_in_tree; then
        echo "gcc tree lives in the future... Giving up"
        exit 1;
    else
        echo "git tree is up-to date. Not importing anything."
    fi


    # I need to update the git tree. I
    # - update the patches to match the new source tree
    # - update debian/changelog
    # - update debian/control.in to require the most recent gcc sources.
    #   This is required because even though rmadison may see a package, it
    #   may not have made it to the repository yet.
    git checkout patches
    git rebase master # this may fail if the patches do not apply cleanly

    git branch patches-$version

    cd $cross_gcc_dir
    git rm patches/*
    mkdir patches

    cd $gcc_dir
    git format-patch -o "$cross_gcc_dir/patches" master

    cd "$cross_gcc_dir/patches"
    ls *.patch > series
    git add *
    git commit --author "$DEBFULLNAME <$DEBEMAIL>" -m 'patch update'

    cd $cross_gcc_dir
    export version
    perl -p -i -e 'if(/Build-Depends:/)    { $saw_bd = 1; }
                   elsif($saw_bd && !/^ /) { $saw_bd = undef; }

                   if($saw_bd) {s/(gcc-\w*-source) *\(.*?\)/\1 (= $ENV{version})/g}' \
         template/debian/control.template
    git add template/debian/control.template
    git commit --author "$DEBFULLNAME <$DEBEMAIL>" -m "forcing build of gcc-4.9-source=$version" template/debian/control.template

    dch --no-auto-nmu "rebuild for $version"
    dch --no-auto-nmu -r ''
    git commit --author "$DEBFULLNAME <$DEBEMAIL>" -m 'version bump' debian/changelog


    # git push --all
    # git push origin +patches:patches
    # push both gcc and cross trees


    popd
    true
}

function build_cross_gcc_dev
{
    local version=$1

    echo "===== building cross-gcc-dev"

    if update_patches $version; then ; else
        echo "Patch update failed. Need human intervention. Giving up"
        exit 1
    fi


    # We may have updated the cross-gcc-dev tree by updating the patches and the
    # changelog. If a new .deb needs to be built, we do it
    local cross_gcc_dev_version_cur=$(dpkg-parsechangelog --show-field=Version)
    local cross_gcc_dev_version_pkg=$(get_cross_gcc_dev_latest_pkgfile_version)

    if [[ -n "$cross_gcc_dev_version_cur" && $cross_gcc_dev_version_pkg = $cross_gcc_dev_version_cur ]]; then
        echo "cross-gcc-dev package up-to-date. Version $cross_gcc_dev_version_pkg";
        return 0;
    fi

    pushd $cross_gcc_dir
    rm -rf ${build_subdir}/cross-gcc-*(/)
    git-buildpackage --git-ignore-new --git-export-dir=${build_subdir} -us -uc

    popd
}

function buildarch
{

    local arch=$1
    local version=$2

    build_failed=1 # I set this to 0 if successful

    echo "===== building arch $arch"

    # I check the libgcc1 build-dep. This isn't strictly necessary since sbuild
    # will fail to get the Build-Depends if libgcc1 isn't available. It is
    # however much quicker to check this here and now. sbuild takes a long time
    # to fail
    local libgcc_version=$(latest_available_version -a $arch libgcc1 | sed 's/.*://')
    if dpkg --compare-versions $version '>>' $libgcc_version; then
        echo "libgcc dependency not yet available. Giving up on build for now"
        return true
    fi


    pushd $cross_gcc_dir

    echo "Generating sources"

    TARGET_LIST="$arch" HOST_LIST="amd64" ./cross-gcc-gensource

    echo "Doing the sbuild"
    cd cross-gcc-packages-amd64/cross-gcc-4.9-$arch
    PARALLEL=${PARALLEL:=4}
    if sbuild --extra-package=$(get_cross_gcc_dev_latest_pkgfile) -A -s --build=amd64 --host=amd64 -d unstable -c unstable-amd64-sbuild; then
        build_failed=0

        # build successful!
    fi

    popd
}

function upload_base
{
    local base=$1

    # if we already uploaded, don't bother
    if [ ! -e "${base}.ftp-master.upload"]; then
        debsign ${base}.changes
        dput ftp-master ${base}.changes
    fi
}

function upload
{
    local arch=$1
    local version=$2

    # upload commands: Not yet daring to automate those
    return 0


    # I upload cross-gcc-dev and a toolchain for the given arch. If
    # cross-gcc-dev was already uploaded, I don't attempt it
    local latest_cross_gcc_dev_ver=$(get_cross_gcc_dev_latest_pkgfile_version)
    upload_base "${cross_gcc_dir}/${build_subdir}/cross-gcc_$(latest_cross_gcc_dev_ver)_all"
    upload_base "${cross_gcc_dir}/cross-gcc-packages-amd64/cross-gcc-4.9-${arch}_${version}_amd64"
}

function source_version
{

    local result=$(latest_available_version gcc-4.9-source)

    if [ -z $result ]; then
        echo "Error: couldn't get latest source version"
        exit 1
    fi

    echo $result
}

function uploaded_version
{
    local arch=$1
    local arch_triplet=$(get_triplet $arch)

    # first I look in the .upload file written by dput and then in APT
    local result=$(< $cross_gcc_dir/cross-gcc-packages-amd64/cross-gcc-4.9-${arch}_*.upload(om[1]) awk '/\.deb\y/ { split( $3, a, "_"); print a[2]; exit}' 2> /dev/null; true)
    result=${result:=$(latest_available_version gcc-4.9-$arch_triplet)}

    echo ${result:=0}
}

function built_version
{
    local arch=$1
    local result=$(< $cross_gcc_dir/cross-gcc-packages-amd64/cross-gcc-4.9-${arch}_*.changes(om[1]) awk '/\.deb\y/ { split( $3, a, "_"); print a[2]; exit}' 2> /dev/null; true)
    result=${result/+/-}
    echo ${result:=0}
}

function try_all
{
    echo "Trying to build everything"

    new_ver=$(source_version)

    build_cross_gcc_dev $new_ver

    # build all out-of-date packages
    for arch ($arches[@]) {
            echo "======== Looking at arch $arch"

            if dpkg --compare-versions $new_ver '>>' $(built_version $arch); then
                buildarch $arch $new_ver

                if ((build_failed)); then
                    echo "WARNING: Failed to build arch $arch; skipping for now"
                fi
            else
                echo "Arch $arch is up-to-date; skipping build"
            fi

            if dpkg --compare-versions $new_ver '>>' $(uploaded_version $arch); then
                upload $arch $new_ver
            else
                echo "Arch $arch is already uploaded; skipping upload"
            fi

            echo "";
        }

        true
}


if [ "${1-}" = "patches" ]; then
    echo "Only generating patches"

    local version=$(source_version)
    update_patches $version
    exit;
fi

if [ "${1-}" = "builddev" ]; then
    echo "Only building cross-gcc-dev"

    local version=$(source_version)
    build_cross_gcc_dev $version
    exit;
fi



while (true) {
    try_all
    echo "===== Looked at all the arches. Sleeping"

    sleep 3600
}
