#!/bin/bash

# Copyright 2016 Canonical Ltd.
# Copyright 2017-2019 Michael Zanetti <michal.zanetti@nymea.io>
#
# 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; version 3.
#
# 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, see <http://www.gnu.org/licenses/>.
#
# Author: Florian Boucault <florian.boucault@canonical.com>
#         Michael Zanetti <michael.zanetti@nymea.io>

# TODO:
# NOW
# - document commands PACKAGE parameter

# LATER
# - mount home directory using technique from http://insights.ubuntu.com/2016/12/08/mounting-your-home-directory-in-lxd/
# - support snaps by using snapcraft
# - support per project custom build rules and custom deploy rules
# - options passed after the command failed with poor messages
# - option to pass configure flags (DEB_CONFIGURE_EXTRA_FLAGS)
# - add support for icecc/distcc
# - support Debian package format 3.0 (quilt) when no upstream tarball is available (ex: libqofono)
# - check if a newer version of the container's image is available
# - option to cleanup device (undeploy)

set -e

RED='\033[0;31m'
GREEN='\033[0;32m'
LIGHT_RED='\033[1;31m'
LIGHT_GREEN='\033[1;32m'
NC='\033[0m'
ERROR_COLOR=$LIGHT_RED
POSITIVE_COLOR=$GREEN
PROGRAM_NAME=`basename $0`

display_help () {
    echo "Usage: $PROGRAM_NAME [options] [command]"
    echo ""
    echo "If no command is passed '$PROGRAM_NAME' will detect the package located in the current directory, "
    echo "create a container for it, download its dependencies, build it and deploy it to the connected device."
    echo ""
    echo "Available commands:"
    echo "    help            - Display this help."
    echo "    setup-lxd       - Setup LXD."
    echo "    new             - Create a container to build in."
    echo "    delete          - Delete a container."
#    echo "    list            - List existing containers."
    echo "    shell           - Executes a shell in a container (will create it if needed)."
    echo "    source          - Download a source package from the repositories."
    echo "    dependencies    - Download and install the dependencies of a package in a container (will create it if needed)"
    echo "    build           - Build a package in a container (will create it if needed)."
    echo "    clean           - Clean up the artifacts generated by a build."
    echo "    deploy          - Deploy built packages to a connected device."
    echo "    stop            - Stop a container"
#    echo "    undeploy        - Remove previously deployed packages from a connected device."
    echo "    run             - Run a command in the container"
    echo ""
    echo "Options:"
#    echo "  --verbose            - Print verbose information."
    echo "  --packages            - Packages to deploy [by default: all the ones already installed on device]."
    echo "  --architecture | -a   - Architecture to build for [defaults to architecture of connected device, if none then armhf]."
    echo "  --ubuntu | -u         - Version of Ubuntu to build for [defaults to version of connected device, if none then 16.04]."
    echo "  --lxd-image           - LXD image to use [defaults to the ones provided by images.nymea.io (example: nymea-builder-xenial-amd64-armhf)."
    echo "  --container-prefix    - LXD container prefix to use"
    echo "  --password            - User password of the device to deploy to [defaults to 0000]."
    echo "  --no-deb              - Do not build Debian packages and uses rsync to deploy the build artifacts."
    echo "  --deploy-path         - When deploying with --no-deb (rsync), installation path for the build artifacts [defaults to /]."
    echo "  --no-changelog | -c   - Don't bump the changelog."
    echo "  --add-repository | -r - Add repository. This is a string understood by apt-add-repository. Can be given multiple times."
    echo "  --remove-repository   - Remove a repository."
    echo "  --with-source | -s    - Include source package in build."
}


exec_device () {
    SSH_ARGS=""
    if [ "$DEPLOY_KEY" != "" ]; then
        SSH_ARGS="$SSH_ARGS -i $DEPLOY_KEY"
    fi
    ssh $SSH_ARGS $DEVICE_USER@$DEVICE_IP $@
}

copy_to_device() {
    SSH_ARGS=""
    if [ "$DEPLOY_KEY" != "" ]; then
        SSH_ARGS="$SSH_ARGS -i $DEPLOY_KEY"
    fi
    scp $SSH_ARGS $1 $DEVICE_USER@$DEVICE_IP:$2
}

copy_from_device() {
    SSH_ARGS=""
    if [ "$DEPLOY_KEY" != "" ]; then
        SSH_ARGS="$SSH_ARGS -i $DEPLOY_KEY"
    fi
    scp $SSH_ARGS $DEVICE_USER@$DEVICE_IP:$1 $2
}

exec_container_root () {
    command="$@"
    #echo lxc exec $LXD_CONTAINER "$@"
    lxc exec $LXD_CONTAINER -- sh -c "$command"
}

exec_container () {
    command="$@"
    #echo lxc exec $LXD_CONTAINER "$@"
    lxc exec $LXD_CONTAINER -- su -l -c "cd $SOURCE_PATH_CONTAINER; $command" $USERNAME
}

stop_container() {
    lxc stop $LXD_CONTAINER --force
}

variables () {
    DEBS_TARBALL_ROOT=debs
    if [ -n "$NEW_BUILD" ] ; then
        if [ $NO_BUMP_CHANGELOG -ne 1 ]; then
            NEW_PACKAGE_VERSION=$PACKAGE_VERSION"+"`date -u +%Y%m%d%H%M`"~`git rev-parse --short HEAD`~$TARGET_UBUNTU"
        else
            NEW_PACKAGE_VERSION=$PACKAGE_VERSION
        fi
    else
        LATEST_DEBS_TARBALL=$(ls -1 --sort=time $DEBS_TARBALL_ROOT_*.tar 2> /dev/null | head -n1)
        NEW_PACKAGE_VERSION=$(echo $LATEST_DEBS_TARBALL | sed "s/$DEBS_TARBALL_ROOT\_//" | sed "s/\.tar//")
    fi
    DEBS_TARBALL="$DEBS_TARBALL_ROOT"_"$NEW_PACKAGE_VERSION.tar"

    LXD_IMAGE_SHORTENED=$(echo $LXD_IMAGE | sed "s/nymea-builder-//")
    LXD_IMAGE_NO_DOT=$(echo $LXD_IMAGE_SHORTENED | sed "s/\./-/")
    LXD_IMAGE_NO_PREFIX=$(echo $LXD_IMAGE_NO_DOT | sed "s/.*\://")
    LXD_IMAGE_NO_SDK=$(echo $LXD_IMAGE_NO_PREFIX | sed "s/ubuntu-sdk/usdk/")
    LXD_IMAGE_NO_SLASH=$(echo $LXD_IMAGE_NO_PREFIX | sed "s/\//-/")
    if [ -z $CONTAINER_PREFIX ]; then
        LXD_CONTAINER_COMPAT=builder-$PACKAGE-$LXD_IMAGE_NO_SLASH
        LXD_CONTAINER=$PACKAGE-$LXD_IMAGE_NO_SLASH
    else
        LXD_CONTAINER_COMPAT=builder-$CONTAINER_PREFIX-$PACKAGE-$LXD_IMAGE_NO_SLASH
        LXD_CONTAINER=$CONTAINER_PREFIX-$PACKAGE-$LXD_IMAGE_NO_SLASH
    fi
    if lxc info $LXD_CONTAINER_COMPAT > /dev/null 2>&1 ; then
        echo "${ERROR_COLOR}Containers are switching to shorter names, please manually delete your container: lxc delete -f $LXD_CONTAINER_COMPAT${NC}"
        echo "${ERROR_COLOR}Next time it will be called $LXD_CONTAINER.${NC}"
        LXD_CONTAINER=$LXD_CONTAINER_COMPAT
    fi

    # Starting from impish, the image wont have the target arch any more. Lets add it to the container then
    if [ "$TARGET_UBUNTU" == "xenial" ] || [ "$TARGET_UBUNTU" == "bionic" ] || [ "$TARGET_UBUNTU" == "focal" ] || [ "$TARGET_UBUNTU" == "hirsute" ] || [ "$TARGET_UBUNTU" == "buster" ]; then
        ;
    else
        LXD_CONTAINER=$LXD_CONTAINER-$TARGET_ARCH
    fi

    # LXD container name can only have HOST_NAME_MAX characters
    LXD_CONTAINER=$(echo $LXD_CONTAINER | cut -c 1-$((`getconf HOST_NAME_MAX` - 1)))
    USERNAME=`id --user --name`
    GROUPNAME=$USERNAME
    if [ -n "$ENCRYPTED_HOME" ] ; then
        USERID=`id --user`
        GROUPID=`id --group`
    else
        USERID=150000
        GROUPID=150000
    fi
    USERDIR=/home/$USERNAME
    SOURCE_REPOSITORY=$USERDIR/source_repository
    SCRIPT_DIR=`dirname $0`
    CREATE_REPO_SCRIPT=create_repository.sh
    PARALLEL_BUILD=$((`nproc` + 1))
    MOUNTED_DIRECTORY=$PWD
    MOUNT_POINT=$USERDIR/$PACKAGE
    SOURCE_PATH_LOCAL=$MOUNTED_DIRECTORY
    SOURCE_PATH_CONTAINER=$MOUNT_POINT
    POST_DEPLOY_SCRIPT=.crossbuilder/post_deploy
    DEPLOY_CONF=.crossbuilder/deploy.conf
    if [ -e $DEPLOY_CONF ]; then
        . $DEPLOY_CONF
    else
        echo "# Device IP and ssh user to be used." >> .crossbuilder/deploy.conf
        echo "#DEVICE_IP=" > .crossbuilder/deploy.conf
        echo "#DEVICE_USER=" >> .crossbuilder/deploy.conf
        echo "# Required for sudo permissions to install packages." >> .crossbuilder/deploy.conf
        echo "#DEVICE_PASSWORD=" >> .crossbuilder/deploy.conf
        echo "# Optional if ssh is by key only and key can't be found in ~/.ssh:" >> .crossbuilder/deploy.conf
        echo "#DEVICE_KEY=" >> .crossbuilder/deploy.conf
    fi
    HOST_FARCH=$(dpkg-architecture -f -a$TARGET_ARCH -qDEB_BUILD_MULTIARCH)
    TARGET_FARCH=$(dpkg-architecture -f -a$TARGET_ARCH -qDEB_HOST_MULTIARCH)
    if [ -z "$DEB_BUILD_PROFILES" ]; then
        DEB_BUILD_PROFILES='nocheck nodoc'
    fi
    if [ $TARGET_UBUNTU == "xenial" ] || [ $TARGET_UBUNTU == "bionic" ]; then
        # Autodbgsym packages don't work in xenials and bionics version of dpkg when building in a container
        DEB_BUILD_PROFILES="$DEB_BUILD_PROFILES noautodbgsym"
    else
        DEB_BUILD_PROFILES="$DEB_BUILD_PROFILES autodbgsym"
    fi
    echo "Using DEB_BUILD_PROFILES: $DEB_BUILD_PROFILES";
    if [ $BUILD_SRCPKG -eq 1 ]; then
        BUILD_TYPE_FLAGS="-F"
    else
        BUILD_TYPE_FLAGS="-b"
    fi
}

check_lxd_accessible () {
    if ! lxc info > /dev/null 2>&1 ; then
        echo "${ERROR_COLOR}LXD was installed but is not accessible. Please restart your computer.${NC}"
        exit 1
    fi
}

lxd_has_image_or_container () {
    CONTAINER_COUNT=$(lxc list | wc -l)
    IMAGE_COUNT=$(lxc image list | wc -l)
    if [ $CONTAINER_COUNT = "3" ] && [ $IMAGE_COUNT = "3" ]; then
        return 1
    fi

    return 0
}

is_subuid_setup () {
    if ! grep "root:1000:1" /etc/subuid > /dev/null ; then
        return 1
    fi
    if ! grep "root:1000:1" /etc/subgid > /dev/null ; then
        return 1
    fi
    return 0
}

ensure_lxd_subuid () {
    if ! is_subuid_setup ; then
        echo "${ERROR_COLOR}LXD requires subuid to be setup adequately to mount directories in containers.${NC}"
        echo "${POSITIVE_COLOR}Would you like to do that now? (y/n) ${NC}"
        read REPLY
        echo
        if [ "$REPLY" = y ]
        then
            sudo usermod --add-subuids 1000-1000 root
            sudo usermod --add-subgids 1000-1000 root
            sudo service lxd restart
        else
            exit 1
        fi
    fi
}

setup_lxd () {
    echo "${POSITIVE_COLOR}Setting up LXD.${NC}"
    if [ -n "$ENCRYPTED_HOME" ] ; then
        echo -n "${ERROR_COLOR}Your home folder is encrypted. $PROGRAM_NAME will use priviledged "
        echo -n "LXD containers and the default storage backend (slower).\n${NC}"
        sudo lxd init --auto
    else
        echo -n "${POSITIVE_COLOR}Would you like to setup LXD with ZFS in your home directory? (y/n) \n${NC}"
        echo -n "This is recommended for faster operation, and also in case there is not enough "
        echo -n "space in your / partition. \n"
        read REPLY
        echo
        if [ "$REPLY" = y ]
        then
            echo "${POSITIVE_COLOR}Installing ZFS.${NC}"
            sudo apt-get install -y zfsutils-linux
            LXD_POOL=~/zfs/lxd.img
            echo "${POSITIVE_COLOR}Creating file $LXD_POOL to contain all of LXD's images and containers.${NC}"
            mkdir -p `dirname $LXD_POOL`
            truncate -s 32G $LXD_POOL
            sudo zpool create lxd $LXD_POOL
            sudo lxd init --auto --storage-backend zfs --storage-pool lxd
            # add automatic mount of zfs pool upon boot
            echo "zpool import -c /etc/zfs/zpool.cache -aN" | sudo tee /etc/rc.local
        else
            sudo lxd init --auto
        fi
    fi
}


new_container () {
    # setup the building container
    if lxc info $LXD_CONTAINER > /dev/null 2>&1 ; then
        echo "${POSITIVE_COLOR}LXD container $LXD_CONTAINER already exists.${NC}"
        # FIXME: check if the container is already started
        lxc start $LXD_CONTAINER || true
    else
        echo "${POSITIVE_COLOR}Creating LXD container $LXD_CONTAINER using $LXD_IMAGE.${NC}"
        lxc remote --public=true --accept-certificate=true add nymea https://images.nymea.io || true
#        lxc remote --protocol=simplestreams --public=true --accept-certificate=true add sdk https://sdk-images.canonical.com || true
        lxc init $LXD_IMAGE $LXD_CONTAINER
        if [ -n "$ENCRYPTED_HOME" ] ; then
            lxc config set $LXD_CONTAINER security.privileged true
        else
            # Note: check the lxc version for lxc.id_map vs lxc.idmap
            LXCVERSION=$(lxc --version)
            LXCMAJORVERSION=$(echo ${LXCVERSION} | cut -d. -f1)
            if [ "${LXCMAJORVERSION}" -lt "3" ]; then
                echo "${POSITIVE_COLOR}Using lxc version 2 compatibility$LXD_IMAGE.${NC}"
                printf "lxc.id_map = g $GROUPID `id --group` 1\nlxc.id_map = u $USERID `id --user` 1" | lxc config set $LXD_CONTAINER raw.lxc -
            else
                printf "uid `id --user` 150000\ngid `id --group` 150000" | lxc config set $LXD_CONTAINER raw.idmap -
            fi
        fi

        lxc start $LXD_CONTAINER
        lxc exec --env GROUPID=$GROUPID --env GROUPNAME=$GROUPNAME $LXD_CONTAINER -- deluser $USERNAME || true
        lxc exec --env GROUPID=$GROUPID --env GROUPNAME=$GROUPNAME $LXD_CONTAINER -- deluser ubuntu || true
        lxc exec --env GROUPID=$GROUPID --env GROUPNAME=$GROUPNAME $LXD_CONTAINER -- delgroup $GROUPNAME || true
        lxc exec $LXD_CONTAINER -- rm -rf /home/ubuntu
        lxc exec --env GROUPID=$GROUPID --env GROUPNAME=$GROUPNAME $LXD_CONTAINER -- addgroup --gid $GROUPID $GROUPNAME
        lxc exec --env GROUPID=$GROUPID --env USERNAME=$USERNAME --env USERID=$USERID $LXD_CONTAINER -- adduser --disabled-password --gecos "" --uid $USERID --gid $GROUPID $USERNAME
        lxc exec --env USERNAME=$USERNAME $LXD_CONTAINER -- usermod -aG sudo $USERNAME
        # wait for the container's network connection
        check_for_container_network
        check_for_dpkg_available
        exec_container_root apt update
        exec_container_root apt install -y sudo debhelper ccache software-properties-common devscripts
        exec_container_root adduser $USERNAME sudo
        # set empty password for the user
        exec_container_root passwd --delete $USERNAME
        exec_container_root "printf 'export PKG_CONFIG_PATH=/usr/lib/$TARGET_FARCH/pkgconfig\n\
if [ "$TARGET_UBUNTU" == "xenial" ] || [ "$TARGET_UBUNTU" == "bionic" ] || [ "$TARGET_UBUNTU" == "stretch" ] || [ "$TARGET_UBUNTU" == "buster" ]; then
  export QT_SELECT=qt5-$HOST_FARCH-$TARGET_FARCH\n\
fi
if [ "$TARGET_ARCH" = "i386" ]; then
  export CC=i686-linux-gnu-gcc\n\
  export CXX=i686-linux-gnu-g++\n\
else
  export CC=$TARGET_FARCH-gcc\n\
  export CXX=$TARGET_FARCH-g++\n\
fi
export PATH="/usr/lib/ccache:$PATH"

source <( dpkg-architecture --host-arch $TARGET_ARCH --print-set )
' >> /etc/profile.d/crossbuilder.sh"

        for i in $(seq 1 ${#ADDITIONAL_REPOS[*]}); do
            REPO="${ADDITIONAL_REPOS[$((i-1))]}"
            REPO_UNESCAPED=`echo $REPO | sed 's/_/ /g'`
            echo "Adding repository: $REPO_UNESCAPED"
            exec_container_root "apt-add-repository '$REPO_UNESCAPED'"
        done
        for i in $(seq 1 ${#REMOVE_REPOS[*]}); do
            REPO="${REMOVE_REPOS[$((i-1))]}"
            REPO_UNESCAPED=`echo $REPO | sed 's/_/ /g'`
            echo "Removing repository: $REPO_UNESCAPED"
            exec_container_root "apt-add-repository -r '$REPO_UNESCAPED'"
        done
        exec_container_root "DEBIAN_FRONTENT=noninteractive apt-get --yes update"
    fi

    if ! lxc config device get $LXD_CONTAINER current_dir_mount disk 2> /dev/null ; then
        echo "${POSITIVE_COLOR}Mounting $MOUNTED_DIRECTORY in container.${NC}"
        lxc config device add $LXD_CONTAINER current_dir_mount disk source=$MOUNTED_DIRECTORY path=$MOUNT_POINT
    else
        lxc config device set $LXD_CONTAINER current_dir_mount source $MOUNTED_DIRECTORY
    fi
    if [ -d $HOME/.ccache ]; then
        if ! lxc config device get $LXD_CONTAINER ccache_dir_mount disk 2> /dev/null ; then
            echo "${POSITIVE_COLOR}Mounting ccache dir ($HOME/.ccache) in container.${NC}"
            lxc config device add $LXD_CONTAINER ccache_dir_mount disk source=$HOME/.ccache/ path=$HOME/.ccache
        fi
    elif [ -d $HOME/.cache/ccache ]; then
        if ! lxc config device get $LXD_CONTAINER ccache_dir_mount disk 2> /dev/null ; then
            echo "${POSITIVE_COLOR}Mounting ccache dir ($HOME/.cache/ccache) in container.${NC}"
            lxc config device add $LXD_CONTAINER ccache_dir_mount disk source=$HOME/.cache/ccache/ path=$HOME/.ccache
        fi
    else
        echo "${NEGATIVE_COLOR}ccache is not set up. In order to speed up builds, consider installing ccache and setting up with:"
        echo "${NEGATIVE_COLOR}$ ccache -s"
    fi
}

delete_container () {
    echo "${POSITIVE_COLOR}Deleting LXD container $LXD_CONTAINER.${NC}"
    lxc delete -f $LXD_CONTAINER
}

shell_container () {
    echo "${POSITIVE_COLOR}Entering shell in LXD container $LXD_CONTAINER.${NC}"
    lxc exec $LXD_CONTAINER -- su --login $USERNAME
}

enable_overlay_source () {
    OVERLAY_APT="/etc/apt/sources.list.d/ci-train-ppa-service-ubuntu-stable-phone-overlay-vivid.list"
    if exec_container_root "grep '#.*deb-src' $OVERLAY_APT" ; then
        exec_container_root "sed -i 's/#.*deb-src/deb-src/' $OVERLAY_APT"
        exec_container_root "DEBIAN_FRONTEND=noninteractive apt-get update --allow-unauthenticated"
    fi
}

get_source_package () {
    echo "${POSITIVE_COLOR}Downloading source package $PACKAGE for Ubuntu $TARGET_UBUNTU.${NC}"

    # cleanup source repository potentially setup by install_dependencies before
    exec_container rm -f $USERDIR/dependencies_installed
    exec_container_root add-apt-repository --remove --enable-source \"deb file://$SOURCE_REPOSITORY/ /\"
    exec_container_root "sudo rm -f /etc/apt/preferences.d/localrepo.pref"

    enable_overlay_source

    if ! lxc exec $LXD_CONTAINER -- su -l -c "cd $MOUNT_POINT; apt-get source --only-source $PACKAGE" $USERNAME ; then
        echo "${ERROR_COLOR}No source package exists with the name $PACKAGE.${NC}"
        echo "${ERROR_COLOR}Deleting container $LXD_CONTAINER.${NC}"
        lxc delete -f $LXD_CONTAINER
        exit 1
    fi

    PACKAGE_VERSION=`dpkg-parsechangelog --show-field Version -l $PACKAGE*/debian/changelog`
    exec_container "mv $PACKAGE*.orig.tar.* $PACKAGE*diff.* $PACKAGE*debian.tar* $PACKAGE*.dsc $USERDIR/" || true
    mv $PACKAGE-*/* .
    mv $PACKAGE-*/.[!.]* . || true
    rmdir $PACKAGE-*
    echo "${POSITIVE_COLOR}Source code for $PACKAGE is now available in $PACKAGE/.${NC}"
    VCS=$(grep -i vcs debian/control | grep --invert-match Vcs-Browser)
    if [ "$VCS" != "" ] ; then
        echo "${POSITIVE_COLOR}'$PACKAGE' packaging is versioned and available at $VCS${NC}"
    fi
}

backup_changelog () {
    cp debian/changelog /tmp/$LXD_CONTAINER-$PACKAGE-changelog.orig
}

restore_changelog () {
    mv /tmp/$LXD_CONTAINER-$PACKAGE-changelog.orig debian/changelog
}

ensure_upstream_tarball () {
    if grep quilt debian/source/format > /dev/null 2>&1 ; then
        echo "${POSITIVE_COLOR}Downloading upstream tarball of $PACKAGE in container.${NC}"
        enable_overlay_source
        # FIXME: try using dget instead so that we can specify a precise version
        exec_container "cd $USERDIR && apt-get source --download-only --allow-unauthenticated $PACKAGE"
    fi
}

install_dependencies () {
    # install build dependencies in container
    if ! exec_container test -e $USERDIR/dependencies_installed ; then
        ensure_upstream_tarball
        echo "${POSITIVE_COLOR}Installing $TARGET_ARCH build dependencies for $PACKAGE in container $LXD_CONTAINER.${NC}"
        exec_container mkdir -p $SOURCE_REPOSITORY
        lxc file push $SCRIPT_DIR/$CREATE_REPO_SCRIPT $LXD_CONTAINER$SOURCE_REPOSITORY/

        backup_changelog
        trap restore_changelog HUP INT TERM QUIT
        if [ $NO_BUMP_CHANGELOG -ne 1 ]; then
            exec_container dch -v $NEW_PACKAGE_VERSION 'New Package build'
        fi
        if ! exec_container "DEB_BUILD_PROFILES='$DEB_BUILD_PROFILES' dpkg-buildpackage -S -us -uc -nc -d -I -Iobj-* -Idebian/tmp/* -I.bzr*" ; then
            restore_changelog
            exit 1
        fi
        if ! exec_container "mv -f $USERDIR/$PACKAGE*.* $SOURCE_REPOSITORY/" ; then
            exec_container "rm -f $USERDIR/$PACKAGE*.*"
        fi
        exec_container $SOURCE_REPOSITORY/$CREATE_REPO_SCRIPT $SOURCE_REPOSITORY

        # Since bionic, add-apt-repository will automatically call apt-get update which fails on this if -o Acquire::AllowInsecureRepositories=true isn't passed
        # As a workaround, let's manually echo the repo strings to sources.list instead
        #exec_container_root add-apt-repository --enable-source \"deb file://$SOURCE_REPOSITORY/ /\"
        exec_container_root "echo 'deb file://$SOURCE_REPOSITORY/ /' | sudo tee -a /etc/apt/sources.list"
        exec_container_root "echo 'deb-src file://$SOURCE_REPOSITORY/ /' | sudo tee -a /etc/apt/sources.list"

        exec_container_root "printf 'Package: *\nPin: release o=local\nPin-Priority: 2000' | sudo tee /etc/apt/preferences.d/localrepo.pref"
        exec_container_root apt-get update -o Acquire::AllowInsecureRepositories=true --allow-unauthenticated
        if ! exec_container_root DEBIAN_FRONTEND=noninteractive apt-get build-dep -y --force-yes -o Apt::Build-Profiles=nocheck,nodoc -a$TARGET_ARCH $PACKAGE=$NEW_PACKAGE_VERSION ; then
            restore_changelog
            exit 1
        fi

        restore_changelog

        # workaround various issues with qmake cross compilation
        # FIXME: this should be integrated in the binary package qt5-qmake-$TARGET_FARCH
        # from source package qtbase-opensource-src
        if exec_container test -e /usr/bin/qt5-qmake-$TARGET_FARCH ; then
            exec_container_root "printf '/usr/lib/$HOST_FARCH/qt5/$TARGET_FARCH/bin\n/usr/lib/$HOST_FARCH/qt5/bin\n/usr/lib/$HOST_FARCH\n' > /usr/share/qtchooser/qt5-$HOST_FARCH-$TARGET_FARCH.conf"
            exec_container_root mkdir -p /usr/lib/$HOST_FARCH/qtchooser
            exec_container_root ln -f -s ../../../share/qtchooser/qt5-$HOST_FARCH-$TARGET_FARCH.conf /usr/lib/$HOST_FARCH/qtchooser/qt5-$TARGET_FARCH.conf
            exec_container_root ln -f -s ../../../share/qtchooser/qt5-$HOST_FARCH-$TARGET_FARCH.conf /usr/lib/$HOST_FARCH/qtchooser/5-$TARGET_FARCH.conf
            exec_container_root mkdir -p /usr/lib/$HOST_FARCH/qt5/$TARGET_FARCH/bin
            exec_container_root ln -f /usr/bin/qt5-qmake-$TARGET_FARCH /usr/lib/$HOST_FARCH/qt5/$TARGET_FARCH/bin/qmake
        fi

        exec_container touch $USERDIR/dependencies_installed
    else
        echo "${POSITIVE_COLOR}$TARGET_ARCH build dependencies for $PACKAGE already installed in container $LXD_CONTAINER.${NC}"
    fi;
}

build_deb () {
    # build package in container
    echo "${POSITIVE_COLOR}Building $PACKAGE for $TARGET_ARCH in parallel (-j$PARALLEL_BUILD).${NC}"
#    exec_container "rm -f ../*.deb"
    exec_container "rm -f debian/*.debhelper.log"
    exec_container "rm -rf ../*.deb ../*.ddeb ../*.dsc ../*.tar* ../*.changes ../$DEBS_TARBALL ../source_repository/* > /dev/null 2>&1 || true"
    backup_changelog
    trap restore_changelog HUP INT TERM QUIT

    if [ $NO_BUMP_CHANGELOG -ne 1 ]; then
        exec_container dch -v $NEW_PACKAGE_VERSION \'\'
    fi
    if ! exec_container "DEB_BUILD_PROFILES='$DEB_BUILD_PROFILES' DEB_BUILD_OPTIONS='parallel=$PARALLEL_BUILD $DEB_BUILD_PROFILES' dpkg-buildpackage --target-arch $TARGET_ARCH -us -uc -nc -I -Iobj-* -Idebian/tmp/* -I.bzr* $BUILD_TYPE_FLAGS" ; then
        restore_changelog
        exit 1
    fi
    restore_changelog

    # transfer resulting debian packages to local machine
    echo "${POSITIVE_COLOR}Packing build artifacts in $DEBS_TARBALL.${NC}"
    rm -f $DEBS_TARBALL_ROOT*
    if [ $BUILD_SRCPKG ]; then
        exec_container "tar cf ../$DEBS_TARBALL ../*.deb ../*.ddeb ../*.dsc ../*.tar* || tar cf ../$DEBS_TARBALL ../*.deb ../*.dsc ../*changes ../$PACKAGE*tar*"
    else
        exec_container "tar cf ../$DEBS_TARBALL ../*.deb ../*.ddeb ../*.dsc ../*.tar* || tar cf ../$DEBS_TARBALL ../*.deb"
    fi
    lxc file pull $LXD_CONTAINER$USERDIR/$DEBS_TARBALL .
    exec_container "rm -rf ../*.deb ../*.ddeb ../*.dsc ../$PACKAGE*.tar* ../*.changes ../$DEBS_TARBALL ../source_repository/* > /dev/null 2>&1 || true"
}

build_make_install () {
    echo "${POSITIVE_COLOR}Building $PACKAGE for $TARGET_ARCH in parallel (-j$PARALLEL_BUILD).${NC}"
    exec_container "rm -f debian/*.debhelper.log"
    exec_container "DEB_BUILD_PROFILES=$DEB_BUILD_PROFILES DEB_BUILD_OPTIONS='parallel=$PARALLEL_BUILD $DEB_BUILD_PROFILES' fakeroot debian/rules install"
}

build () {
    if [ -n "$NO_DEB" ] ; then
        build_make_install
    else
        build_deb
    fi
}

copy_build_to_container () {
    BUILD_FOLDER=$1
    PREVIOUS_DEBS_TARBALL=$2
    echo "${POSITIVE_COLOR}Copying and extracting $DEBS_TARBALL from $BUILD_FOLDER into container $LXD_CONTAINER.${NC}"
    exec_container mkdir -p $SOURCE_REPOSITORY
    lxc file push $BUILD_FOLDER/$PREVIOUS_DEBS_TARBALL $LXD_CONTAINER$SOURCE_REPOSITORY/
    exec_container "tar xf $SOURCE_REPOSITORY/$PREVIOUS_DEBS_TARBALL --directory $SOURCE_REPOSITORY/"
    exec_container "tar xf $SOURCE_REPOSITORY/$PREVIOUS_DEBS_TARBALL --directory $USERDIR/"
}

clean () {
    echo "${POSITIVE_COLOR}Cleaning previous build of $PACKAGE for $TARGET_ARCH.${NC}"
    exec_container debian/rules clean
}

check_for_container_network() {
    NETWORK_UP=0
    for i in `seq 1 20`
    do
        # lxc < 4.20 prints "eth0:  inet <ip>", > 4.20 prints "inet: <ip> (global)"
        if lxc info $LXD_CONTAINER | grep -e "eth0.*inet\b\|inet:.*(global)" > /dev/null 2>&1 ; then
            NETWORK_UP=1
            break
        fi
        if [ "$TARGET_UBUNTU" == "xenial" ]; then
            # in newer lxc versions xenial containers network won't come up on its own
            lxc exec $LXD_CONTAINER dhclient eth0
            exec_container_root "echo nameserver 8.8.8.8 > /etc/resolv.conf"
        fi
        sleep 1
    done
    if [ $NETWORK_UP -ne 1 ] ; then
        echo "${ERROR_COLOR}Container is not connected to the Internet.${NC}"
        exit 1
    fi
}

check_for_dpkg_available() {
    DPKG_AVAILABLE=0
    for i in `seq 1 60`
    do
        echo "${ERROR_COLOR} /var/lib/dpkg/lock exists... Waiting for it to disappear...${NC}"
        if exec_container_root test ! -e /var/lib/dpkg/lock; then
            DPKG_AVAILABLE=1
            break
        fi
        sleep 1
    done
    if [ $DPKG_AVAILABLE -ne 1 ] ; then
        echo "${ERROR_COLOR} /var/lib/dpkg/lock still exists after one minute. Assuming it is stale. Deleting it...${NC}"
        exec_container_root rm /var/lib/dpkg/lock
    fi
}

check_for_device_network() {
    NETWORK_UP=0
    for i in `seq 1 5`
    do
        if exec_device ping -c1 -w1 google.com | grep PING > /dev/null 2>&1 ; then
            NETWORK_UP=1
            break
        fi
    done
    if [ $NETWORK_UP -ne 1 ] ; then
        echo "${ERROR_COLOR}Device connected is not connected to the Internet.${NC}"
        exit 1
    fi
}

deploy_deb () {
    if ! test -e $DEBS_TARBALL ; then
        echo "${ERROR_COLOR}No Debian packages ($DEBS_TARBALL) to deploy to device. Run $PROGRAM_NAME build first.${NC}"
        exit 1
    fi

    echo "${POSITIVE_COLOR}Transferring Debian packages to device.${NC}"
    # tranfer debian packages to device
    exec_device mkdir -p /tmp/repo
    copy_to_device $DEBS_TARBALL /tmp/repo
    exec_device "cd /tmp/repo && tar xvf /tmp/repo/$DEBS_TARBALL && rm -f /tmp/repo/$DEBS_TARBALL_ROOT*"

    # install debian packages on device
    if [ ! -z "$PACKAGES_TO_DEPLOY" ] ; then
        echo "${POSITIVE_COLOR}Installing manually specified packages:" $PACKAGES_TO_DEPLOY${NC}
        DPKG_ARGS=""
        for package in $PACKAGES_TO_DEPLOY ; do
            DPKG_ARGS="$DPKG_ARGS /tmp/repo/$package"_"$NEW_PACKAGE_VERSION"_"$TARGET_ARCH.deb"
        done
        exec_device SUDO_ASKPASS=/tmp/askpass.sh sudo -A dpkg -i $DPKG_ARGS 
    else
        check_for_device_network
        echo "${POSITIVE_COLOR}Upgrading packages already installed on device with newly built ones.${NC}"
        # create local deb repository on device
        rm -f $CREATE_REPO_SCRIPT
        if ! copy_from_device /tmp/repo/$CREATE_REPO_SCRIPT /dev/null 2> /dev/null ; then
            copy_to_device $SCRIPT_DIR/$CREATE_REPO_SCRIPT /tmp/repo/
            exec_device /tmp/repo/$CREATE_REPO_SCRIPT /tmp/repo
            exec_device "printf 'deb file:/tmp/repo/ /\n' > /tmp/repo/sources.list"
            exec_device "cp /etc/apt/sources.list /tmp/repo/all.list"
            exec_device "cat /tmp/repo/sources.list >> /tmp/repo/all.list"
            SERIES=$(exec_device lsb_release -cs | tr -d '\r')
            exec_device "printf 'Package: *\nPin: release o=local\nPin-Priority: 2000\n\nPackage: *\nPin: release a=$SERIES*\nPin-Priority: 50' | SUDO_ASKPASS=/tmp/askpass.sh sudo -A tee /etc/apt/preferences.d/localrepo.pref"
            exec_device SUDO_ASKPASS=/tmp/askpass.sh sudo -A DEBIAN_FRONTEND=noninteractive apt-get update --allow-unauthenticated
        else
            exec_device /tmp/repo/$CREATE_REPO_SCRIPT /tmp/repo
        fi;

        exec_device "SUDO_ASKPASS=/tmp/askpass.sh sudo -A sed -i '/Pin-Priority/c\Pin-Priority: 50' /etc/apt/preferences.d/localrepo.pref"
        exec_device SUDO_ASKPASS=/tmp/askpass.sh sudo -A DEBIAN_FRONTEND=noninteractive apt-get update -o Dir::Etc::sourcelist="/tmp/repo/sources.list" -o Dir::Etc::sourceparts="-" -o APT::Get::List-Cleanup="0"
        exec_device SUDO_ASKPASS=/tmp/askpass.sh sudo -A DEBIAN_FRONTEND=noninteractive apt-get dist-upgrade -o Dir::Etc::sourcelist="/tmp/repo/all.list" --yes --force-yes
        exec_device "SUDO_ASKPASS=/tmp/askpass.sh sudo -A sed -i '/Pin-Priority/c\Pin-Priority: 1001' /etc/apt/preferences.d/localrepo.pref"
    fi;
}

deploy_make_install () {
    echo "${POSITIVE_COLOR}Transferring build artifacts to device.${NC}"
    if [ -z "$PACKAGES_TO_DEPLOY" ] ; then
        PACKAGES_TO_DEPLOY=$(grep Package debian/control | cut -d ' ' -f 2)
    fi

    for binary_package in $PACKAGES_TO_DEPLOY
    do
        FOLDERS_TO_DEPLOY="$FOLDERS_TO_DEPLOY debian/$binary_package/"
        # clean up build artifacts from a potential previous Debian package build
        rm -rf debian/$binary_package/DEBIAN
    done

    if [ -z "$DEPLOY_PATH" ] ; then
        DEPLOY_PATH="/"
    fi

    sync_with_device $FOLDERS_TO_DEPLOY $DEPLOY_PATH
}

deploy_to_device () {
    if test -z "$DEVICE_IP" ; then
        echo "${POSITIVE_COLOR}Edit $DEPLOY_CONF to set up deployment of the packages to a device.${NC}"
        exit 0 # exiting with status ok even though we didn't deploy, but as there is no config we can assume it didn't fail, just wasn't needed
    fi

    ping -c 1 $DEVICE_IP
    DEVICE_STATE=$?
    if [ $DEVICE_STATE -ne 0 ] ; then
        echo "${ERROR_COLOR}No device connected to deploy to.${NC}"
        exit 1
    fi;

    # setup sudo on device
    exec_device "printf '#\041/bin/sh\necho $DEVICE_PASSWORD' >/tmp/askpass.sh"
    exec_device chmod +x /tmp/askpass.sh

    # check password is correct
    exec_device SUDO_ASKPASS=/tmp/askpass.sh sudo -A touch /tmp/password_ok
    if copy_from_device /tmp/password_ok . ; then
        rm -f password_ok
        exec_device SUDO_ASKPASS=/tmp/askpass.sh sudo -A rm -f /tmp/password_ok
    else
        echo "${ERROR_COLOR}Device password incorrect. Use --password to pass the correct one or write it in $DEPLOY_CONF.${NC}"
        exit 1
    fi

    exec_device SUDO_ASKPASS=/tmp/askpass.sh sudo -A mount -o remount,rw /

    if [ -n "$NO_DEB" ] ; then
        deploy_make_install
    else
        deploy_deb
    fi

    # execute post deploy
    if test -e $SOURCE_PATH_LOCAL/$POST_DEPLOY_SCRIPT ; then
        echo "${POSITIVE_COLOR}Execute project specific post deploy script ($POST_DEPLOY_SCRIPT).${NC}"
        copy_to_device $SOURCE_PATH_LOCAL/$POST_DEPLOY_SCRIPT /tmp
        exec_device sh /tmp/$(basename $POST_DEPLOY_SCRIPT)
    else
        echo "${POSITIVE_COLOR}If a script named $POST_DEPLOY_SCRIPT existed, it would be executed on device after every deploy.${NC}"
    fi
}

MISSING_PACKAGES=
if ! which dpkg > /dev/null ; then
    MISSING_PACKAGES="dpkg"
fi
if ! which dpkg-parsechangelog > /dev/null ; then
    MISSING_PACKAGES="dpkg-dev $MISSING_PACKAGES"
fi

if [ ! -z "$MISSING_PACKAGES" ] ; then
    echo "${POSITIVE_COLOR}$PROGRAM_NAME depends on $MISSING_PACKAGES. Installing:${NC}"
    echo "sudo apt install $MISSING_PACKAGES"
    sudo apt install $MISSING_PACKAGES
fi

if stat --file-system $HOME | grep ecrypt ; then
    ENCRYPTED_HOME=1
fi

if ! which lxd > /dev/null ; then
    echo "${POSITIVE_COLOR}$PROGRAM_NAME uses LXD to download dependencies and build."
    echo -n "${POSITIVE_COLOR}Would you like to install LXD? (y/n) ${NC}"
    read REPLY
    echo
    if [ "$REPLY" = y ]
    then
        echo "sudo apt-get install -y lxd"
        sudo apt-get install -y lxd
#        sudo dpkg-reconfigure -p medium lxd
        setup_lxd
        ensure_lxd_subuid
        echo "${ERROR_COLOR}LXD is now setup but will only work after you restart your computer.${NC}"
        exit 0
    else
        echo "${ERROR_COLOR}$PROGRAM_NAME will not work without LXD. Bye.$OPTION${NC}"
        exit 1
    fi
fi

check_lxd_accessible
ensure_lxd_subuid

HOST_ARCH=`dpkg --print-architecture`
TARGET_ARCH=amd64
TARGET_UBUNTU=xenial
NO_BUMP_CHANGELOG=0
BUILD_SRCPKG=0

DEVICE_PASSWORD=nymea # Will be set by variables() if overridden in .crossbuilder/deploy.conf

ADDITIONAL_REPOS=()
REMOVE_REPOS=()

# Load run configuration cache (will load TARGET_ARCH and TARGET_UBUNTU)
CACHE_CONF=.crossbuilder/cache.conf
if [ -e $CACHE_CONF ]; then
    . $CACHE_CONF
fi


while [ "$1" != "" ]; do
    OPTION=`echo $1 | awk -F= '{print $1}'`
    VALUE=`echo $1 | awk -F= '{print $2}'`
    case $1 in
        -*)
            case $OPTION in
            --packages)
                PACKAGES_TO_DEPLOY=$VALUE
            ;;
            --architecture)
                TARGET_ARCH=$VALUE
            ;;
            -a)
                shift;
                TARGET_ARCH=$1
            ;;
            --ubuntu)
                TARGET_UBUNTU=$VALUE
            ;;
            -u)
                shift;
                TARGET_UBUNTU=$1
            ;;
            --lxd-image)
                LXD_IMAGE=$VALUE
            ;;
            --password)
                DEVICE_PASSWORD=$VALUE
            ;;
            --no-deb)
                NO_DEB=1
            ;;
            --deploy-path)
                DEPLOY_PATH=$VALUE
            ;;
            --container-prefix)
                CONTAINER_PREFIX=$VALUE
            ;;
            --help)
                display_help
                exit 0
            ;;
            --no-changelog)
                NO_BUMP_CHANGELOG=1
            ;;
            -c)
                NO_BUMP_CHANGELOG=1
            ;;
            --add-repository)
                shift
                ADDITIONAL_REPOS+=("$1")
            ;;
            -r)
                shift
                ADDITIONAL_REPOS+=("$1")
            ;;
            --remove-repository)
                shift
                REMOVE_REPOS+=("$1")
            ;;
            --with-source | -s)
                BUILD_SRCPKG=1
            ;;
            *)
                display_help
                echo ""
                echo "${ERROR_COLOR}error: unknown option: $OPTION${NC}"
                exit 1
            ;;
            esac
            shift
        ;;
        *)
            break
        ;;
    esac
done

# write back run config cache values
set +e
if [ ! -d .crossbuilder ]; then mkdir .crossbuilder; fi
if [ ! -e $CACHE_CONF ]; then touch $CACHE_CONF; fi
grep TARGET_ARCH $CACHE_CONF > /dev/null 2>&1
if [ $? -ne 0 ]; then echo "TARGET_ARCH=$TARGET_ARCH" >> $CACHE_CONF; else sed -i "s/TARGET_ARCH=.*/TARGET_ARCH=$TARGET_ARCH/" $CACHE_CONF; fi
grep TARGET_UBUNTU $CACHE_CONF > /dev/null 2>&1
if [ $? -ne 0 ]; then echo "TARGET_UBUNTU=$TARGET_UBUNTU" >> $CACHE_CONF; else sed -i "s/TARGET_UBUNTU=.*/TARGET_UBUNTU=$TARGET_UBUNTU/" $CACHE_CONF; fi
set -e

if [ -z "$LXD_IMAGE" ] ; then
    # Starting with impish/bullseye we're using a single container for all target archs
    if [ "$TARGET_UBUNTU" == "xenial" ] || [ "$TARGET_UBUNTU" == "bionic" ] || [ "$TARGET_UBUNTU" == "focal" ] || [ "$TARGET_UBUNTU" == "hirsute" ] || [ "$TARGET_UBUNTU" == "buster" ]; then
      LXD_IMAGE=nymea:nymea-builder-$TARGET_UBUNTU-$HOST_ARCH-$TARGET_ARCH
    else
      LXD_IMAGE=nymea:nymea-builder-$TARGET_UBUNTU-$HOST_ARCH
    fi
    echo "LXD_IMAGE: $LXD_IMAGE"
fi

COMMAND=$1
if [ -n "$COMMAND" ] ; then
    shift
fi

if [ "$COMMAND" = "help" ] ; then
    display_help
    exit 0
fi

check_command_parameter_count () {
    MIN_PARAMETERS=$1
    MAX_PARAMETERS=$2
    COUNT=$(echo $PARAMETERS | wc -w)
    if  [ -n $MIN_PARAMETERS ] && [ $COUNT -lt $MIN_PARAMETERS ] ; then
        display_help
        echo ""
        echo "${ERROR_COLOR}Command '$COMMAND' requires a minimum of $MIN_PARAMETERS parameters. $COUNT passed.${NC}"
        exit 1
    fi
    if [ -n $MAX_PARAMETERS ] && [ $COUNT -gt $MAX_PARAMETERS ] ; then
        display_help
        echo ""
        echo "${ERROR_COLOR}Command '$COMMAND' accepts at most $MAX_PARAMETERS parameters. $COUNT passed.${NC}"
        exit 1
    fi
}

detect_package () {
    if test -e debian/changelog ; then
        PACKAGE=`dpkg-parsechangelog --show-field Source`
        PACKAGE_VERSION=`dpkg-parsechangelog --show-field Version`
    fi
}

check_changelog_exists () {
    if ! test -e debian/changelog ; then
        echo "${ERROR_COLOR}No debian/changelog found in $PWD.${NC}"
        echo "If the source code is not available it can be automatically downloaded from the repositories using the 'source' command." 
        exit 1
    fi
}

enter_package () {
    if [ -n "$ORIGINAL_DIRECTORY" ] ; then
        cd $ORIGINAL_DIRECTORY
    fi
    ORIGINAL_DIRECTORY=$PWD
    PACKAGE=$1
    if [ -n "$PACKAGE" ] ; then
        if ! cd $PACKAGE ; then
            echo "${ERROR_COLOR}No directory $PACKAGE.${NC}"
            exit 1
        fi
    fi
}

enter_or_detect_package () {
    PACKAGE=$1
    if [ -n "$PACKAGE" ] ; then
        enter_package $PACKAGE
        detect_package
        check_changelog_exists
    else
        detect_package
        check_changelog_exists
    fi
}

enter_new_or_detect_package() {
    PACKAGE=$1
    if [ -n "$PACKAGE" ] ; then
        mkdir -p $PACKAGE
        enter_package $PACKAGE
        detect_package
    else
        detect_package
        check_changelog_exists
    fi
}

if [ -z "$COMMAND" ] ; then
    NEW_BUILD=1
    detect_package
    check_changelog_exists
    variables
    echo "${POSITIVE_COLOR}Building $PACKAGE for $TARGET_ARCH and deploying to device.${NC}"
    new_container
    install_dependencies
    build
    deploy_to_device
else
    PARAMETERS=$@
    case "$COMMAND" in
        setup-lxd)
            check_command_parameter_count 0 0
            if lxd_has_image_or_container ; then
                echo "${ERROR_COLOR}You are already using LXD. Setting it up again will destroy all your existing LXD images and containers.${NC}"
                echo -n "${ERROR_COLOR}Are you sure you wish to continue? (y/n) ${NC}"
                read REPLY
                echo
                if [ "$REPLY" = y ]
                then
                    ALL_LXD_CONTAINERS=$(lxc list -cn | grep -v "\-\-\-\-" | grep -v NAME | cut -d ' ' -f 2)
                    ALL_LXD_IMAGES=$(lxc image list | grep -v "\-\-\-\-" | grep -v ALIAS | cut -d '|' -f 3)
                    if [ -n "$ALL_LXD_CONTAINERS" ] ; then
                        lxc delete -f $ALL_LXD_CONTAINERS
                    fi
                    for image in $ALL_LXD_IMAGES ; do
                        lxc image delete $image
                    done
                else
                    exit 0
                fi
            fi
            setup_lxd
        ;;
        new)
            check_command_parameter_count 0 1
            enter_new_or_detect_package $1
            variables
            new_container
        ;;
        delete)
            check_command_parameter_count 0 1
            enter_or_detect_package $1
            variables
            delete_container
        ;;
        stop)
            enter_or_detect_package $1
            variables
            stop_container
        ;;
        shell)
            check_command_parameter_count 0 1
            enter_new_or_detect_package $1
            variables
            new_container
            shell_container
        ;;
        source)
            PACKAGE=$1
            if [ -z "$PACKAGE" ] ; then
                echo "${ERROR_COLOR}Command 'source' requires a package name: $PROGRAM_NAME source PACKAGE_NAME${NC}"
                exit 1
            fi
            check_command_parameter_count 1 1
            # FIXME: should fail if $PACKAGE already exists and is not empty
            mkdir -p $PACKAGE
            cd $PACKAGE
            variables
            new_container
            get_source_package
        ;;
        dependencies)
            NEW_BUILD=1
            check_command_parameter_count 0 1
            enter_or_detect_package $1
            variables
            new_container
            exec_container rm -f $USERDIR/dependencies_installed
            install_dependencies
        ;;
        build)
            NEW_BUILD=1
            PACKAGES=$@
            if [ -z "$PACKAGES" ] ; then
                enter_or_detect_package $1
                variables
                new_container
                install_dependencies
                build
            else
                if [ -n "$NO_DEB" ] ; then
                    echo "${ERROR_COLOR}Building dependent packages with the --no-deb option is not supported yet.${NC}"
                    exit 1
                fi

                for package in $PACKAGES ; do
                    enter_or_detect_package $package
                    variables
                    new_container
                    if [ -n "$PREVIOUS_BUILD_FOLDER" ] ; then
                        copy_build_to_container $PREVIOUS_BUILD_FOLDER $PREVIOUS_DEBS_TARBALL
                        exec_container rm -f $USERDIR/dependencies_installed
                    fi
                    install_dependencies
                    build
                    PREVIOUS_BUILD_FOLDER=$PWD
                    PREVIOUS_DEBS_TARBALL=$DEBS_TARBALL
                done
            fi
        ;;
        clean)
            check_command_parameter_count 0 1
            enter_or_detect_package $1
            variables
            clean
        ;;
        deploy)
            check_command_parameter_count 0 1
            enter_or_detect_package $1
            variables
            deploy_to_device
        ;;
        run)
            enter_or_detect_package
            variables
            new_container
            exec_container $@
        ;;
        *)
            display_help
            echo ""
            echo "${ERROR_COLOR}error: unknown command: $COMMAND${NC}"
            exit 1
        ;;
    esac
fi;
