#!/bin/sh

# Abort execution as soon as an error is encountered
# That way the script do not let the user think the process completed correctly
# and leave the opportunity to fix the problem and restart compilation where
# it stopped
set -e

# this is where this script will store downloaded files and check for already
# downloaded files
dlwhere="${RBDEV_DOWNLOAD:-/tmp/rbdev-dl}"

# will append the target string to the prefix dir mentioned here
# Note that the user running this script must be able to do make install in
# this given prefix directory. Also make sure that this given root dir
# exists.
prefix="${RBDEV_PREFIX:-/usr/local}"

# This directory is used to extract all files and to build everything in. It
# must not exist before this script is invoked (as a security measure).
builddir="${RBDEV_BUILD:-/tmp/rbdev-build}"

# This script needs to use GNU Make. On Linux systems, GNU Make is invoked
# by running the "make" command, on most BSD systems, GNU Make is invoked
# by running the "gmake" command. Set the "make" variable accordingly.
if [ -f "`which gmake 2>/dev/null`" ]; then
    make="gmake"
else
    make="make"
fi

if [ -z $GNU_MIRROR ] ; then
    GNU_MIRROR=http://mirrors.kernel.org/gnu
fi

# These are the tools this script requires and depends upon.
reqtools="gcc bzip2 gzip make patch makeinfo automake libtool autoconf flex bison"

##############################################################################
# Functions:

findtool(){
  file="$1"

  IFS=":"
  for path in $PATH
  do
    # echo "Checks for $file in $path" >&2
    if test -f "$path/$file"; then
      echo "$path/$file"
      return
    fi
  done
}

input() {
    read response
    echo $response
}

#$1 file
#$2 URL"root
getfile() {
  if test -f $dlwhere/$1; then
    echo "ROCKBOXDEV: Skipping download of $2/$1: File already exists"
    return
  fi
  tool=`findtool curl`
  if test -z "$tool"; then
    tool=`findtool wget`
    if test -n "$tool"; then
      # wget download
      echo "ROCKBOXDEV: Downloading $2/$1 using wget"
      $tool -T 60 -O $dlwhere/$1 $2/$1
    fi
  else
     # curl download
      echo "ROCKBOXDEV: Downloading $2/$1 using curl"
     $tool -Lo $dlwhere/$1 $2/$1
  fi

  if [ $? -ne 0 ] ; then
      echo "ROCKBOXDEV: couldn't download the file!"
      echo "ROCKBOXDEV: check your internet connection"
      exit
  fi

  if test -z "$tool"; then 
    echo "ROCKBOXDEV: No downloader tool found!"
    echo "ROCKBOXDEV: Please install curl or wget and re-run the script"
    exit
  fi
}


build() {
    toolname="$1"
    target="$2"
    version="$3"
    patch="$4"
    configure_params="$5"
    needs_libs="$6"

    patch_url="http://www.rockbox.org/gcc"

    case $toolname in
        gcc)
            file="gcc-core-$version.tar.bz2"
            url="$GNU_MIRROR/gcc/gcc-$version"
            ;;

        binutils)
            file="binutils-$version.tar.bz2"
            url="$GNU_MIRROR/binutils"
            ;;

        crosstool-ng)
            file="crosstool-ng-$version.tar.bz2"
            url="http://crosstool-ng.org/download/crosstool-ng"
            ;;
        *)
            echo "ROCKBOXDEV: Bad toolname $toolname"
            exit
            ;;
    esac
    
    # create build directory
    if test -d $builddir; then
        if test ! -w $builddir; then
            echo "ROCKBOXDEV: No write permission for $builddir"
            exit
        fi
    else
        mkdir -p $builddir
    fi

    # download source tarball
    if test ! -f "$dlwhere/$file"; then
        getfile "$file" "$url"
    fi

    # download patch
    for p in $patch; do
        if test ! -f "$dlwhere/$p"; then
            getfile "$p" "$patch_url"
        fi
    done

    cd $builddir

    echo "ROCKBOXDEV: extracting $file"
    tar xjf $dlwhere/$file

    # do we have a patch?
    for p in $patch; do
        echo "ROCKBOXDEV: applying patch $p"

        # apply the patch
        (cd $builddir/$toolname-$version && patch -p1 < "$dlwhere/$p")

        # check if the patch applied cleanly
        if [ $? -gt 0 ]; then
            echo "ROCKBOXDEV: failed to apply patch $p"
            exit
        fi
    done

    # kludge to avoid having to install GMP, MPFR and MPC, for new gcc
    if test -n "$needs_libs"; then
        cd "gcc-$version"
        if (echo $needs_libs | grep -q gmp && test ! -d gmp); then
            echo "ROCKBOXDEV: Getting GMP"
            if test ! -f $dlwhere/gmp-4.3.2.tar.bz2; then
                getfile "gmp-4.3.2.tar.bz2" "$GNU_MIRROR/gmp"
            fi
            tar xjf $dlwhere/gmp-4.3.2.tar.bz2
            ln -s gmp-4.3.2 gmp
        fi

        if (echo $needs_libs | grep -q mpfr && test ! -d mpfr); then
            echo "ROCKBOXDEV: Getting MPFR"
            if test ! -f $dlwhere/mpfr-2.4.2.tar.bz2; then
                getfile "mpfr-2.4.2.tar.bz2" "$GNU_MIRROR/mpfr"
            fi
            tar xjf $dlwhere/mpfr-2.4.2.tar.bz2
            ln -s mpfr-2.4.2 mpfr
        fi

        if (echo $needs_libs | grep -q mpc && test ! -d mpc); then
            echo "ROCKBOXDEV: Getting MPC"
            if test ! -f $dlwhere/mpc-0.8.1.tar.gz; then
                getfile "mpc-0.8.1.tar.gz" "http://www.multiprecision.org/mpc/download"
            fi
            tar xzf $dlwhere/mpc-0.8.1.tar.gz
            ln -s mpc-0.8.1 mpc
        fi
        cd $builddir
    fi

    echo "ROCKBOXDEV: mkdir build-$toolname"
    mkdir build-$toolname

    echo "ROCKBOXDEV: cd build-$toolname"
    cd build-$toolname

    echo "ROCKBOXDEV: $toolname/configure"
    case $toolname in
        crosstool-ng) # ct-ng doesnt support out-of-tree build and the src folder is named differently
            toolname="crosstool-ng"
            cp -r ../$toolname-$version/* ../$toolname-$version/.version .
            ./configure --prefix=$prefix $configure_params
        ;;
        *)
            CFLAGS='-U_FORTIFY_SOURCE -fgnu89-inline' ../$toolname-$version/configure --target=$target --prefix=$prefix --enable-languages=c --disable-libssp --disable-docs $configure_params
        ;;
    esac

    echo "ROCKBOXDEV: $toolname/make"
    $make

    echo "ROCKBOXDEV: $toolname/make install"
    $make install

    echo "ROCKBOXDEV: rm -rf build-$toolname $toolname-$version"
    cd ..
    rm -rf build-$toolname $toolname-$version
}


make_ctng() {
    if test -f "`which ct-ng 2>/dev/null`"; then
        ctng="ct-ng"
    else
        ctng=""
    fi

    if test ! -n "$ctng"; then
        if test ! -f "$prefix/bin/ct-ng"; then # look if we build it already
            build "crosstool-ng" "" "1.13.2" "crosstool-ng-1.13.2.diff"
        fi
    fi
    ctng=`PATH=$prefix/bin:$PATH which ct-ng`
}

build_ctng() {
    ctng_target="$1"
    extra="$2"
    tc_arch="$3"
    tc_host="$4"

    make_ctng

    dlurl="http://www.rockbox.org/gcc/$ctng_target"

    # download 
    getfile "ct-ng-config" "$dlurl"

    test -n "$extra" && getfile "$extra" "$dlurl"
    
    # create build directory
    if test -d $builddir; then
        if test ! -w $builddir; then
            echo "ROCKBOXDEV: No write permission for $builddir"
            exit
        fi
    else
        mkdir -p $builddir
    fi

    # copy config and cd to $builddir
    mkdir $builddir/build-$ctng_target
    ctng_config="$builddir/build-$ctng_target/.config"
    cat "$dlwhere/ct-ng-config" | sed -e "s,\(CT_PREFIX_DIR=\).*,\1$prefix," > $ctng_config
    cd $builddir/build-$ctng_target

    $ctng "build"

    # install extras
    if test -e "$dlwhere/$extra"; then
        # verify the toolchain has sysroot support
        if test -n `cat $ctng_config | grep CT_USE_SYSROOT\=y`; then
            sysroot=`cat $ctng_config | grep CT_SYSROOT_NAME | sed -e 's,CT_SYSROOT_NAME\=\"\([a-zA-Z0-9]*\)\",\1,'`
            tar xf "$dlwhere/$extra" -C "$prefix/$tc_arch-$ctng_target-$tc_host/$sysroot"
        fi
    fi
    
    # cleanup
    cd $builddir
    rm -rf $builddir/build-$ctng_target
}
    
##############################################################################
# Code:

# Verify required tools
for t in $reqtools; do
    tool=`findtool $t`
    if test -z "$tool"; then
        echo "ROCKBOXDEV: \"$t\" is required for this script to work."
        echo "ROCKBOXDEV: Please install \"$t\" and re-run the script."
        exit
    fi
done

echo "Download directory : $dlwhere (set RBDEV_DOWNLOAD to change)"
echo "Install prefix     : $prefix  (set RBDEV_PREFIX to change)"
echo "Build dir          : $builddir (set RBDEV_BUILD to change)"
echo "Make options       : $MAKEFLAGS (set MAKEFLAGS to change)"
echo ""

# Verify download directory
if test -d "$dlwhere"; then
  if ! test -w "$dlwhere"; then
    echo "ROCKBOXDEV: No write permission for $dlwhere"
    exit
  fi
else
  mkdir $dlwhere
  if test $? -ne 0; then
    echo "ROCKBOXDEV: Failed creating directory $dlwhere"
    exit
  fi
fi


# Verify the prefix dir
if test ! -d $prefix; then
  mkdir -p $prefix
  if test $? -ne 0; then
      echo "ROCKBOXDEV: Failed creating directory $prefix"
      exit
  fi
fi
if test ! -w $prefix; then
  echo "ROCKBOXDEV: No write permission for $prefix"
  exit
fi

echo "Select target arch:"
echo "s   - sh       (Archos models)"
echo "m   - m68k     (iriver h1x0/h3x0, iaudio m3/m5/x5 and mpio hd200)"
echo "a   - arm      (ipods, iriver H10, Sansa, D2, Gigabeat, etc)"
echo "i   - mips     (Jz4740 and ATJ-based players)"
echo "r   - arm-app  (Samsung ypr0)"
echo "separate multiple targets with spaces"
echo "(Example: \"s m a\" will build sh, m68k and arm)"
echo ""

selarch=`input`
system=`uname -s`

# add target dir to path to ensure the new binutils are used in gcc build
PATH="$prefix/bin:${PATH}"

for arch in $selarch
do
    echo ""
    case $arch in
        [Ss])
            # For binutils 2.16.1 builtin rules conflict on some systems with a
            # default rule for Objective C. Disable the builtin make rules. See
            # http://sourceware.org/ml/binutils/2005-12/msg00259.html
            export MAKEFLAGS="-r $MAKEFLAGS"
            build "binutils" "sh-elf" "2.16.1" "binutils-2.16.1-texinfo-fix.diff" "--disable-werror"
            build "gcc" "sh-elf" "4.0.3" "gcc-4.0.3-rockbox-1.diff"
            ;;

        [Ii])
            build "binutils" "mipsel-elf" "2.17" "" "--disable-werror"
            patch=""
            if [ "$system" = "Interix" ]; then
                patch="gcc-4.1.2-interix.diff"
            fi
            build "gcc" "mipsel-elf" "4.1.2" "$patch"
            ;;

        [Mm])
            build "binutils" "m68k-elf" "2.20.1" "binutils-2.20.1-texinfo-fix.diff" "--disable-werror"
            build "gcc" "m68k-elf" "4.5.2" "" "--with-arch=cf MAKEINFO=missing" "gmp mpfr mpc"
            ;;

        [Aa])
            binopts=""
            gccopts=""
            case $system in
                Darwin)
                    binopts="--disable-nls"
                    gccopts="--disable-nls"
                    ;;
            esac
            build "binutils" "arm-elf-eabi" "2.20.1" "binutils-2.20.1-ld-thumb-interwork-long-call.diff binutils-2.20.1-texinfo-fix.diff" "$binopts --disable-werror"
            build "gcc" "arm-elf-eabi" "4.4.4" "rockbox-multilibs-noexceptions-arm-elf-eabi-gcc-4.4.2_1.diff" "$gccopts MAKEINFO=missing" "gmp mpfr"
            ;;
        [Rr])
            build_ctng "ypr0" "alsalib.tar.gz" "arm" "linux-gnueabi"
            ;;
        *)
            echo "ROCKBOXDEV: Unsupported architecture option: $arch"
            exit
            ;;
    esac
done

echo ""
echo "ROCKBOXDEV: Done!"
echo ""
echo "ROCKBOXDEV: Make sure your PATH includes $prefix/bin"
echo ""
