#!/bin/sh


BUILD_GCC=true
BUILD_BINUTILS=true
BUILD_GDB=true
START_SHELL=false

PROJECT_ROOT="$(pwd)"
CROSS_ROOT="$PROJECT_ROOT/cross"
PREFIX="$CROSS_ROOT"
CROSS_BUILD_DIR="$PROJECT_ROOT/.cross_build"

TARGET=''
CPUS=1

# Get the target arch
if [ -z "$ARCH" ]; then
	ARCH="$1"
	shift
	if [ -z "$ARCH" ]; then
		echo "Must define an arch"
		exit 1
	fi
fi

case "$ARCH" in
	'x86_64-elf'|'x86_64'|'x64'|'amd64')
		TARGET='x86_64-elf'
		CONFIG_EXTRA_GCC="$CONFIG_EXTRA_GCC --enable-multilib"
		;;
	'i686-elf'|'i386'|'x32'|'386')
		TARGET='i686-elf'
		CONFIG_EXTRA_GCC="$CONFIG_EXTRA_GCC --enable-multilib"
		;;
	'riscv-64-elf'|'rv64'|'riscv64')
		TARGET='riscv-64-elf'
		;;
	*)
		echo "arch=$ARCH not implemented"
		exit 1
		;;
esac

# Get the build target
while [ "${1:-}" != "" ]; do
	case "$1" in
		"--no-gcc")
			BUILD_GCC=false
			;;
		"--no-binutils")
			BUILD_BINUTILS=false
			;;
		"--no-gdb")
			BUILD_GDB=false
			;;
		"--start-shell")
			START_SHELL=true
			BUILD_GCC=false
			BUILD_BINUTILS=false
			BUILD_GDB=false
			;;
	esac
	shift
done

# TODO: add some more options to speed up the build / disable unnecessary features
# https://gitlab.archlinux.org/archlinux/packaging/packages/riscv64-linux-gnu-binutils/-/blob/main/PKGBUILD
CONFIG_EXTRA_BINUTILS="\
	--without-zstd \
	--with-sysroot \
	--enable-languages=c \
	--disable-nls \
	--disable-werror"

# TODO: add some more options to speed up the build / disable unnecessary features
# https://gitlab.archlinux.org/archlinux/packaging/packages/riscv64-linux-gnu-gcc/-/blob/main/PKGBUILD
CONFIG_EXTRA_GCC="\
	--without-zstd \
	--disable-nls \
	--enable-languages=c \
	--without-headers"

CONFIG_EXTRA_GDB="\
	--with-sysroot \
	--disable-nls \
	--disable-werror \
	--without-headers \
	--enable-languages=c \
	--enable-tui=yes \
	--with-expat"

#CONFIG_HOST="$(gcc -dumpmachine)"

FTP_MIRROR="https://ftpmirror.gnu.org"

gdb_version='13.1'
gcc_version='12.2.0'
binutils_version='2.37'

gcc_url="$FTP_MIRROR/gcc/gcc-$gcc_version"
binutils_url="$FTP_MIRROR/binutils"
gdb_url="$FTP_MIRROR/gdb"

gdb_build_log=gdb-log-"$(date +%F_%H-%M)".log
gcc_build_log=gcc-log-"$(date +%F_%H-%M)".log
binutils_build_log=binutils-log-"$(date +%F_%H-%M)".log

# check if the user wants a particular version
if [ -n "$GDB_VERSION" ]; then
	gdb_version="$GDB_VERSION"
fi

# sanity check
if [ -z "$TARGET" ]; then
	echo "Target not specified, how'd you get this far?"
	exit 1
fi

# get the right number of cpus
# and some extra fixes
case $(uname) in
	'OpenBSD')
		CPUS="$(sysctl -n hw.ncpu)"
		CPUS=$((CPUS + 1))
		CONFIG_EXTRA_GCC="$CONFIG_EXTRA_GCC --with-gmp=/usr/local"
		# use GNU make
		alias make=gmake
		;;
	'Linux')
		CPUS="$(nproc)"
		CPUS=$((CPUS + 1))
		;;
	*)
		;;
esac


gpg_verify() {
	keyring="$1"
	pkg="$2"
	sig="$3"
	gpg --verify --keyring "$keyring" "$sig" "$pkg"
	return $?
}


download_sources() {
	pkg="$1"
	sig="$2"
	url="$3"
	wget -nv -O "$pkg" "$url/$pkg" || return $?
	wget -nv -O "$sig" "$url/$sig" || return $?
}


download_and_build() {

	pkg_name="$1"
	pkg_version="$2"
	pkg_config="$3"
	pkg_url="$4"
	pkg_ark="$pkg_name-$pkg_version.tar.xz"
	pkg_sig="$pkg_name-$pkg_version.tar.xz.sig"

	# Get the archives and signature if they are not already present
	if ! [ -e "$pkg_ark" ] || ! [ -e "$pkg_sig" ]; then
		download_sources "$pkg_ark" "$pkg_sig" "$pkg_url" || return 1
	fi

	# Verify the archive
	if ! gpg_verify ./gnu-keyring.gpg "$pkg_ark" "$pkg_sig"; then
		echo "Package signature was incorrect, please remove the donwloaded sources and try again"
		return 1
	fi

	# Extract the archive
	xz -k -d "$pkg_ark"
	tar -xf "$pkg_name-$pkg_version.tar" && rm -f "$pkg_name-$pkg_version.tar"

	# FIXME: this forces a comlete rebuild
	#if [ -d "build-$pkg_name-$TARGET" ]; then
	#	rm -rf "build-$pkg_name-$TARGET"
	#fi
	mkdir -p "build-$pkg_name-$TARGET"
	cd "build-$pkg_name-$TARGET" || exit 1

	oldpath="$PATH"
	export PATH="$PREFIX/bin:$PATH"
# shellcheck disable=SC2086
	../"$pkg_name-$pkg_version"/configure \
		--target="$TARGET"            \
		$pkg_config                   \
		--prefix="$PREFIX"            || return 1

	case "$pkg_name" in
		'gcc')
			make all-gcc || return 1
			make all-target-libgcc || return 1
			make install-gcc || return 1
			make install-target-libgcc || return 1
			;;
		'binutils')
			make -j$CPUS || return 1
			make install || return 1
			;;
		'gdb')
			make all-gdb || return 1
			make install-gdb || return 1
			;;
	esac

	export PATH="$oldpath"
	cd ..
	return 0
}


#set -x

mkdir -p "$PREFIX"
mkdir -p "$CROSS_BUILD_DIR/$TARGET"

cd "$CROSS_BUILD_DIR/$TARGET" || exit 1

# get the gnu keyring, used to verify the archives
if [ $BUILD_GCC = true ] || [ $BUILD_BINUTILS = true ] || [ $BUILD_GDB = true ]; then
	if ! [ -e gnu-keyring.gpg ]; then
		wget -O gnu-keyring.gpg https://ftp.gnu.org/gnu/gnu-keyring.gpg
	fi
fi

if [ $BUILD_BINUTILS = true ]; then
	echo "### BUILDING BINUTILS ###"

	download_and_build binutils "$binutils_version" "$CONFIG_EXTRA_BINUTILS" \
	"$binutils_url" > ../"$binutils_build_log" 2>&1
	res=$?

	if ! [ $res -eq 0 ]; then
		echo "Error building binutils, check $CROSS_BUILD_DIR/$binutils_build_log for more details"
		exit 1
	fi
fi


if [ $BUILD_GCC = true ]; then
	echo "### BUILDING GCC ###"

	download_and_build gcc "$gcc_version" "$CONFIG_EXTRA_GCC" \
	"$gcc_url" > ../"$gcc_build_log" 2>&1
	res=$?

	if ! [ $res -eq 0 ]; then
		echo "Error building gcc, check $CROSS_BUILD_DIR/$gcc_build_log for more details"
		exit 1
	fi
fi


if [ $BUILD_GDB = true ]; then
	echo "### BUILDING GDB ###"

	download_and_build gdb "$gdb_version" "$CONFIG_EXTRA_GDB" \
	"$gdb_url" > ../"$gdb_build_log" 2>&1
	res=$?

	if ! [ $res -eq 0 ]; then
		echo "Error building gdb, check $CROSS_BUILD_DIR/$gdb_build_log for more details"
		exit 1
	fi
fi

cd "$PROJECT_ROOT" || exit 1

if [ $START_SHELL = true ]; then
	PATH="$CROSS_ROOT/bin:$PATH"
	export PATH TARGET ARCH
	echo "### Starting $SHELL with correct PATH and TARGET ###"
	exec "$SHELL" || exit 1
fi

# TODO: validate that all things are where they should
