#!/bin/bash
# sb2-init - Copyright (C) 2007 Lauri Leukkunen <lle@rahina.org>
# Licensed under GPL version 2

my_path=$_
if [ $(basename $my_path) != $(basename $0) ]; then
	my_path=$0
	if [ $(basename $my_path) = $my_path ]; then
		my_path=$(which $my_path)
	fi
fi

function show_existing_config_info()
{
	has_targets="no"
	echo "Already initialized targets:"
	echo
	for f in $HOME/.scratchbox2/*/sb2.config
	do
		if [ -f $f ]
		then
			SBOX_CONFIG_VERSION=0
			. $f
			has_targets="yes"
			targetdir=`dirname $f`
			targetname=`basename $targetdir`
			echo "Target $targetname:"
			if [ "$SBOX_CONFIG_VERSION" -lt 5 ]
			then
				echo "(configuration file version too old)"
			else
				echo "configured at $SBOX_INIT_TIME by $SBOX_INIT_ID, with command"
				echo "( cd $SBOX_TARGET_ROOT;"
				echo "sb2-init $SBOX_INIT_ORIG_ARGS )"
			fi
			echo
		fi
	done
	if [ has_targets = "no" ]
	then
		echo "none."
	fi
}

function usage()
{
	cat <<EOF
sb2-init - initialize a target root for scratchbox2
Usage:
	sb2-init [OPTION]... [TARGETNAME] [COMPILER]

sb2-init is expected to be run in the directory you want
to use as scratchbox 2 target root.

TARGETNAME is what you want to call this target
COMPILER is of the form $HOME/arm-2006q3/bin/arm-linux-gcc


Options:
    -c "command"      specify cpu transparency command, for example:
                      "qemu-arm", "sbrsh" or "qemu-arm -m 512"
    -r [hostname]     generate sbrsh config using remote device address
    -l [hostname]     NFS server/localhost address seen by remote device
    -d                set target as default scratchbox2 target
    -m [mapping_mode] use mapping_mode as default
    -h                print this help
    -n                don't build libtool for the target
    -N                don't generate localization files for the target
    -s                skip checks for target root's /usr/include etc.
    -t [tools_dir]    set directory containing the build tools distribution
    -C "options"      add extra options for the compiler, for example:
                      "-fgnu89-inline"
    -A arch           manually override target architecture
    -v                display version

Examples:
    sb2-init -c qemu-arm ARM arm-linux-gcc
    sb2-init -c qemu-arm -m devel ARM arm-linux-gcc
    sb2-init -sn -c sbrsh armel-debian /path/to/arm-linux-gcc

EOF
	show_existing_config_info
	exit 2
}

function version()
{
	cat $SBOX_DIR/share/scratchbox2/version
	exit 0
}


function write_target_config()
{
	cat - > $HOME/.scratchbox2/$TARGET/sb2.config <<EOF
# Scratchbox2 configuration file generated by sb2-init.

SBOX_INIT_ORIG_ARGS="$SBOX_INIT_ORIG_ARGS"
SBOX_INIT_TIME=$SBOX_INIT_TIME
SBOX_INIT_ID="$SBOX_INIT_ID"

SBOX_CONFIG_VERSION=8

SBOX_TARGET_ROOT=$SBOX_TARGET_ROOT

SBOX_CPU=$ARCH
SBOX_GCC_TARGET=$GCC_TARGET
SBOX_CPUTRANSPARENCY_METHOD="$CPUTRANSP"
SBOX_UNAME_MACHINE=$ARCH

SBOX_DEFAULT_GCC_PREFIX=$GCC_PREFIX

SBOX_CROSS_GCC_NAME=cross-gcc
SBOX_CROSS_GCC_PREFIX_LIST=$GCC_TARGET-:$SB2INIT_CROSS_GCC_PREFIX_LIST:$GCC_PREFIX
SBOX_CROSS_GCC_SUBST_PREFIX=$GCC_PREFIX
SBOX_CROSS_GCC_SPECS_FILE="$SBOX_CROSS_GCC_SPECS_FILE"
SBOX_CROSS_GCC_DIR=$GCC_PATH
SBOX_CROSS_GCC_LD_ARGS=
SBOX_EXTRA_CROSS_COMPILER_ARGS="$SBOX_EXTRA_CROSS_COMPILER_ARGS"
SBOX_EXTRA_CROSS_COMPILER_STDINC="$SBOX_EXTRA_CROSS_COMPILER_STDINC"
SBOX_EXTRA_CROSS_LD_ARGS="-rpath-link $SBOX_TARGET_ROOT/usr/lib:$SBOX_TARGET_ROOT/lib"

DEB_BUILD_ARCH=$SB2INIT_DEB_BUILD_ARCH
DEB_BUILD_ARCH_CPU=$SB2INIT_DEB_BUILD_ARCH_CPU
DEB_BUILD_ARCH_ABI=$SB2INIT_DEB_BUILD_ARCH_ABI
DEB_BUILD_GNU_CPU=$ARCH
DEB_BUILD_GNU_TYPE=$SB2INIT_DEB_BUILD_GNU_TYPE
DEB_BUILD_GNU_SYSTEM=$SB2INIT_DEB_BUILD_GNU_SYSTEM

DEB_HOST_ARCH=$DEBIAN_CPU
DEB_HOST_ARCH_OS=$SB2INIT_DEB_HOST_ARCH_OS
DEB_HOST_ARCH_CPU=$SB2INIT_DEB_HOST_ARCH_CPU

DEB_HOST_GNU_CPU=$ARCH
DEB_HOST_GNU_TYPE=$SB2INIT_DEB_HOST_GNU_TYPE
DEB_HOST_GNU_SYSTEM=$SB2INIT_DEB_HOST_GNU_SYSTEM

SBOX_HOST_GCC_NAME=host-gcc
SBOX_HOST_GCC_PREFIX_LIST=host-
SBOX_HOST_GCC_SUBST_PREFIX=
SBOX_HOST_GCC_SPECS_FILE=
SBOX_HOST_GCC_DIR=/usr/bin
SBOX_HOST_GCC_LD_ARGS=
SBOX_EXTRA_HOST_COMPILER_ARGS="$HOST_GCC_INC"

SBRSH_CONFIG=$HOME/.scratchbox2/$TARGET/sbrsh.config

if [ -z "\$SBOX_MAPMODE" ]; then
	SBOX_MAPMODE=$MAPPING_MODE
fi

SBOX_TOOLS_ROOT=$TOOLS_ROOT

if [ -n "\$SBOX_TOOLS_ROOT" ]; then
	LD_LIBRARY_PATH=/usr/local/lib:/usr/lib/:/usr/lib64:/lib:/lib64:\$SBOX_TOOLS_ROOT/usr/lib/libfakeroot:\$SBOX_TOOLS_ROOT/usr/lib64/libfakeroot:\$SBOX_TOOLS_ROOT/usr/lib32/libfakeroot:\$LD_LIBRARY_PATH
else
	# SBOX_TOOLS_ROOT not set, include the fakeroot lib path in any case.
	LD_LIBRARY_PATH=\$LD_LIBRARY_PATH:/usr/lib/libfakeroot:/usr/lib64/libfakeroot:/usr/lib32/libfakeroot
fi
EOF
	echo "Finished writing sb2.config"
}

function write_sbrsh_config()
{
	cat - > $HOME/.scratchbox2/$TARGET/sbrsh.config <<EOF
# sbrsh configuration file generated by sb2-init.

$TARGET  $REMOTEHOST
  nfs   $LOCALHOST:$SBOX_TARGET_ROOT  /  rw,nolock,noac,tcp
  nfs   $LOCALHOST:$HOME  $HOME  rw,nolock,noac,tcp
  bind  /tmp      /tmp
  bind  /proc     /proc
  bind  /sys      /sys
  bind  /dev      /dev
  bind  /dev/pts  /dev/pts
EOF
	echo "Finished writing sbrsh.config"
}

function check_buildroot_sanity()
{
	a_ok=1

	if [ ! -e usr/include/stdio.h ]; then
		echo "no usr/include/stdio.h"
		a_ok=0
	fi

	if [ ! -e lib/libc.so.6 ] && [ ! -e lib/libc.so.0 ]; then
		echo "no lib/libc.so.6 or lib/libc.so.0"
		a_ok=0
	fi

	if [ ! -e usr/lib/libc.so ]; then
		echo "no usr/lib/libc.so"
		a_ok=0
	fi
	
	if [ $a_ok == 1 ]; then
		true
	else
		echo "Your buildroot seems to lack basic essentials like headers 
or c-library. You should probably get either a ready rootfs tarball or
copy the necessary files from your toolchain into place. After doing that
you can re-run this script."
		exit 1
	fi
}

if [ -z "$SBOX_DIR" ]
then
	SBOX_DIR=$(readlink -f $(dirname $(readlink -f $my_path))/..)
fi

# SBOX_INIT_* variables are used to record who initialized, and when and 
# how sb2 was initialized.
# It is possible to set SBOX_INIT_ID externally (for example, if
# another program is used to initialize the system)
SBOX_INIT_ORIG_ARGS="$*"
SBOX_INIT_TIME=`date +%Y-%m-%d_%H:%M:%S`
if [ -z "$SBOX_INIT_ID" ]
then
	SBOX_INIT_ID="user '$USER'"
fi

REMOTEHOST=
LOCALHOST=

set_as_default=0
with_libtool=1
with_locales=1
skip_checks=false

SBOX_EXTRA_CROSS_COMPILER_ARGS=""

if [ -z "$*" ]; then
	usage
fi

while getopts A:c:C:r:l:m:dhnNst:v foo
do
	case $foo in
	(A) ARCH=$OPTARG ;;
	(c) CPUTRANSP=$OPTARG ;;
	(r) REMOTEHOST=$OPTARG ;;
	(l) LOCALHOST=$OPTARG ;;
	(d) set_as_default=1 ;;
	(h) usage ;;
	(m) MAPPING_MODE=$OPTARG ;;
	(n) with_libtool=0 ;;
	(N) with_locales=0 ;;
	(s) skip_checks=true ;;
	(t) TOOLS_ROOT=$OPTARG ;;
	(v) version ;;
	(C) SBOX_EXTRA_CROSS_COMPILER_ARGS="$SBOX_EXTRA_CROSS_COMPILER_ARGS $OPTARG " ;;
	(*) usage ;;
	esac
done
shift $(($OPTIND - 1))

if [ "$LOCALHOST" ] && [ -z "$REMOTEHOST" ]; then
	echo "Warning: Local host specified without remote host."
fi

TARGET=$1
SBOX_TARGET_ROOT=$PWD
GCC=$2

if [ -z "$TARGET" ]; then
	echo "Error: no target given"
	exit 1
fi

if [ -z "$GCC" ]; then
	echo "Warning: no compiler given"
else
	GCC_FULLPATH=$(which $GCC)
	# test that gcc exists and can be executed
	if [ $? != 0 ]; then
		echo "$GCC doesn't exist"
		exit 1
	fi
	GCC_PATH=$(dirname $(which $GCC))
	if [ $GCC -v > /dev/null 2>&1 != 0 ]; then
		echo "Invalid compiler specified: $GCC"
		exit 1
	fi

	GCC_PREFIX=$(basename $GCC | sed 's/-gcc$/-/')

	if [ -z "$ARCH" ]
	then
		echo "Using $GCC to detect target architecture:"
		ARCH=$($GCC -dumpmachine | awk -F- '{ print $1 }')
		GCC_TARGET=$($GCC -dumpmachine)
	else
		echo "Target architecture set to $ARCH"
	fi
fi

DEBIAN_CPU=$ARCH

HOST_ARCH="$(uname --machine)"
if echo "$HOST_ARCH" | grep -q "^i.86*"; then
	HOST_ARCH="i[3456]86"
fi

case "$ARCH" in
	$HOST_ARCH*) ;;
	arm*) 
		if [ -z "$GCC_TARGET" ]
		then
			DEBIAN_CPU=$ARCH
		else
			echo $GCC_TARGET | grep -q -i eabi
			if [ $? == 0 ]; then
				DEBIAN_CPU=armel
			fi
		fi
		;;
	ppc*) ;;
	mips*) ;;
	sh*) ;;
	i386*) ;;
	i486*) ;;
	i586*) ;;
	i686*) ;;
	*)
		echo "Unsupported target architecture: $ARCH"
		echo "You must add support for it into preload/sb_exec.c"
		echo "and utils/sb2-init"
		exit 1
		;;

esac

# defaults for SB2_INIT_DEB_BUILD_GNU_TYPE, SB2_INIT_DEB_HOST_GNU_TYPE, etc,
# and the cross-gcc prefix list:
# these may be changed by sb2rc.$MAPPING_MODE
SB2INIT_DEB_BUILD_ARCH=$DEBIAN_CPU
SB2INIT_DEB_BUILD_ARCH_ABI="gnu"
SB2INIT_DEB_BUILD_GNU_TYPE=$ARCH-linux-gnu
SB2INIT_DEB_BUILD_GNU_SYSTEM=""
SB2INIT_DEB_BUILD_ARCH_CPU=$DEBIAN_CPU

SB2INIT_DEB_HOST_GNU_TYPE=$ARCH-linux-gnu
SB2INIT_DEB_HOST_GNU_SYSTEM=""
SB2INIT_DEB_HOST_ARCH_OS="linux"
SB2INIT_DEB_HOST_ARCH_CPU=$DEBIAN_CPU

SB2INIT_CROSS_GCC_PREFIX_LIST=$ARCH-linux-

# $ARCH has been set, get mode-specific settings..
if [ -f $SBOX_DIR/share/scratchbox2/modeconf/sb2rc.$MAPPING_MODE ]
then
	echo "Reading mode-specific settings.."
	. $SBOX_DIR/share/scratchbox2/modeconf/sb2rc.$MAPPING_MODE "initializing"
fi

if [ -z "$MAPPING_MODE" ]; then
	MAPPING_MODE="simple"
fi

if [ ! -d $SBOX_DIR/share/scratchbox2/lua_scripts/pathmaps/$MAPPING_MODE ]; then
	echo "Invalid mapping mode: $MAPPING_MODE"
	exit 1
fi


if [ ! $skip_checks ]; then
	check_buildroot_sanity
fi


mkdir -p $HOME/.scratchbox2

if [ -z "$(sb2-config -l)" ]; then
# force this as default anyway as there are no
# other existing targets
	set_as_default=1
fi

if [ -n "$TOOLS_ROOT" ]; then
	if [ -e "$TOOLS_ROOT/etc/scratchbox-version" ]; then
		# this is a scratchbox 1.x directory, tread carefully
		# sb1 is not FHS, *sigh*
		TOOLS_ROOT=$TOOLS_ROOT/tools
	fi
	# else assume standard FHS system
fi

# Use "specs" file for gcc if it exists, otherwise add -I/usr/include to params
SBOX_CROSS_GCC_SPECS_FILE=""
SBOX_EXTRA_CROSS_COMPILER_STDINC=""
if [ -f $SBOX_DIR/share/scratchbox2/modeconf/gcc-specs.$MAPPING_MODE ]; then
	SBOX_CROSS_GCC_SPECS_FILE="$SBOX_DIR/share/scratchbox2/modeconf/gcc-specs.$MAPPING_MODE"
else
	SBOX_EXTRA_CROSS_COMPILER_STDINC="-I/usr/include"
fi

SBOX_EXTRA_CROSS_COMPILER_ARGS="$SBOX_EXTRA_CROSS_COMPILER_ARGS -L$SBOX_TARGET_ROOT/usr/lib -L$SBOX_TARGET_ROOT/lib"

# test if the cross compiler needs to be silenced about /usr/include
# usage
echo "" | $GCC_FULLPATH -E - -Wno-poison-system-directories > /dev/null 2>&1
if [ $? = 0 ] ; then
	SBOX_EXTRA_CROSS_COMPILER_ARGS="$SBOX_EXTRA_CROSS_COMPILER_ARGS -Wno-poison-system-directories"
fi

HOST_GCC_INC=$(echo "#include <stdio.h>" | gcc -M -E - | SBOX_DIR=$SBOX_DIR perl -e 'while(<STDIN>) { $foo{$1} = 1 if m/\/usr([^[:space:]]*\/include)/;}; foreach my $k (keys %foo) {print " -isystem $ENV{SBOX_DIR}/share/scratchbox2/host_usr$k"};')

mkdir -p $HOME/.scratchbox2/$TARGET/bin
write_target_config

if [ -n "$REMOTEHOST" ]; then
	if [ -z "$LOCALHOST" ]; then
		LOCALHOST=$(ip -f inet -o addr \
			| awk -- '/: eth[0-9]/ { print $4; exit }' \
			| cut -d/ -f1)
		echo "Using NFS server address: $LOCALHOST"
	fi
	write_sbrsh_config
fi

if [ $set_as_default == 1 ]; then
	sb2-config -d $TARGET
fi

#
# If target architecture is same as host (currently i386)
# we need to extract localization archive from target and
# place resulting files under ~/.scratchbox2/locales.
#
if [ $with_locales == 1 ]; then
	if [ -z "$CPUTRANSP" ]; then
		$SBOX_DIR/bin/sb2 -t $TARGET \
		    $SBOX_DIR/share/scratchbox2/scripts/sb2-generate-locales
	fi
fi

if [ $with_libtool == 1 ]; then
	$SBOX_DIR/bin/sb2 -t $TARGET $SBOX_DIR/bin/sb2-build-libtool
	TEST=$?
	printf "\n\n"
	if [ $TEST == 0 ]; then
		echo "sb2-init completed successfully, have fun!"
	else
		echo "Running $SBOX_DIR/bin/sb2-build-libtool failed"
		echo "You can run this manually later, otherwise your"
		echo "sb2 environment is correctly setup and ready to use"
	fi
fi
