#!/bin/bash

# Build cross binutils for ARM.
# by Stuart Winter <mozes@slackware.com>
#
# References:
# http://www.cygwin.com/ml/crossgcc/2006-12/msg00075.html
# http://www.linuxfromscratch.org/lfs/view/development/chapter05/glibc.html
# http://www.linuxfromscratch.org/lfs/view/development/chapter05/gcc-pass1.html
# 
# The purpose of this is to build enough of a toolchain that I can use
# distcc to a fast x86 host from my native ARM build hosts.
# This script does not build an entire cross toolchain and the result
# in /opt/arm is pretty messy (although could easily be tidied).
# 
# Additional scripts included:
#
# rc.local-addition: a method of starting distccd on the x86 host machines.
# dbuild: used on the ARM hosts as a wrapper for calling the Slackware ARM
# build scripts via distcc.  Instructions for usage are contained within the script.
#
# For the original work I did, check out this:
# ftp://ftp.armedslack.org/loft/oldsource/OLD_CROSS_COMPILE/ARM-compiling-bits-bobs
####################################################################################
# Notes#
########
# 1. This script is supposed to be run:
#    [ ]  on an x86 running Slackware
#    [ ]  from inside armedslack-current/source/x-toolchain
####################################################################################

# Automatically determine architecture for build & packaging:
case "$( uname -m )" in
  # The i486 here just means "x86".
  i?86)   export LOCALARCH=i486
          export SLKCFLAGS="-O3" ;;
  x86_64) export LOCALARCH=$( uname -m ) 
          export SLKCFLAGS="-O3 -fPIC" ;;
esac
echo "Local architecture: $LOCALARCH"

NUMJOBS="-j$(awk '/^processor[[:space:]]*:/{n=$3} END{print n+1}' /proc/cpuinfo)"

# Versions of packages:
#BINUTILSVER=2.20
BINUTILSVER=2.20.51.20100418
BINUTILSVER=2.20
GCCVER=4.4.4
LINUXVER=2.6.33.5
GLIBCVER=2.11.1

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

# Determine patch level required & apply the patch:
function auto_apply_patch () {
 patchfile=$1

 echo
 echo "***********************************************************"
 echo "** Working on Patch: $patchfile"
 echo "***********************************************************"
 echo

 # Decompress the patch if it's compressed with a known method:
 FTYPE=$( file $patchfile )
 case "$FTYPE" in
    *xz*compressed*)
        xz -dc $patchfile > $TMP/$(basename $patchfile).unpacked
        patchfile=$TMP/$(basename $patchfile).unpacked ;;
    *bzip2*compressed*)
        bzcat -f $patchfile > $TMP/$(basename $patchfile).unpacked
        patchfile=$TMP/$(basename $patchfile).unpacked ;;
    *gzip*compressed*)
        zcat -f $patchfile > $TMP/$(basename $patchfile).unpacked
        patchfile=$TMP/$(basename $patchfile).unpacked ;;
 esac

 # By now the patch is decompressed or wasn't compressed originally.
 #
 # Most patches should not require more levels than this:
 success=0
 for (( pl=0 ; pl<=5 ; pl++ )) ; do
   echo "Patch : $patchfile , trying patch level $pl"
     patch -N --fuzz=20 -t --dry-run -p$pl < $patchfile > /dev/null 2>&1 && success=1 && break
   done
 if [ $success = 1 ]; then
    echo "Patch: $patchfile will apply at level $pl"
    patch -N --fuzz=20 --verbose -p$pl < $patchfile
    return 0
  else
    echo "Patch: $patchfile failed to apply at levels 0-5"
    return 1
 fi
}

################## Extract ARM Linux Kernel headers #######################

function build_kernel_headers () {
echo "Unpacking Linux source"
cd $TMP
tar xf $CWD/sources/kernel/linux-$LINUXVER.tar.xz
cd linux*
make mrproper
cp -f $CWD/sources/kernel/config-kirkwood-linux-2.6 .config
make ARCH=arm headers_check || exit 1
make ARCH=arm INSTALL_HDR_PATH=$INSTLOC headers_install || exit 1
# We don't want this header - we use glibc's:
rm -fv $INSTLOC/include/scsi/scsi.h
# Nor this - this conflicts with libdrm.  Not that it makes any difference
# to us for the cross compiler, but I like to keep things in order:
rm -rf $INSTLOC/include/drm
}

################## Build binutils #########################################

function build_binutils () {
# Extract source:
cd $TMP
echo "Unpacking binutils source..."
tar xf $CWD/sources/binutils/binutils*$BINUTILSVER*.tar*
cd binutils* || exit 1

# Apply patches:
zcat $CWD/sources/binutils/binutils*diff*gz | patch -p1 || exit 1
for i in debian/patches/*patch ; do
    auto_apply_patch $i || exit 1
done

# Configure:
./configure \
   --prefix=$INSTLOC \
   --with-sysroot=$INSTLOC \
   --enable-shared \
   --disable-nls \
   --host=$LOCALARCH-slackware-linux \
   --build=$LOCALARCH-slackware-linux \
   --target=$TARGET || exit 1

# Build:
make $NUMJOBS || exit 1
# Install into /opt:
make install

# Our binutils stuff is in here now:
export PATH=$PATH:$INSTLOC/bin/:$INSTLOC/arm-slackware-linux/bin/
}

################## Build glibc headers ###########################################

function build_glibc () {

cd $TMP
echo "Unpacking glibc source..."
tar xf $CWD/sources/glibc/glibc-$GLIBCVER*.tar.*
cd glibc* || exit 1

# Add ports:
tar xf $CWD/sources/glibc/glibc-ports-*.tar.* || exit 1
rm -rf ports
mv glibc-ports-* ports || exit 1

# Unpack & apply patches:
set -x

# Apply Debian's ARM patches:
zcat $CWD/sources/glibc/eglibc*diff* | patch -p1 || exit 1
for i in debian/patches/arm/* ; do
  auto_apply_patch $i || exit 1
done

# Apply Slackware-specific patches -- taken from glibc.SlackBuild:
#
# Use old-style locale directories rather than a single (and strangely
# formatted) /usr/lib/locale/locale-archive file:
zcat $CWD/sources/glibc/glibc.locale.no-archive.diff.gz | patch -p1 --verbose || exit 1
# The is_IS locale is causing a strange error about the "echn" command
# not existing.  This patch reverts is_IS to the version shipped in
# glibc-2.5:
zcat $CWD/sources/glibc/is_IS.diff.gz | patch -p1 --verbose || exit 1
# Fix NIS netgroups:
zcat $CWD/sources/glibc/glibc.nis-netgroups.diff.gz | patch -p1 --verbose || exit 1
# Support ru_RU.CP1251 locale:
zcat $CWD/sources/glibc/glibc.ru_RU.CP1251.diff.gz | patch -p1 --verbose || exit 1
# Fix missing MAX macro in getcwd.c:
zcat $CWD/sources/glibc/glibc.getcwd.max.macro.diff.gz | patch -p1 --verbose || exit 1
# Fix resolver problem with glibc-2.9:
zcat $CWD/sources/glibc/glibc-2.10-dns-no-gethostbyname4.diff.gz | patch -p0 --verbose || exit 1
# This reverts a patch that was made to glibc to fix "namespace leakage",
# which seems to cause some build failures (e.g. with conntrack):
zcat $CWD/sources/glibc/glibc.revert.to.fix.build.breakages.diff.gz | patch -p1 --verbose || exit 1

# Gentoo patches:
#tar xvvf $CWD/sources/glibc/glibc-patches-*.tar.bz2 -C.
#for i in patches/*.patch ; do
#  auto_apply_patch $i 
#done

# Apply patch to build with make-3.82
# (from: http://www.linuxquestions.org/questions/linux-from-scratch-13/glibc-not-compiling-in-chapter-6-9-a-824573/)
sed -i 's/ot \$/ot:\n\ttouch $@\n$/' manual/Makefile

set +x

# Configure:
mkdir build.dir
cd build.dir
CFLAGS="-g $SLKCFLAGS -DBOOTSTRAP_GCC" \
../configure libc_cv_forced_unwind=yes libc_cv_c_cleanup=yes \
   --prefix=$INSTLOC \
   --without-cvs \
   --disable-sanity-checks \
   --enable-add-ons=libidn,"ports nptl " \
   --disable-profile \
   --without-selinux \
   --with-tls \
   --enable-kernel=2.6.31 \
   --with-headers=$INSTLOC/include/ \
   --enable-hacker-mode \
   --host=$LOCALARCH-slackware-linux \
   --build=$LOCALARCH-slackware-linux \
   --target=$TARGET || exit 1

make $NUMJOBS sysdeps/gnu/errlist.c || exit 1
mkdir -p stdio-common
touch stdio-common/errlist-compat.c
make $NUMJOBS cross-compiling=yes install_root=$INSTLOC prefix="" CFLAGS="-g $SLKCFLAGS -DBOOTSTRAP_GCC" install-headers || exit 1

mkdir -p $INSTLOC/include/gnu
touch $INSTLOC/include/gnu/stubs.h
#cp -vfa bits/stdio_lim.h $INSTLOC/bits/stdio_TARGET_PREFIX
cp -vfa bits/stdio_lim.h $INSTLOC/include/bits/

}

################## Build gcc ##############################################

function build_gcc () {

# Extract source:
cd $TMP
echo "Unpacking gcc source..."
tar xf $CWD/sources/gcc/gcc-$GCCVER.tar.xz
cd gcc-* || exit 1

# Apply Debian patches:
zcat $CWD/sources/gcc/gcc*diff* | patch -p1 || exit 1
  pushd debian/patches
  rm -f ada-gcc-name.diff # don't want to change this
  for patchf in \
     svn*update* \
     ada* \
     *arm* \
     libjava* \
     pr*diff* \
     rev146451.diff ; do
       popd 2> /dev/null # little hack
       auto_apply_patch debian/patches/$patchf # || exit 1
   done

# Apply Gentoo patches:
#tar xvvf $CWD/sources/gcc-*-patches-*.tar.bz2
#for i in patch/*.patch ; do
#  auto_apply_patch $i
#done

mkdir gcc.build
cd gcc.build

# -fstack-protector on gcc 4.2.3 causes segfaults on ARM
# so it's best to disable it since many newer packages check for 
# this option and enable if supported.
#STACKPROT="--disable-libssp --disable-sjlj-exceptions"
# It works in 4.4.2:
STACKPROT="--enable-libssp"

# --enable-libffi
#      --enable-threads=no \

# Configure:
../configure $STACKPROT \
      --prefix=$INSTLOC \
      --disable-multilib \
      --with-newlib \
      --enable-threads=posix \
      --with-local-prefix=$INSTLOC \
      --enable-symvers=gnu \
      --enable-__cxa_atexit \
      --disable-shared \
      --without-headers \
      --disable-nls \
      --enable-languages=c,c++,fortran,objc \
      --disable-checking \
      --verbose \
      --with-arch=armv4t \
      --host=$LOCALARCH-slackware-linux \
      --build=$LOCALARCH-slackware-linux \
      --target=$TARGET || exit 1 

# Build:
make $NUMJOBS all-gcc || exit 1
make install-gcc || exit 1

}

function finishup() {

# Make symlinks:
cd $INSTLOC/bin
for i in * ; do
  ln -vfs $i $( echo $i | sed s'?'"$TARGET"-'??' )
done
ln -vfs gcc cc

}

#######################################################################

# Compiler target:
TARGET=arm-slackware-linux-gnueabi

# Installation location
#INSTLOC=/opt/arm/$LOCALARCH
#
# Here we have i486 & x86_64 so we have arch specific toolchains for these
# two:
INSTLOC=/devel/build-servers/$LOCALARCH/arm
rm -rf $INSTLOC
mkdir -vpm755 $INSTLOC
CWD=$PWD

# Temp build locations:
TMP=/tmp/xbuild
rm -rf $TMP
mkdir -p $TMP

# Create some framework:
mkdir -p $INSTLOC/lib
mkdir -p $INSTLOC/usr/lib
mkdir -p $INSTLOC/include

# Build:
build_kernel_headers || exit 1
build_binutils || exit 1
build_glibc || exit 1
build_gcc || exit 1
finishup
