#!/usr/bin/env bash

# $Id: build-mspgcc 82 2012-09-06 12:28:33Z ice $

# Copyright (c) 2011, 2012 Tamas Tevesz <ice@extreme.hu>
# 
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
# 
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

# build and install GCC toolchain for MSP430
#
# prerequisites
# - bash
# - wget, GNU patch, GNU make
# - things needed to build binutils, GCC and GDB
#
# environment
# - PREFIX - set to directory where toolchain is to be installed in
#            (defaults to ${HOME}/opt/mspgcc)
# - BUNDLE_SUPPORT_LIBS - if set to `yes', MPC, MPFR and GMP will be
#            will be downloaded, built and installed along with the
#            toolchain (useful for redistribution).

# 2012-9-15 rk added latest FRAM gcc patch and changed defaults for
#           PREFIX and BUNDLE_SUPPORT_LIBS to make it less dependent
#           on external shared libs.

# rm -rf BUILD bin* build.log* gcc* gdb* gm* mp* msp*

set -e

mspgcc_ver="20120406"
binutils_lts_patches=""
gcc_lts_patches="sf3540953 sf3559978"
gdb_lts_patches=""
libc_lts_patches="sf3522752"
mcu_lts_patches="sf3522088"

: ${BUNDLE_SUPPORT_LIBS:-yes}

if [ "${BUNDLE_SUPPORT_LIBS}" = "yes" ]
then
	# Minimum required versions as per gcc/INSTALL/prerequisites.html
	mpc_version="0.8.1"
	mpfr_version="2.4.2"
	gmp_version="4.3.2"
fi

# ---

base_url="http://sourceforge.net/projects/mspgcc/files"
lts_patches_url="${base_url}/Patches/LTS/${mspgcc_ver}"

MSP430_ROOT="$( dirname -- $( readlink -f "${0}" ) )"

ncpus=1
uname_s="$( uname -s )"
case "${uname_s}" in
Linux|CYGWIN_NT)
	G=""
	ncpus="$( grep -c '^processor' /proc/cpuinfo )"
	;;
*BSD)
	if [ "${BUNDLE_SUPPORT_LIBS}" != "yes" ]
	then
		localbase="/usr/local"
		[ "${uname_s}" = "NetBSD" ] && localbase="/usr/pkg"
		GCC_CONFG_ARGS="--with-gmp=${localbase} --with-mpfr=${localbase}"
	fi
	G="g"
	ncpus="$( sysctl -n hw.ncpu )"
	;;
esac

dl()
{
	local fn
	# SF directlinks
	fn="$( basename "${1%/download}" )"
	[ -f "${fn}" ] && return
	wget --content-disposition -qO "${fn}" "${1}"
}

xtr()
{
	local fn="${1}"
	local dn="${2}"
	local extr="j"
	expr "${fn}" : '.*\.gz$' >/dev/null && extr="z"
	[ -d "${dn}" ] && rm -rf ./"${dn}"
	mkdir "${dn}"
	pushd "${dn}" >/dev/null
	${TAR} -x${extr} --strip-components 1 -f ../"${fn}"
	popd >/dev/null
}

if [ ! "${ncpus/[^0-9-]*/}" -o ${ncpus} -lt 1 ]
then
	ncpus=1
fi

[ -d "${PREFIX}" ] && {
	echo "${PREFIX} exists, remove manually"
	exit 1
}

[ -d BUILD ] && rm -rf ./BUILD

exec > >( tee "${MSP430_ROOT}/build.log.$$" )
exec 2>&1

echo '!!! fetch+untar+patch mspgcc'
dl "${base_url}/mspgcc/mspgcc-${mspgcc_ver}.tar.bz2"
[ -d "mspgcc-${mspgcc_ver}"  ] && rm -rf ./"mspgcc-${mspgcc_ver}"
tar xjf "mspgcc-${mspgcc_ver}.tar.bz2"

echo '!!! extract versions'
cd ./"mspgcc-${mspgcc_ver}"
if [ -f msp430-binutils-*.patch ]
then
	set -- $( echo msp430-binutils-*.patch | sed -r 's!msp430-binutils-(.[^-]+)-(.[^.]+).patch!\1 \2!' )
	binutils_upstream_ver="${1}"
	binutils_patch_ver="${2}"
	set --
else
	echo "Can not extract binutils version information"
	exit 1
fi
if [ -f msp430-gcc-*.patch ]
then
	set -- $( echo msp430-gcc-*.patch | sed -r 's!msp430-gcc-(.[^-]+)-(.[^.]+).patch!\1 \2!' )
	gcc_upstream_ver="${1}"
	gcc_patch_ver="${2}"
	set --
else
	echo "Can not extract gcc version information"
	exit 1
fi
if [ -f msp430-gdb-*.patch ]
then
	set -- $( echo msp430-gdb-*.patch | sed -r 's!msp430-gdb-(.[^-]+)-(.[^.]+).patch!\1 \2!' )
	gdb_upstream_ver="${1}"
	gdb_patch_ver="${2}"
	set --
else
	echo "Can not extract gdb version information"
	exit 1
fi
if [ -f msp430-libc.version ]
then
	libc_ver="$(< msp430-libc.version )"
else
	echo "Can not extract libc version information"
	exit 1
fi
if [ -f msp430mcu.version ]
then
	mcu_ver="$(< msp430mcu.version )"
else
	echo "Can not extract mcu version information"
fi

cd ../

printf 'MSPGCC version:         %s\n' "${mspgcc_ver}"
printf 'binutils version:       %s\n' "${binutils_upstream_ver}"
printf 'binutils patch:         %s\n' "${binutils_patch_ver}"
printf 'binutils LTS patches:   %s\n' "${binutils_lts_patches:-None}"
printf 'GCC version:            %s\n' "${gcc_upstream_ver}"
printf 'GCC patch:              %s\n' "${gcc_patch_ver}"
printf 'GCC LTS patches:        %s\n' "${gcc_lts_patches:-None}"
printf 'GDB version:            %s\n' "${gdb_upstream_ver}"
printf 'GDB patch:              %s\n' "${gdb_patch_ver}"
printf 'GDB LTS patches:        %s\n' "${gdb_lts_patches:-None}"
printf 'libc version:           %s\n' "${libc_ver}"
printf 'libc LTS patches:       %s\n' "${libc_lts_patches:-None}"
printf 'MCU version:            %s\n' "${mcu_ver}"
printf 'MCU LTS patches:        %s\n' "${mcu_lts_patches:-None}"

MSP430MCU_ROOT="${MSP430_ROOT}/msp430mcu-${mcu_ver}"
PREFIX="${PREFIX:-${HOME}/opt/mspgcc_energia}"

PATCH="${G}patch"
MAKE="${G}make -j ${ncpus}"
TAR="${G}tar"
PATH=${PREFIX}/bin:${PATH}
export PREFIX MSP430_ROOT MSP430MCU_ROOT MAKE

echo '!!! fetch'
dl "http://ftp.gnu.org/pub/gnu/binutils/binutils-${binutils_upstream_ver}.tar.bz2"
dl "http://ftp.gnu.org/pub/gnu/gcc/gcc-${gcc_upstream_ver}/gcc-${gcc_upstream_ver}.tar.bz2"
dl "http://ftp.gnu.org/pub/gnu/gdb/gdb-${gdb_upstream_ver}.tar.bz2"
dl "${base_url}/msp430mcu/msp430mcu-${mcu_ver}.tar.bz2"
dl "${base_url}/msp430-libc/msp430-libc-${libc_ver}.tar.bz2"

if [ "${BUNDLE_SUPPORT_LIBS}" = "yes" ]
then
	dl "ftp://gmplib.org/pub/gmp-${gmp_version}/gmp-${gmp_version}.tar.bz2"
	dl "http://www.mpfr.org/mpfr-${mpfr_version}/mpfr-${mpfr_version}.tar.bz2"
	dl "http://www.multiprecision.org/mpc/download/mpc-${mpc_version}.tar.gz"
fi

echo '!!! untar+patch binutils'
xtr "binutils-${binutils_upstream_ver}.tar.bz2" "binutils"
(
	cd "binutils"
	${PATCH} -p1 < "../mspgcc-${mspgcc_ver}/msp430-binutils-${binutils_upstream_ver}-${binutils_patch_ver}.patch"
)

echo '!!! untar+patch gcc'
xtr "gcc-${gcc_upstream_ver}.tar.bz2" "gcc"
(
	cd "gcc"
	${PATCH} -p1 < "../mspgcc-${mspgcc_ver}/msp430-gcc-${gcc_upstream_ver}-${gcc_patch_ver}.patch"
)

if [ "${BUNDLE_SUPPORT_LIBS}" = "yes" ]
then
	echo '!!! untar+patch gcc support libs - gmp'
	xtr "../gmp-${gmp_version}.tar.bz2" "gcc/gmp"
	echo '!!! untar+patch gcc support libs - mpfr'
	xtr "../mpfr-${mpfr_version}.tar.bz2" "gcc/mpfr"
	echo '!!! untar+patch gcc support libs - mpc'
	xtr "../mpc-${mpc_version}.tar.gz" "gcc/mpc"
fi

echo '!!! untar+patch gdb'
xtr "gdb-${gdb_upstream_ver}.tar.bz2" "gdb"
(
	cd "gdb"
	${PATCH} -p1 < "../mspgcc-${mspgcc_ver}/msp430-gdb-${gdb_upstream_ver}-${gdb_patch_ver}.patch"
)

echo '!!! untar msp430mcu'
[ -d "msp430mcu-${mcu_ver}" ] && rm -rf ./"msp430mcu-${mcu_ver}"
tar xjf "msp430mcu-${mcu_ver}.tar.bz2"

echo '!!! untar msp430-libc'
[ -d "msp430-libc-${libc_ver}" ] && rm -rf ./"msp430-libc-${libc_ver}"
tar xjf "msp430-libc-${libc_ver}.tar.bz2"

echo '!!! lts patches'
if [ "${binutils_lts_patches}" ]
then
	for p in ${binutils_lts_patches}
	do
		rm -f "msp430-binutils-${binutils_upstream_ver}-${binutils_patch_ver}-${p}.patch"
		dl "${lts_patches_url}/msp430-binutils-${binutils_upstream_ver}-${binutils_patch_ver}-${p}.patch/download"
		(
			cd "binutils"
			${PATCH} -p1 < "../msp430-binutils-${binutils_upstream_ver}-${binutils_patch_ver}-${p}.patch"
		)
	done
fi

if [ "${gcc_lts_patches}" ]
then
	for p in ${gcc_lts_patches}
	do
		dl "${lts_patches_url}/msp430-gcc-${gcc_upstream_ver}-${gcc_patch_ver}-${p}.patch/download"
		(
			cd "gcc"
			${PATCH} -p1 < "../msp430-gcc-${gcc_upstream_ver}-${gcc_patch_ver}-${p}.patch"
		)
	done
fi

if [ "${gdb_lts_patches}" ]
then
	for p in ${gdb_lts_patches}
	do
		dl "${lts_patches_url}/msp430-gdb-${gdb_upstream_ver}-${gdb_patch_ver}-${p}.patch/download"
		(
			cd "gdb"
			${PATCH} -p1 < "../msp430-gdb-${gdb_upstream_ver}-${gdb_patch_ver}-${p}.patch"
		)
	done
fi

if [ "${libc_lts_patches}" ]
then
	for p in ${libc_lts_patches}
	do
		dl "${lts_patches_url}/msp430-libc-${libc_ver}-${p}.patch/download"
		(
			cd "msp430-libc-${libc_ver}"
			${PATCH} -p1 < "../msp430-libc-${libc_ver}-${p}.patch"
		)
	done
fi

if [ "${mcu_lts_patches}" ]
then
	for p in ${mcu_lts_patches}
	do
		dl "${lts_patches_url}/msp430mcu-${mcu_ver}-${p}.patch/download"
		(
			cd "msp430mcu-${mcu_ver}"
			${PATCH} -p1 < "../msp430mcu-${mcu_ver}-${p}.patch"
		)
	done
fi

echo '!!! binutils configure'
mkdir -p BUILD/binutils
cd BUILD/binutils
"../../binutils/configure" \
	--disable-nls \
	--target=msp430 \
	--prefix="${PREFIX}"
echo '!!! binutils make'
${MAKE}
echo '!!! binutils check'
${MAKE} check-gas RUNTESTFLAGS=msp430.exp
echo '!!! binutils install'
${MAKE} install
cd ../../

mkdir -p BUILD/gcc
cd BUILD/gcc
echo '!!! gcc configure'
"../../gcc/configure" ${GCC_CONFG_ARGS} \
	--enable-languages="c,c++" \
	--disable-nls \
	--target=msp430 \
	--prefix="${PREFIX}" \
	--with-pkgversion="MSPGCC ${mspgcc_ver}${gcc_lts_patches:+ (With patches: ${gcc_lts_patches})}"
echo '!!! gcc make'
${MAKE}
echo '!!! gcc check'
${MAKE} check-gcc RUNTESTFLAGS=msp430.exp
echo '!!! gcc install'
${MAKE} install
cd ../../

cd "msp430-libc-${libc_ver}/src"
[ -d Build ] && rm -rf Build
echo '!!! libc make'
${MAKE} PREFIX="${PREFIX}"
echo '!!! libc install'
${MAKE} PREFIX="${PREFIX}" install
cd ../../

cd "msp430mcu-${mcu_ver}"
echo '!!! mcu install'
sh scripts/install.sh "${PREFIX}"
cd ../

mkdir -p BUILD/gdb
cd BUILD/gdb
echo '!!! gdb configure'
"../../gdb/configure" \
	--disable-nls \
	--target=msp430 \
	--prefix="${PREFIX}"
echo '!!! gdb make'
${MAKE}
echo '!!! gdb install'
${MAKE} install
cd ../../
