#!/bin/bash

# eBuild-Linux script
#
# Boostraps the building of a cross-compiler for an embedded Linux
# system using a set of Linux kernel sources, glibc sources and
# compiler sources.

# Exit on any error.
set -e

# Default overridable variables

RELEASE=erelease-yymmdd		# Name of release, usually /opt/redhat/$RELEASE
REVISION=x			# release revision
BUILD=i686-pc-linux-gnulibc2.3	# Platform tools will run on
HOST=$BUILD			# Platform we're building on
TARGET=unknown-linux-gnu	# Platform tools will target
BUILDTOP="`pwd`"
SRCTOP="$BUILDTOP/sources"
MAKE="make"			# In case we need a special version
HOSTEXPLICIT=""
SYSROOT=""			# sys-root used by native compiler
KARCH=""			# Kernel architecture type (eg arm, mips, x86)
				# The dir kernel_src/include/asm-$KARCH should exist
KPROC=""			# Kernel processor architecture (eg armv, mips32, pentiumv)
				# The dir kernel_src/include/asm-$KARCH/proc-$KPROC should exist 
KTYPE=""			# Kernel processor type (eg XScale, Athlon)
				# The dir kernel_src/include/asm-$KARCH/arch-$KTYPE should exist
LANGUAGES="c,c++"		# Languages to enable
TOOLS_CONFIG_OPTIONS=""		# Extra options to pass to configure when creating toolchain
GLIBC_CONFIG_OPTIONS=""		# Extra options to pass to configure when creating glibc
GLIBC_MULTILIBS=none		# sets CPPFLAGS prior to building glibc
CC_for_host=gcc			# Compiler for the HOST platform.
xCC_prefix=
DELETE=""			# Delete stages if they already exist

while [ $# -ne 0 ]; do
    option=$1
    shift

    optarg=""
    case $option in
    --*=*)
        optarg=`echo $option | sed -e 's/^--[a-zA-Z0-9_-]*\=//'`
        ;;
    esac

    case $option in
    --x|-x|--debug|-D)
        set -x
        ;;
    --delete)
        DELETE=1
        ;;
    --lang*)
        LANGUAGES=$optarg
        ;;
    --make*)
        MAKE=$optarg
	export MAKE
        ;;
    --sysroot*)
        SYSROOT=$optarg
	;;
    --prefix*)
        PREFIX=$optarg
        ;;
    --buildtop*)
        BUILDTOP="$optarg"
        ;;
    --host*)
        HOST=$optarg
	HOSTEXPLICIT=1
        ;;
    --target*)
        TARGET=$optarg
        ;;
    --build*)
        BUILD=$optarg
        ;;
    --karch*)
        KARCH=$optarg
        ;;
    --kproc*)
        KPROC=$optarg
        ;;
    --ktype*)
        KTYPE=$optarg
        ;;
    --rev*)
        REVISION=$optarg
        ;;
    --rel*)
        RELEASE=$optarg
        ;;
    --srctop*)
	SRCTOP="$optarg"
        ;;
    --xCC_prefix*)
        xCC_prefix="$optarg"
	;;
    --CC*)
    	CC_for_host="$optarg"
	;;
    --tools-config-options*)
	TOOLS_CONFIG_OPTIONS=$optarg
	;;
    --glibc-config-options*)
	GLIBC_CONFIG_OPTIONS=$optarg
	;;
    --gml*)
	GLIBC_MULTILIBS="$optarg"
	;;
    esac
done

if [ X$SYSROOT = X ]; then
  if [ X$HOSTEXPLICIT = X1 ]; then
    testcc="$HOST-gcc"
  else
    testcc="gcc"
  fi
  sr="`$testcc -E -v - < /dev/null 2>&1 >/dev/null | sed -n 's,/usr/include$,/,p' | sed 's/^ *//'`"
  if [ sr != '/' ]; then
    SYSROOT="$sr`$testcc --print-multi-os-directory`"
  fi
fi

if [ X$PREFIX = X ]; then
  PREFIX="/opt/redhat/$RELEASE-$REVISION"
fi

mkdir -p "$BUILDTOP"
if [ X$DELETE = X1 ]; then
  rm -f "$BUILDTOP"/*STMP
fi
cd "$BUILDTOP"

case $xCC_prefix in
"" | *" ") ;;
*) xCC_prefix=$xCC_prefix\  ;;
esac
  
# Source trees:
if test -d $SRCTOP/headers/$TARGET/usr; then
  kernel_src=$SRCTOP/header/$TARGET/usr
else
  kernel_src=$SRCTOP/kernel/$TARGET
fi
glibc_src=$SRCTOP/glibc/$TARGET
tools_src=$SRCTOP/tools/cross

# sysroot variables

sysroot_stage1=$BUILDTOP/sysroot-stage1
sysroot_stage2=$BUILDTOP/sysroot-stage2
sysroot_stage3=$BUILDTOP/sysroot-stage3

# tools variables

tools_install_stage1=$BUILDTOP/tools-install-stage1
tools_prefix_stage1=$tools_install_stage1/$RELEASE-$REVISION
tools_exec_prefix_stage1=$tools_prefix_stage1/H-$HOST
tools_sys_root_prefix_stage1=$tools_prefix_stage1/H-$HOST/$TARGET
# --with-newlib is added in order to define inhibit_libc when building
# libgcc for the first time.  This prevents libgcc from trying to use
# headers and functions from glibc, which has not been built yet.
tools_stage1conf="$tools_src/configure -v --host=$HOST --target=$TARGET --build=$BUILD --enable-languages=c --prefix=$tools_prefix_stage1 --exec-prefix=$tools_exec_prefix_stage1 --with-sysroot --disable-shared --disable-threads --with-newlib $TOOLS_CONFIG_OPTIONS"
tools_stage1bld="all-binutils all-ld all-gas all-gcc"
tools_stage1inst="install-binutils install-ld install-gas install-gcc"

tools_prefix_stage2=$PREFIX
tools_exec_prefix_stage2=$tools_prefix_stage2/H-$HOST
tools_sys_root_prefix_stage2=$tools_prefix_stage2/H-$HOST/$TARGET
tools_stage2conf="$tools_src/configure -v --host=$HOST --target=$TARGET --build=$BUILD --enable-languages=$LANGUAGES --prefix=$tools_prefix_stage2 --exec_prefix=$tools_exec_prefix_stage2 --with-sysroot --enable-threads --x-libraries=$SYSROOT/usr/X11R6/lib/ --x-includes=$SYSROOT/usr/X11R6/include/ $TOOLS_CONFIG_OPTIONS"
tools_stage2bld="all info"
tools_stage2inst="install install-info"

# glibc variables

glibc_host=$TARGET
glibc_target=$TARGET
glibc_build=$BUILD

glibc_config_prefix=/usr

glibc_stage1conf="$glibc_src/configure -v --with-headers=$sysroot_stage2/usr/include --prefix=$glibc_config_prefix  --host=$glibc_host --build=$glibc_build --target=$glibc_target --enable-add-ons=linuxthreads --disable-profile --without-cvs $GLIBC_CONFIG_OPTIONS"

glibc_stage2conf="$glibc_src/configure -v --with-headers=$sysroot_stage3/usr/include --prefix=$glibc_config_prefix  --host=$glibc_host --build=$glibc_build --target=$glibc_target --enable-add-ons=linuxthreads --disable-profile --without-cvs $GLIBC_CONFIG_OPTIONS"

# other variables

mkinstalldirs=$tools_src/mkinstalldirs

dummy_header_files="\
    usr/include/sys/types.h \
    usr/include/errno.h\
    usr/include/string.h\
    usr/include/stdlib.h\
    usr/include/stdio.h\
    usr/include/unistd.h\
    usr/include/time.h"
dummy_header_dirs="usr/include usr/include/sys"

# We defintely don't want this picked up from the environment by accident.
#unset CFLAGS

OLDCPPFLAGS="$CPPFLAGS"

# Save a bit of text in the strings below.

AT=" at `/bin/date +%H:%M`."

# Do the actual work.

echo "Starting $0" $AT

# Stage 1
#     * copy kernel headers
#     * make tweaks for glibc
#     * build gcc/binutils/ld/gas with above headers

# stage1 environment variables

PATH="$tools_exec_prefix_stage1/bin:$PATH"
export PATH

if [ ! -f KERN-STAGE1-STMP ]; then
    # copy kernel headers to sysroot dir
    AT=" at `/bin/date +%H:%M`."
    echo "Copying/Generating kernel files for stage1" $AT
    rm -rf "$sysroot_stage1/usr/include"
    $mkinstalldirs $sysroot_stage1/usr/include > /dev/null 2>&1
    (cd $kernel_src; make oldconfig all || true )
    (cd $kernel_src/include ; tar cf - linux asm*) | \
        (cd $sysroot_stage1/usr/include ; tar xpf -)

    echo "Doing include hacks for stage1."
    $mkinstalldirs $sysroot_stage1/usr/include/gnu > /dev/null 2>&1
    touch $sysroot_stage1/usr/include/link.h
    touch $sysroot_stage1/usr/include/gnu/stubs.h
    touch $sysroot_stage1/usr/include/linux/autoconf.h
    if [ -f $glibc_src/features.h ]; then
        # we must be glibc 2.0.x
        cp $glibc_src/features.h $sysroot_stage1/usr/include/features.h
    elif [ -f $glibc_src/include/features.h ]; then
        # we must be glibc 2.1 or greater
        cp $glibc_src/include/features.h $sysroot_stage1/usr/include/features.h
    else
        # we have yet another different glibc
        echo "Can't find features.h in glibc-src.  Exiting."
        exit 1
    fi
    if [ -f $glibc_src/linuxthreads/sysdeps/pthread/pthread.h ]; then
        cp $glibc_src/linuxthreads/sysdeps/pthread/pthread.h $sysroot_stage1/usr/include/pthread.h
    fi

    if test -f $kernel_src/Makefile; then
	# make version.h and symlinks
	echo "Making version.h and symlinks for stage1."
	cp $kernel_src/Makefile $sysroot_stage1/usr
	(cd $sysroot_stage1/usr ; \
	 $MAKE -I $kernel_src ARCH=$KARCH PROCESSOR=$KPROC INCDIR=$KTYPE CONFIGURATION= TOPDIR=`pwd` symlinks include/linux/version.h) > /dev/null
	rm $sysroot_stage1/usr/Makefile
    fi

    # touch dummy headers libgcc depends upon from glibc
    for dir in $dummy_header_dirs; do
        (cd $sysroot_stage1; $mkinstalldirs $dir) > /dev/null
    done
    for file in $dummy_header_files; do
        (cd $sysroot_stage1; 
         echo '/* This space intentionally left blank */' > $file)
    done

    echo 'typedef struct _IO_FILE FILE;' > $sysroot_stage1/usr/include/stdio.h
    echo 'extern FILE *stderr;' >> $sysroot_stage1/usr/include/stdio.h
    echo '#define SEEK_CUR 1' >> $sysroot_stage1/usr/include/stdio.h
    echo '#define SEEK_SET 0' >> $sysroot_stage1/usr/include/stdio.h

    mkdir -p $tools_sys_root_prefix_stage1
    rm -rf $tools_sys_root_prefix_stage1/sys-root
    ln -s $sysroot_stage1 $tools_sys_root_prefix_stage1/sys-root

    echo "Done include hacks for stage1."
    touch KERN-STAGE1-STMP
else
    echo "Skipping include hacks for stage1."
fi

if [ ! -f TOOLS-STAGE1-STMP ]; then
    # build stage1 tools
    CC=$CC_for_host; export CC
    AT=" at `/bin/date +%H:%M`."
    echo "Doing tools stage1" $AT
    rm -rf tools-stage1; mkdir -p tools-stage1
    
    AT=" at `/bin/date +%H:%M`."
    (cd tools-stage1; $tools_stage1conf) > tools-stage1.log 2>&1 \
      || { echo "*** tools stage1 configure failed" $AT; exit 3; }
    AT=" at `/bin/date +%H:%M`."
    (cd tools-stage1; $MAKE -w $tools_stage1bld) >> tools-stage1.log 2>&1 \
      || { echo "*** tools stage1 build failed" $AT; exit 4; }
    AT=" at `/bin/date +%H:%M`."
    (cd tools-stage1; $MAKE -w $tools_stage1inst) >> tools-stage1.log 2>&1 \
      || { echo "*** tools stage1 install failed" $AT; exit 5; }


    # If we're building static libgcc only (for bootstraping
    # purposes), but dynamic glibc, it will try to link with
    # libgcc_eh.a, that, in a dynamic-library setting, is supposed to
    # contain some object files split out of libgcc.a.  Creating a
    # soft link is enough to get things to work.  We might as well
    # create an empty linker script file, but this approach feels
    # safer.  Ideally, glibc shouldn't just assume libgcc_eh.a.  It
    # doesn't if you disable-shared, but then we'd need yet another
    # bootstrapping stage.
    case " $tools_stage1conf " in
    *" --disable-shared "*)
      case " $glibc_stage1conf " in
      *" --disable-shared "*) ;;
      *)
        find $tools_prefix_stage1 -name libgcc.a | sed 's,libgcc\.a$,,' |
        while read d; do ln -s libgcc.a $d/libgcc_eh.a; done
	;;
      esac
      ;;
    esac

    unset CC
    touch TOOLS-STAGE1-STMP
    echo "Finished tools stage1"
else
    echo "Skipping tools stage1"
fi

# Stage 2
#     * copy kernel headers
#     * make tweaks
#     * build and install glibc with stage1 compiler
#     * build and install toolchain with stage1 compiler and stage2 glibc

PATH="$tools_exec_prefix_stage1/bin:$PATH"
export PATH

if [ ! -f KERN-STAGE2-STMP ]; then
    # copy kernel headers to install dir
    AT=" at `/bin/date +%H:%M`."
    echo "Copying kernel files for stage2" $AT
    $mkinstalldirs $sysroot_stage2/usr/include > /dev/null
    (cd $kernel_src/include ; tar cf - linux asm*) | \
        (cd $sysroot_stage2/usr/include ; tar xpf -)

    # some hacks I don't fully grok
    echo "Doing include hacks for stage2."
    $mkinstalldirs $sysroot_stage2/usr/include/gnu > /dev/null
    touch $sysroot_stage2/usr/include/gnu/stubs.h
    touch $sysroot_stage2/usr/include/linux/autoconf.h
    if [ -f $glibc_src/features.h ]; then
        # we must be glibc 2.0.x
        cp $glibc_src/features.h $sysroot_stage1/usr/include/features.h
    elif [ -f $glibc_src/include/features.h ]; then
        # we must be glibc 2.1 or greater
        cp $glibc_src/include/features.h $sysroot_stage1/usr/include/features.h
    else
        # we have yet another different glibc
        echo "Can't find features.h in glibc-src.  Exiting."
        exit 1
    fi
    if [ -f $glibc_src/linuxthreads/sysdeps/pthread/pthread.h ]; then
        cp $glibc_src/linuxthreads/sysdeps/pthread/pthread.h $sysroot_stage2/usr/include/pthread.h
    fi

    if test -f $kernel_src/Makefile; then
	# make version.h and symlinks
	echo "Making version.h and symlinks for stage2."
	cp $kernel_src/Makefile $sysroot_stage2/usr
	(cd $sysroot_stage2/usr ; \
	 $MAKE -I $kernel_src ARCH=$KARCH PROCESSOR=$KPROC INCDIR=$KTYPE CONFIGURATION= TOPDIR=`pwd` symlinks include/linux/version.h)
	rm $sysroot_stage2/usr/Makefile
    fi

    mkdir -p $tools_sys_root_prefix_stage2
    rm -rf $tools_sys_root_prefix_stage2/sys-root
    ln -s $sysroot_stage2 $tools_sys_root_prefix_stage2/sys-root

    touch KERN-STAGE2-STMP
    echo "Finished copying kernel files for stage2"
else
    echo "Skipping copying kernel files for stage2"
fi

if [ ! -f GLIBC-STAGE1-STMP ]; then
    # should be enough to build glibc now
    for gml in $GLIBC_MULTILIBS; do
        if [ X$gml = Xnone ]; then
	    POSTFIX=""
	    CC=$glibc_target-gcc; export CC
        else
	    POSTFIX="_`echo $gml | sed  's/[^][a-zA-Z0-9-]/_/g'`"
            CC="$glibc_target-gcc $gml"; export CC
        fi
        AT=" at `/bin/date +%H:%M`."
        echo "Doing glibc stage1$POSTFIX" $AT
        rm -rf glibc-stage1$POSTFIX; mkdir glibc-stage1$POSTFIX
        
        AT=" at `/bin/date +%H:%M`."
        (cd glibc-stage1$POSTFIX; $glibc_stage1conf) > glibc-stage1$POSTFIX.log 2>&1 \
          || { echo "*** glibc stage1$POSTFIX configure failed" $AT; exit 6; }
        AT=" at `/bin/date +%H:%M`."
        (cd glibc-stage1$POSTFIX; $MAKE -w) >> glibc-stage1$POSTFIX.log 2>&1 \
          || { echo "*** glibc stage1$POSTFIX build failed" $AT; exit 7; }
        AT=" at `/bin/date +%H:%M`."
        (cd glibc-stage1$POSTFIX; $MAKE -w install_root=${sysroot_stage2} install) >> glibc-stage1$POSTFIX.log 2>&1 \
          || { echo "*** glibc stage1$POSTFIX install failed" $AT; exit 8; }
    
        unset CC
        echo "Done glibc stage1$POSTFIX"
    done
    touch GLIBC-STAGE1-STMP
else
    echo "Skipping glibc stage1"
fi

if [ ! -f TOOLS-STAGE2-STMP ]; then
    # and now we do a full tool build
    CC=$CC_for_host; export CC
    
    AT=" at `/bin/date +%H:%M`."
    echo "Doing tools stage2" $AT
    rm -rf tools-stage2; mkdir tools-stage2
    
    AT=" at `/bin/date +%H:%M`."
    (cd tools-stage2; $tools_stage2conf) > tools-stage2.log 2>&1 \
      || { echo "*** tools stage2 configure failed" $AT; exit 9; }
    AT=" at `/bin/date +%H:%M`."
    (cd tools-stage2; $MAKE -w $tools_stage2bld) >> tools-stage2.log 2>&1 \
      || { echo "*** tools stage2 build failed" $AT; exit 10; }
    AT=" at `/bin/date +%H:%M`."
    (cd tools-stage2; $MAKE -w $tools_stage2inst) >> tools-stage2.log 2>&1 \
      || { echo "*** tools stage2 install failed" $AT; exit 11; }

    unset CC
    
    touch TOOLS-STAGE2-STMP
    echo "Done tools stage2"
else
    echo "Skipping tools stage2"
fi

# Stage 3
#     * copy kernel headers
#     * make tweaks
#     * build and install glibc with stage2 compiler

PATH="$tools_exec_prefix_stage2/bin:$PATH"
export PATH

if [ ! -f KERN-STAGE3-STMP ]; then
    # copy kernel headers to install dir
    AT=" at `/bin/date +%H:%M`."
    echo "Copying stage2 sysroot to stage3" $AT

    if [ -d "$sysroot_stage3" ]; then
      rm -rf "$sysroot_stage3"
    fi
    cp -a $sysroot_stage2 $sysroot_stage3

    touch KERN-STAGE3-STMP
    echo "Done copying stage2 sysroot to stage3"
else
    echo "Skipping copying stage2 sysroot to stage3"
fi

if [ ! -f GLIBC-STAGE2-STMP ]; then
    # should be enough to build glibc w/threads now
    CC=$xCC_prefix$glibc_target-gcc; export CC
    rm -rf $tools_sys_root_prefix_stage2/sys-root
    for gml in $GLIBC_MULTILIBS; do
        if [ X$gml = Xnone ]; then
	    POSTFIX=""
	    CC=$glibc_target-gcc; export CC
        else
	    POSTFIX="_`echo $gml | sed  's/[^][a-zA-Z0-9-]/_/g'`"
            CC="$glibc_target-gcc $gml"; export CC
        fi
        AT=" at `/bin/date +%H:%M`."
        echo "Doing glibc stage2$POSTFIX" $AT
        rm -rf glibc-stage2$POSTFIX; mkdir glibc-stage2$POSTFIX
    
        AT=" at `/bin/date +%H:%M`."
        (cd glibc-stage2$POSTFIX; $glibc_stage2conf) > glibc-stage2$POSTFIX.log 2>&1 \
          || { echo "*** glibc stage2$POSTFIX configure failed" $AT; exit 12; }
        AT=" at `/bin/date +%H:%M`."
        (cd glibc-stage2$POSTFIX; $MAKE -w) >> glibc-stage2$POSTFIX.log 2>&1 \
          || { echo "*** glibc stage2$POSTFIX build failed" $AT; exit 13; }
        AT=" at `/bin/date +%H:%M`."
        (cd glibc-stage2$POSTFIX; $MAKE -w install_root=$sysroot_stage3 install) >> glibc-stage2$POSTFIX.log 2>&1 \
          || { echo "*** glibc stage2$POSTFIX install failed" $AT; exit 14; }

        unset CC
    done
    cp -a $sysroot_stage3 $tools_sys_root_prefix_stage2/sys-root
    touch GLIBC-STAGE2-STMP
    echo "Done glibc stage2"
else
    echo "Skipping doing glibc stage2"
fi

AT=" at `/bin/date +%H:%M`."
echo "Ending $0 run" $AT
