#!/bin/bash
#
# $Id: bld.sh,v 1.1 2012/03/29 16:19:25 jlinoff Exp jlinoff $
#
# This downloads, builds and installs the gcc-4.7.0 compiler and
# boost 1.49. It takes handles the dependent packages like gmp-5.0.4,
# mpfr-3.1.0, ppl-0.12 and cloog-0.17.0.
#
# To install gcc-4.7.0 in ~/tmp/gcc-4.7.0/rtf/bin you would run this
# script as follows:
#
#    % # Install in ~/tmp/gcc-4.7.0/rtf/bin
#    % bld.sh ~/tmp/gcc-4.7.0 2>&1 | tee bld.log
#
# If you do not specify a directory, then it will install in the
# current directory which means that following command will also
# install in ~/tmp/gcc-4.7.0/rtf/bin:
#
#    % # Install in ~/tmp/gcc-4.7.0/rtf/bin
#    % mkdir -p ~/tmp/gcc-4.7.0
#    % cd ~/tmp/gcc-4.7.0
#    % bld.sh 2>&1 | tee bld.log
#
# This script creates 4 subdirectories:
#
#    Directory  Description
#    =========  ==================================================
#    archives   This is where the package archives are downloaded.
#    src        This is where the package source is located.
#    bld        This is where the packages are built from source.
#    rtf        This is where the packages are installed.
#
# When the build is complete you can safely remove the archives, bld
# and src directory trees to save disk space.
#

# Execute command with decorations and status testing.
# Usage  : docmd $ar <cmd>
# Example: docmd $ar ls -l
function docmd {
    local ar=$1
    shift
    local cmd=($*)
    echo 
    echo " # ================================================================"
    if [[ "$ar" != "" ]] ; then
	echo " # Archive: $ar"
    fi
    echo " # PWD: "$(pwd)
    echo " # CMD: "${cmd[@]}
    echo " # ================================================================"
    ${cmd[@]}
    local st=$?
    echo "STATUS = $st"
    if (( $st != 0 )) ; then
	exit $st;
    fi
}

# Report an error and exit.
# Usage  : doerr <line1> [<line2> .. <line(n)>]
# Example: doerr "line 1 msg"
# Example: doerr "line 1 msg" "line 2 msg"
function doerr {
    local prefix="ERROR: "
    for ln in "$@" ; do
	echo "${prefix}${ln}"
	prefix="       "
    done
    exit 1
}

# Extract archive information.
# Usage  : ard=( $(extract-ar-info $ar) )
# Example: ard=( $(extract-ar-info $ar) )
#          fn=${ard[1]}
#          ext=${ard[2]}
#          d=${ard[3]}
function extract-ar-info {
    local ar=$1
    local fn=$(basename $ar)
    local ext=$(echo $fn | awk -F. '{print $NF}')
    local d=${fn%.*tar.$ext}
    echo $ar
    echo $fn
    echo $ext
    echo $d
}

# Print a banner for a new section.
# Usage  : banner STEP $ar
# Example: banner "DOWNLOAD" $ar
# Example: banner "BUILD" $ar
function banner {
    local step=$1
    local ard=( $(extract-ar-info $2) )
    local ar=${ard[0]}
    local fn=${ard[1]}
    local ext=${ard[2]}
    local d=${ard[3]}
    echo
    echo '# ================================================================'
    echo "# Step   : $step"
    echo "# Archive: $ar"
    echo "# File   : $fn"
    echo "# Ext    : $ext"
    echo "# Dir    : $d"
    echo '# ================================================================'
}

# Make a group directories
# Usage  : mkdirs <dir1> [<dir2> .. <dir(n)>]
# Example: mkdirs foo bar spam spam/foo/bar
function mkdirs {
    local ds=($*)
    #echo "mkdirs"
    for d in ${ds[@]} ; do
	#echo "  testing $d"
	if [ ! -d $d ] ; then
	    #echo "    creating $d"
	    mkdir -p $d
	fi
    done
}

# ================================================================
# DATA
# ================================================================
# List of archives
# The order is important.
ARS=(
    http://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.14.tar.gz
    ftp://ftp.gmplib.org/pub/gmp-5.0.4/gmp-5.0.4.tar.bz2
    http://www.mpfr.org/mpfr-current/mpfr-3.1.0.tar.bz2
    http://www.multiprecision.org/mpc/download/mpc-0.9.tar.gz
    http://bugseng.com/products/ppl/download/ftp/releases/0.12/ppl-0.12.tar.bz2
    http://www.bastoul.net/cloog/pages/download/cloog-0.17.0.tar.gz
    http://ftp.gnu.org/gnu/gcc/gcc-4.7.0/gcc-4.7.0.tar.bz2
    http://ftp.gnu.org/gnu/binutils/binutils-2.22.tar.bz2
    http://ftp.gnu.org/gnu/glibc/glibc-2.15.tar.bz2
    http://sourceforge.net/projects/boost/files/boost/1.49.0/boost_1_49_0.tar.bz2
)

# ================================================================
# MAIN
# ================================================================
umask 0

# Read the command line argument, if it exists.
ROOTDIR=$(readlink -f .)
if (( $# == 1 )) ; then
    ROOTDIR=$(readlink -f $1)
elif (( $# > 1 )) ; then
    doerr "too many command line arguments ($#), only zero or one is allowed" "foo"
fi

# Setup the directories.
ARDIR="$ROOTDIR/archives"
RTFDIR="$ROOTDIR/rtf"
SRCDIR="$ROOTDIR/src"
BLDDIR="$ROOTDIR/bld"
TSTDIR="$SRCDIR/LOCAL-TEST"

echo
echo "# ================================================================"
echo '# Version    : $Id: bld.sh,v 1.1 2012/03/29 16:19:25 jlinoff Exp jlinoff $'
echo "# RootDir    : $ROOTDIR"
echo "# ArchiveDir : $ARDIR"
echo "# RtfDir     : $RTFDIR"
echo "# SrcDir     : $SRCDIR"
echo "# BldDir     : $BLDDIR"
echo "# TstDir     : $TSTDIR"
echo "# Gcc        : "$(which gcc)
echo "# GccVersion : "$(gcc --version | head -1)
echo "# Hostname   : "$(hostname)
echo "# O/S        : "$(uname -s -r -v -m)
echo "# Date       : "$(date)
echo "# ================================================================"

mkdirs $ARDIR $RTFDIR $SRCDIR $BLDDIR

# ================================================================
# Download
# ================================================================
for ar in ${ARS[@]} ; do
    banner 'DOWNLOAD' $ar
    ard=( $(extract-ar-info $ar) )
    fn=${ard[1]}
    ext=${ard[2]}
    d=${ard[3]}
    if [  -f "${ARDIR}/$fn" ] ; then
	echo "skipping $fn"
    else
	# get
	docmd $ar wget $ar -O "${ARDIR}/$fn"
    fi
done

# ================================================================
# Extract
# ================================================================
for ar in ${ARS[@]} ; do
    banner 'EXTRACT' $ar
    ard=( $(extract-ar-info $ar) )
    fn=${ard[1]}
    ext=${ard[2]}
    d=${ard[3]}
    sd="$SRCDIR/$d"
    if [ -d $sd ] ; then
	echo "skipping $fn"
    else
	# unpack
	pushd $SRCDIR
	case "$ext" in
	    "bz2")
		docmd $ar tar jxf ${ARDIR}/$fn
		;;
	    "gz")
		docmd $ar tar zxf ${ARDIR}/$fn
		;;
	    "tar")
		docmd $ar tar xf ${ARDIR}/$fn
		;;
	    *)
		doerr "unrecognized extension: $ext" "Can't continue."
		;;
	esac
	popd
	if [ ! -d $sd ] ;  then
	    # Some archives (like gcc-g++) overlay. We create a dummy
	    # directory to avoid extracting them every time.
	    mkdir -p $sd
	fi
    fi
done

# ================================================================
# Build
# ================================================================
for ar in ${ARS[@]} ; do
    banner 'BUILD' $ar
    ard=( $(extract-ar-info $ar) )
    fn=${ard[1]}
    ext=${ard[2]}
    d=${ard[3]}
    sd="$SRCDIR/$d"
    bd="$BLDDIR/$d"
    if [ -d $bd ] ; then
	echo "skipping $sd"
    else
        # Build
	if [ $(expr match "$fn" 'gcc-g++') -ne 0 ] ; then
            # Don't build/configure the gcc-g++ package explicitly because
	    # it is part of the regular gcc package.
	    echo "skipping $sd"
	    # Dummy
	    continue
	fi

        # Set the CONF_ARGS
	in_bld=1 # build in the bld area
	run_conf=1
	run_bootstrap=0
	case "$d" in
	    binutils-*)
		CONF_ARGS=(
		    --disable-cloog-version-check
		    --disable-werror
		    --enable-cloog-backend=isl
		    --enable-lto
		    --enable-libssp
		    --enable-gold
		    --prefix=${RTFDIR}
		    --with-cloog=${RTFDIR}
		    --with-gmp=${RTFDIR}
		    --with-mlgmp=${RTFDIR}
		    --with-mpc=${RTFDIR}
		    --with-mpfr=${RTFDIR}
		    --with-ppl=${RTFDIR}
		    CC=${RTFDIR}/bin/gcc
		    CXX=${RTFDIR}/bin/g++
		)
		;;

	    boost_*)
		# The boost configuration scheme requires
		# that the build occur in the source directory.
		run_conf=0
		run_bootstrap=1
		in_bld=0
		CONF_ARGS=(
		    --prefix=${RTFDIR}
		    --with-python=python2.7
		    CC=${RTFDIR}/bin/gcc
		    CXX=${RTFDIR}/bin/g++
		)
		;;

	    cloog-*)
		GMPDIR=$(ls -1d ${BLDDIR}/gmp-*)
		CONF_ARGS=(
		    --prefix=${RTFDIR}
		    --with-gmp-builddir=${GMPDIR}
		    --with-gmp=build
		    --with-isl=${RTFDIR}
		)
		;;

	    gcc-*)
		# We are using a newer version of CLooG (0.17.0).
		# I have also made stack protection available
		# (similar to DEP in windows).
		CONF_ARGS=(
		    --disable-cloog-version-check
		    --enable-cloog-backend=isl
		    --enable-languages='c,c++'
		    --enable-lto
		    --enable-libssp
		    --prefix=${RTFDIR}
		    --with-cloog=${RTFDIR}
		    --with-gmp=${RTFDIR}
		    --with-mlgmp=${RTFDIR}
		    --with-mpc=${RTFDIR}
		    --with-mpfr=${RTFDIR}
		    --with-ppl=${RTFDIR}
		)
		# We need to make a special fix here for
		# supporting CLooG 0.17.0. Between 0.16.0
		# and 0.17.0 they changed LANGUAGE_C to
		# CLOOG_LANGUAGE_C which was the correct
		# thing to do but it means that we have
		# to change one of the source files in
		# the distribution.
		src="$sd/gcc/graphite-clast-to-gimple.c"
		if [ -f $src ] ; then
		    if [ ! -f $src.orig ] ; then
			mv $src $src.orig
			sed -e 's/ LANGUAGE_C/ CLOOG_LANGUAGE_C/g' \
			    $src.orig > $src
		    fi
		fi
		;;

	    glibc-*)
		CONF_ARGS=(
		    --prefix=${RTFDIR}
		    CC=${RTFDIR}/bin/gcc
		    CXX=${RTFDIR}/bin/g++
		)
		;;

	    gmp-*)
		CONF_ARGS=(
		    --enable-cxx
		    --prefix=${RTFDIR}
		)
		;;

	    libiconv-*)
		CONF_ARGS=(
		    --prefix=${RTFDIR}
		)
		;;

	    mpc-*)
		CONF_ARGS=(
		    --prefix=${RTFDIR}
		    --with-gmp=${RTFDIR}
		    --with-mpfr=${RTFDIR}
		)
		;;

	    mpfr-*)
		CONF_ARGS=(
		    --prefix=${RTFDIR}
		    --with-gmp=${RTFDIR}
		)
		;;

	    ppl-*)
		CONF_ARGS=(
		    --prefix=${RTFDIR}
		    --with-gmp=${RTFDIR}
		)
		;;

	    *)
		doerr "unrecognized package: $d"
		;;
	esac

	if (( $in_bld )) ; then
	    mkdir -p $bd
	    pushd $bd
	else
	    echo "NOTE: This package must be built in the source directory."
	    pushd $sd
	fi
	if (( $run_conf )) ; then
	    docmd $ar $sd/configure --help
	    docmd $ar $sd/configure ${CONF_ARGS[@]}
	    docmd $ar make
	    docmd $ar make install
	fi
	if (( $run_bootstrap )) ; then
	    export PATH="${RTFDIR}/bin:${PATH}"
	    export LD_LIBRARY_PATH="${RTFDIR}/lib:${LD_LIBRARY_PATH}"
	    which g++

	    docmd $ar $sd/bootstrap.sh --help
	    docmd $ar $sd/bootstrap.sh ${CONF_ARGS[@]}
	    ./b2
	    ./b2 install

	    unset PATH
	    unset LD_LIBRARY_PATH
	fi

	# Redo the tests if anything changed.
	rm -rf $TSTDIR
	popd
    fi
done

# ================================================================
# Test
# ================================================================
if [ -d $TSTDIR ] ; then
    echo "skipping tests"
else
    export PATH="${RTFDIR}/bin:${PATH}"
    export LD_LIBRARY_PATH="${RTFDIR}/lib:${LD_LIBRARY_PATH}"
    mkdir -p $TSTDIR
    pushd $TSTDIR
    docmd "LOCAL TEST  1" which g++
    docmd "LOCAL TEST  2" which gcc
    docmd "LOCAL TEST  3" which c++
    docmd "LOCAL TEST  4" g++ --version

    # Simple aliveness test.
    cat >test1.cc <<EOF
#include <iostream>
using namespace std;
int main()
{
  cout << "IO works" << endl;
  return 0;
}
EOF
    docmd "LOCAL TEST  5" g++ -O3 -Wall -o test1.bin test1.cc
    docmd "LOCAL TEST  6" ./test1.bin

    docmd "LOCAL TEST  7" g++ -g -Wall -o test1.dbg test1.cc
    docmd "LOCAL TEST  8" ./test1.dbg

    # Simple aliveness test for boost.
    cat >test2.cc <<EOF
#include <iostream>
#include <boost/algorithm/string.hpp>
using namespace std;
using namespace boost;
int main()
{
  string s1(" hello world! ");
  cout << "value      : '" << s1 << "'" <<endl;

  to_upper(s1);
  cout << "to_upper() : '" << s1 << "'" <<endl;

  trim(s1);
  cout << "trim()     : '" << s1 << "'" <<endl;

  return 0;
}
EOF
    docmd "LOCAL TEST  9" g++ -O3 -Wall -o test2.bin test2.cc
    docmd "LOCAL TEST 10" ./test2.bin

    docmd "LOCAL TEST 11" g++ -g -Wall -o test2.dbg test2.cc
    docmd "LOCAL TEST 12" ./test2.dbg

    docmd "LOCAL TEST" ls -l 
    popd
fi

