#!/bin/bash

#Requirements for this script to work:
#* Make sure that the user who uses the chroot is in group 'src', or change
#  the ${GROUP} variable to a group that contains the user.
#* Add any path of the host system that you want to access inside the chroot
#  to the /etc/schroot/default/fstab file. This is important in particular if
#  your homedir is not in /home.
#* Add this to your /etc/fstab:
#  tmpfs /var/lib/schroot/mount tmpfs defaults,size=3G 0 0
#  tmpfs /var/lib/schroot/unpack tmpfs defaults,size=3G 0 0

#Configuration
: ${ARCH:=amd64}
: ${DIST_RELEASE:=wheezy}
: ${VARIANT:=}
: ${CONF_DIR:=/etc/schroot/chroot.d}
: ${CHROOT_DIR:=/srv/chroot}
: ${ALTERNATIVE_EDITOR:=/usr/bin/vim.basic}
: ${CHROOT_FINAL_HOOK:=/bin/true}
: ${GROUP:=src}
# Additional Variables taken from the environmen
# DATA_DIR
# CHROOT_EXTRA_DEBIAN_PACKAGES

# make the appended variant name more readable
[ -n "$VARIANT" ] && VARIANT="-${VARIANT#-}"

#Automatically generated variables
CHROOTNAME=$DIST_RELEASE-$ARCH$VARIANT
CHNAME=building_$CHROOTNAME
TEMP_CHROOT_CONF=$CONF_DIR/$CHNAME.conf
FINAL_CHROOT_CONF=$CHROOTNAME.conf
ROOT=`pwd`
CHDIR=$ROOT/$CHNAME
USER=`whoami`
COMP_FILENAME=$CHROOTNAME.tar.gz
COMP_FILEPATH=$ROOT/$COMP_FILENAME
TEMP_DATA_DIR=`mktemp -d`
ACTUAL_DATA_DIR=$DATA_DIR
ACTUAL_DATA_DIR=${ACTUAL_DATA_DIR:-$TEMP_DATA_DIR}
SHA1_LIST='
cabal-install-1.18.0.2.tar.gz 2d1f7a48d17b1e02a1e67584a889b2ff4176a773
cabal-install-1.22.4.0.tar.gz b98eea96d321cdeed83a201c192dac116e786ec2
ghc-7.6.3-i386-unknown-linux.tar.bz2 f042b4171a2d4745137f2e425e6949c185f8ea14
ghc-7.6.3-x86_64-unknown-linux.tar.bz2 46ec3f3352ff57fba0dcbc8d9c20f7bcb6924b77
ghc-7.8.4-i386-unknown-linux-deb7.tar.bz2 4f523f854c37a43b738359506a89a37a9fa9fc5f
ghc-7.8.4-x86_64-unknown-linux-deb7.tar.bz2 3f68321b064e5c1ffcb05838b85bcc00aa2315b4
'

# export all variables needed in the schroot
export ARCH SHA1_LIST

# Use gzip --rsyncable if available, to speed up transfers of generated files
# The environment variable GZIP is read automatically by 'gzip',
# see ENVIRONMENT in gzip(1).
gzip --rsyncable </dev/null >/dev/null 2>&1 && export GZIP="--rsyncable"

#Runnability checks
if [ $USER != 'root' ]
then
  echo "This script requires root permissions to run"
  exit
fi

if [ -f $TEMP_CHROOT_CONF ]
then
  echo "The configuration file name for the temporary chroot"
  echo "  $TEMP_CHROOT_CONF"
  echo "already exists."
  echo "Remove it or change the CHNAME value in the script."
  exit
fi

#Create configuration dir and files if they do not exist
if [ ! -d $ACTUAL_DATA_DIR ]
then
  mkdir $ACTUAL_DATA_DIR
  echo "The data directory"
  echo "  $ACTUAL_DATA_DIR"
  echo "has been created."
fi

if [ ! -f $ACTUAL_DATA_DIR/final.schroot.conf.in ]
then
  cat <<END >$ACTUAL_DATA_DIR/final.schroot.conf.in
[${CHROOTNAME}]
description=Debian ${DIST_RELEASE} ${ARCH}
groups=${GROUP}
source-root-groups=root
type=file
file=${CHROOT_DIR}/${COMP_FILENAME}
END
  echo "The file"
  echo " $ACTUAL_DATA_DIR/final.schroot.conf.in"
  echo "has been created with default configurations."
fi

if [ ! -f $ACTUAL_DATA_DIR/temp.schroot.conf.in ]
then
  cat <<END >$ACTUAL_DATA_DIR/temp.schroot.conf.in
[${CHNAME}]
description=Debian ${DIST_RELEASE}${VARIANT} ${ARCH}
directory=${CHDIR}
groups=${GROUP}
users=root
type=directory
END
  echo "The file"
  echo " $ACTUAL_DATA_DIR/temp.schroot.conf.in"
  echo "has been created with default configurations."
fi

#Stop on errors
set -e

#Cleanup
rm -rf $CHDIR
mkdir $CHDIR

#Install tools for building chroots
apt-get install -y schroot debootstrap

shopt -s expand_aliases
alias in_chroot='schroot -c $CHNAME -d / '
function subst_variables {
  sed \
    -e "s/\${ARCH}/$ARCH/" \
    -e "s*\${CHDIR}*$CHDIR*" \
    -e "s/\${CHNAME}/$CHNAME/" \
    -e "s/\${CHROOTNAME}/$CHROOTNAME/" \
    -e "s*\${CHROOT_DIR}*$CHROOT_DIR*" \
    -e "s/\${COMP_FILENAME}/$COMP_FILENAME/" \
    -e "s/\${DIST_RELEASE}/$DIST_RELEASE/" $@
}

#Generate chroot configurations
cat $ACTUAL_DATA_DIR/temp.schroot.conf.in | subst_variables > $TEMP_CHROOT_CONF
cat $ACTUAL_DATA_DIR/final.schroot.conf.in | subst_variables > $FINAL_CHROOT_CONF

#Install the base system
debootstrap --arch $ARCH $DIST_RELEASE $CHDIR

APT_INSTALL="apt-get install -y --no-install-recommends"

if [ $DIST_RELEASE = squeeze ]
then
  echo "deb http://backports.debian.org/debian-backports" \
       "$DIST_RELEASE-backports main contrib non-free" \
       > $CHDIR/etc/apt/sources.list.d/backports.list
fi

#Install all the packages
in_chroot -- \
  apt-get update


# Functions for downloading and checking Haskell core components.
# The functions run commands within the schroot.

# arguments : file_name expected_sha1
function verify_sha1 {
  local SUM="$( in_chroot -- sha1sum "$1" | awk '{print $1;exit}' )"
  if [ "$SUM" != "$2" ] ; then
    echo "ERROR: The SHA1 sum $SUM of $1 doesn't match $2." >&2
    return 1
  else
    echo "SHA1 of $1 verified correct."
  fi
}

# arguments: URL
function lookup_sha1 {
  grep -o "${1##*/}"'\s\+[0-9a-fA-F]*' <<<"$SHA1_LIST" | awk '{print $2;exit}'
}

# arguments : file_name URL
function download {
  local FNAME="$1"
  local URL="$2"
  in_chroot -- wget --no-check-certificate --output-document="$FNAME" "$URL"
  verify_sha1 "$FNAME" "$( lookup_sha1 "$URL" )"
}

function install_ghc {
  local GHC_ARCH=$ARCH
  local TDIR=$( schroot -c $CHNAME -d / -- mktemp -d )
  [ -n "$TDIR" ]
  if [ "$ARCH" == "amd64" ] ; then
    download "$TDIR"/ghc.tar.bz2 \
      http://www.haskell.org/ghc/dist/${GHC_VERSION}/ghc-${GHC_VERSION}-x86_64-unknown-linux${GHC_VARIANT}.tar.bz2
  elif [ "$ARCH" == "i386" ] ; then
    download "$TDIR"/ghc.tar.bz2 \
      http://www.haskell.org/ghc/dist/${GHC_VERSION}/ghc-${GHC_VERSION}-i386-unknown-linux${GHC_VARIANT}.tar.bz2
  else
    echo "Don't know what GHC to download for architecture $ARCH" >&2
    return 1
  fi
  schroot -c $CHNAME -d "$TDIR" -- \
    tar xjf ghc.tar.bz2
  schroot -c $CHNAME -d "$TDIR/ghc-${GHC_VERSION}" -- \
    ./configure --prefix=/usr/local
  schroot -c $CHNAME -d "$TDIR/ghc-${GHC_VERSION}" -- \
     make install
  schroot -c $CHNAME -d "/" -- \
    rm -rf "$TDIR"
}

function install_cabal {
  local TDIR=$( schroot -c $CHNAME -d / -- mktemp -d )
  [ -n "$TDIR" ]
  download "$TDIR"/cabal-install.tar.gz \
    http://www.haskell.org/cabal/release/cabal-install-${CABAL_INSTALL_VERSION}/cabal-install-${CABAL_INSTALL_VERSION}.tar.gz
  schroot -c $CHNAME -d "$TDIR" -- \
    tar xzf cabal-install.tar.gz
  schroot -c $CHNAME -d "$TDIR/cabal-install-${CABAL_INSTALL_VERSION}" -- \
    bash -c 'EXTRA_CONFIGURE_OPTS="--enable-library-profiling" ./bootstrap.sh --global'
  schroot -c $CHNAME -d "/" -- \
    rm -rf "$TDIR"
}


case ${DIST_RELEASE}${VARIANT} in

  squeeze)

    GHC_VERSION="7.6.3"
    GHC_VARIANT=""
    CABAL_INSTALL_VERSION="1.18.0.2"
    CABAL_LIB_VERSION=">=1.18.0 && <1.19"
    export GHC_VERSION GHC_VARIANT CABAL_INSTALL_VERSION

    # do not install libghc6-network-dev, since it's too old, and just
    # confuses the dependencies
    in_chroot -- \
      $APT_INSTALL \
        autoconf automake \
        zlib1g-dev \
        libgmp3-dev \
        libcurl4-gnutls-dev \
        libpcre3-dev \
        happy \
        hscolour pandoc \
        graphviz qemu-utils \
        python-docutils \
        python-pyparsing \
        python-pyinotify \
        python-pycurl \
        python-ipaddr \
        python-yaml \
        python-paramiko

    in_chroot -- \
      $APT_INSTALL python-setuptools python-dev build-essential

    in_chroot -- \
      easy_install \
        unittest2==0.5.1 \
        logilab-astng==0.24.1 \
        logilab-common==0.58.3 \
        pylint==0.26.0

    in_chroot -- \
      easy_install \
        sphinx==1.1.3 \
        pep8==1.3.3 \
        coverage==3.4 \
        bitarray==0.8.0

    install_ghc

    install_cabal

    in_chroot -- \
      cabal update

    # sinec we're using Cabal >=1.16, we can use the parallel install option
    in_chroot -- \
      cabal install --global -j --enable-library-profiling \
        attoparsec-0.11.1.0 \
        base64-bytestring-1.0.0.1 \
        blaze-builder-0.3.3.2 \
        case-insensitive-1.1.0.3 \
        Crypto-4.2.5.1 \
        curl-1.3.8 \
        happy \
        hashable-1.2.1.0 \
        hinotify-0.3.6 \
        hscolour-1.20.3 \
        hslogger-1.2.3 \
        json-0.7 \
        lifted-base-0.2.2.0 \
        lens-4.0.4 \
        MonadCatchIO-transformers-0.3.0.0 \
        network-2.4.1.2 \
        parallel-3.2.0.4 \
        parsec-3.1.3 \
        regex-pcre-0.94.4 \
        temporary-1.2.0.1 \
        vector-0.10.9.1 \
        zlib-0.5.4.1 \
        \
        'hlint>=1.9.12' \
        HUnit-1.2.5.2 \
        QuickCheck-2.6 \
        test-framework-0.8.0.3 \
        test-framework-hunit-0.3.0.1 \
        test-framework-quickcheck2-0.3.0.2 \
        \
        snap-server-0.9.4.0 \
        PSQueue-1.1 \
        \
        "Cabal $CABAL_LIB_VERSION" \
        cabal-file-th-0.2.3 \
        shelltestrunner

    #Install selected packages from backports
    in_chroot -- \
      $APT_INSTALL -t squeeze-backports \
        git \
        git-email \
        vim \
        exuberant-ctags

;;

  wheezy)

    in_chroot -- \
      $APT_INSTALL \
      autoconf automake ghc ghc-haddock libghc-network-dev \
      libghc-test-framework{,-hunit,-quickcheck2}-dev \
      libghc-json-dev libghc-curl-dev libghc-hinotify-dev \
      libghc-parallel-dev libghc-utf8-string-dev \
      libghc-hslogger-dev libghc-crypto-dev \
      libghc-regex-pcre-dev libghc-attoparsec-dev \
      libghc-vector-dev libghc-temporary-dev \
      libghc-snap-server-dev libpcre3 libpcre3-dev happy hscolour pandoc \
      libghc-zlib-dev libghc-psqueue-dev \
      cabal-install \
      python-setuptools python-sphinx python-epydoc graphviz python-pyparsing \
      python-pycurl python-paramiko \
      python-bitarray python-ipaddr python-yaml qemu-utils python-coverage pep8 \
      shelltestrunner python-dev openssh-client vim git git-email exuberant-ctags

    # We need version 0.9.4 of pyinotify because the packaged version, 0.9.3, is
    # incompatibile with the packaged version of python-epydoc 3.0.1.
    # Reason: a logger class in pyinotify calculates its superclasses at
    # runtime, which clashes with python-epydoc's static analysis phase.
    #
    # Problem introduced in:
    #   https://github.com/seb-m/pyinotify/commit/2c7e8f8959d2f8528e0d90847df360
    # and "fixed" in:
    #   https://github.com/seb-m/pyinotify/commit/98c5f41a6e2e90827a63ff1b878596

    in_chroot -- \
      easy_install \
        logilab-astng==0.24.1 \
        logilab-common==0.58.3 \
        pylint==0.26.0 \
        pep8==1.3.3

    in_chroot -- \
      easy_install pyinotify==0.9.4

     in_chroot -- \
       cabal update

     in_chroot -- \
       cabal install --global \
        'base64-bytestring>=1' \
        lens-3.10.2 \
        'lifted-base>=0.1.2' \
        'hlint>=1.9.12'
;;

  jessie)

    in_chroot -- \
      $APT_INSTALL \
      autoconf automake ghc ghc-haddock libghc-network-dev \
      libghc-test-framework{,-hunit,-quickcheck2}-dev \
      libghc-json-dev libghc-curl-dev libghc-hinotify-dev \
      libghc-parallel-dev libghc-utf8-string-dev \
      libghc-hslogger-dev libghc-crypto-dev \
      libghc-regex-pcre-dev libghc-attoparsec-dev \
      libghc-vector-dev libghc-temporary-dev \
      libghc-snap-server-dev libpcre3 libpcre3-dev happy hscolour pandoc \
      libghc-zlib-dev libghc-psqueue-dev \
      libghc-base64-bytestring-dev libghc-lens-dev libghc-lifted-base-dev \
      libghc-cabal-dev \
      cabal-install \
      python-setuptools python-sphinx python-epydoc graphviz python-pyparsing \
      python-pycurl python-pyinotify python-paramiko \
      python-bitarray python-ipaddr python-yaml qemu-utils python-coverage pep8 \
      shelltestrunner python-dev pylint openssh-client \
      vim git git-email exuberant-ctags

    in_chroot -- \
      cabal update

    in_chroot -- \
      cabal install --global \
       'hlint>=1.9.12'
;;

  jessie-ghc78)

    GHC_VERSION="7.8.4"
    GHC_VARIANT="-deb7"
    CABAL_INSTALL_VERSION="1.22.4.0"
    # the version of the Cabal library below must match the version used by
    # CABAL_INSTALL_VERSION, see the dependencies of cabal-install
    CABAL_LIB_VERSION=">=1.22.2 && <1.23"
    export GHC_VERSION GHC_VARIANT CABAL_INSTALL_VERSION

    in_chroot -- \
      $APT_INSTALL \
        autoconf automake \
        zlib1g-dev \
        libgmp3-dev \
        libcurl4-openssl-dev \
        libpcre3-dev \
        happy \
        hlint hscolour pandoc \
        graphviz qemu-utils \
        python-docutils \
        python-pyparsing \
        python-pyinotify \
        python-pycurl \
        python-ipaddr \
        python-yaml \
        python-paramiko \
        git \
        git-email \
        vim

    in_chroot -- \
      $APT_INSTALL python-setuptools python-dev build-essential

    in_chroot -- \
      easy_install \
        logilab-astng==0.24.1 \
        logilab-common==0.58.3 \
        pylint==0.26.0

    in_chroot -- \
      easy_install \
        sphinx==1.1.3 \
        pep8==1.3.3 \
        coverage==3.4 \
        bitarray==0.8.0

    install_ghc

    install_cabal

    in_chroot -- \
      cabal update

    # since we're using Cabal >=1.16, we can use the parallel install option
    in_chroot -- \
      cabal install --global -j --enable-library-profiling \
        attoparsec==0.12.1.6 \
        base64-bytestring==1.0.0.1 \
        blaze-builder==0.4.0.1 \
        case-insensitive==1.2.0.4 \
        Crypto==4.2.5.1 \
        curl==1.3.8 \
        happy==1.19.5 \
        hashable==1.2.3.2 \
        hinotify==0.3.7 \
        hscolour==1.23 \
        hslogger==1.2.8 \
        json==0.9.1 \
        lifted-base==0.2.3.6 \
        lens==4.9.1 \
        MonadCatchIO-transformers==0.3.1.3 \
        network==2.6.0.2 \
        parallel==3.2.0.6 \
        parsec==3.1.7 \
        regex-pcre==0.94.4 \
        temporary==1.2.0.3 \
        vector==0.10.12.3 \
        zlib==0.5.4.2 \
        \
        hlint==1.9.20 \
        HUnit==1.2.5.2 \
        QuickCheck==2.8.1 \
        test-framework==0.8.1.1 \
        test-framework-hunit==0.3.0.1 \
        test-framework-quickcheck2==0.3.0.3 \
        \
        snap-server==0.9.5.1 \
        \
        "Cabal $CABAL_LIB_VERSION" \
        cabal-file-th==0.2.3 \
        shelltestrunner==1.3.5
;;

  precise)
    # ghc, git-email and other dependencies are hosted in the universe
    # repository, which is not enabled by default.
    echo "Adding universe repository..."
    cat > $CHDIR/etc/apt/sources.list.d/universe.list <<EOF
deb http://archive.ubuntu.com/ubuntu precise universe
EOF
    in_chroot -- \
      apt-get update

    echo "Installing packages"
    in_chroot -- \
      $APT_INSTALL \
      autoconf automake ghc ghc-haddock \
      libghc-curl-dev libghc-hinotify-dev \
      libghc-parallel-dev libghc-utf8-string-dev \
      libghc-crypto-dev \
      libghc-attoparsec-dev \
      libghc-vector-dev libghc-temporary-dev libghc-psqueue-dev \
      libghc-cabal-dev \
      cabal-install \
      libpcre3 libpcre3-dev happy hscolour pandoc \
      python-setuptools python-sphinx python-epydoc graphviz python-pyparsing \
      python-pyinotify python-pycurl python-paramiko \
      python-bitarray python-ipaddr python-yaml qemu-utils python-coverage pep8 \
      python-dev pylint openssh-client vim git git-email exuberant-ctags \
      build-essential

    in_chroot -- \
      easy_install \
        logilab-astng==0.24.1 \
        logilab-common==0.58.3 \
        pylint==0.26.0 \
        pep8==1.3.3

    in_chroot -- \
      cabal update

     # Precise has network-2.4.0.0, which breaks, see
     #   https://github.com/haskell/network/issues/60
     in_chroot -- \
       cabal install --global \
        'base64-bytestring>=1' \
        hslogger-1.2.3 \
        'hlint>=1.9.12' \
        json-0.7 \
        lens-3.10.2 \
        'lifted-base>=0.1.2' \
        'network>=2.4.0.1' \
        'regex-pcre>=0.94.4' \
        parsec-3.1.3 \
        shelltestrunner \
        'snap-server>=0.8.1' \
        test-framework-0.8.0.3 \
        test-framework-hunit-0.3.0.1 \
        test-framework-quickcheck2-0.3.0.2 \
        'transformers>=0.3.0.0'
    ;;

  *)
    in_chroot -- \
      $APT_INSTALL \
      autoconf automake ghc ghc-haddock libghc-network-dev \
      libghc-test-framework{,-hunit,-quickcheck2}-dev \
      libghc-json-dev libghc-curl-dev libghc-hinotify-dev \
      libghc-parallel-dev libghc-utf8-string-dev \
      libghc-hslogger-dev libghc-crypto-dev \
      libghc-regex-pcre-dev libghc-attoparsec-dev \
      libghc-vector-dev libghc-temporary-dev libghc-psqueue-dev \
      libghc-snap-server-dev libpcre3 libpcre3-dev happy hscolour pandoc \
      libghc-lens-dev libghc-lifted-base-dev \
      libghc-cabal-dev \
      cabal-install \
      libghc-base64-bytestring-dev \
      python-setuptools python-sphinx python-epydoc graphviz python-pyparsing \
      python-pyinotify python-pycurl python-paramiko \
      python-bitarray python-ipaddr python-yaml qemu-utils python-coverage pep8 \
      shelltestrunner python-dev pylint openssh-client \
      vim git git-email exuberant-ctags \
      build-essential

    in_chroot -- \
      cabal update

     in_chroot -- \
       cabal install --global \
        'hlint>=1.9.12'
;;
esac

# print what packages and versions are installed:
in_chroot -- \
  cabal list --installed --simple-output

in_chroot -- \
  $APT_INSTALL sudo fakeroot rsync locales less socat

# Configure the locale
case $DIST_RELEASE in
  precise)
    in_chroot -- \
      $APT_INSTALL language-pack-en
    ;;
  *)
    echo "en_US.UTF-8 UTF-8" >> $CHDIR/etc/locale.gen

    in_chroot -- \
      locale-gen
    ;;
esac

in_chroot -- \
  $APT_INSTALL lvm2 ssh bridge-utils iproute iputils-arping \
               ndisc6 python-openssl openssl \
               fping qemu-utils

in_chroot -- \
  easy_install psutil

in_chroot -- \
  easy_install jsonpointer \
    jsonpointer \
    jsonpatch

in_chroot -- \
  $APT_INSTALL \
  python-epydoc debhelper quilt

# extra debian packages

for package in $CHROOT_EXTRA_DEBIAN_PACKAGES
do in_chroot -- \
  $APT_INSTALL $package
done

#Set default editor
in_chroot -- \
  update-alternatives --set editor $ALTERNATIVE_EDITOR

# Final user hook

in_chroot -- $CHROOT_FINAL_HOOK

rm -f $COMP_FILEPATH
echo "Creating compressed schroot image..."
cd $CHDIR
tar czf $COMP_FILEPATH ./*
cd $ROOT

rm -rf $CHDIR
rm -f $TEMP_CHROOT_CONF
rm -rf $TEMP_DATA_DIR

echo "Chroot created. In order to run it:"
echo " * sudo cp $FINAL_CHROOT_CONF $CONF_DIR/$FINAL_CHROOT_CONF"
echo " * sudo mkdir -p $CHROOT_DIR"
echo " * sudo cp $COMP_FILEPATH $CHROOT_DIR/$COMP_FILENAME"
echo "Then run \"schroot -c $CHROOTNAME\""
