#!/bin/bash

# Build cross binutils for ARM.
# by Stuart Winter <mozes@slackware.com>
# Some parts are taken from Slackware package build scripts directly.
#
# 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
#
# If this was to be extended to a full cross compiler, this would be useful:
## http://gcc.gnu.org/onlinedocs/gcc-4.4.3/gcc/Environment-Variables.html#Environment-Variables
#TD=/devel/build-servers/x86_64/arm/
#export GCC_EXEC_PREFIX=$TD
#export COMPILER_PATH=$TD/bin:$TD/libexec/gcc/arm-slackware-linux-gnueabi/4.5.1/
#export C_INCLUDE_PATH=$TD/include/:$TD/lib/gcc/arm-slackware-linux-gnueabi/4.5.1/include/
#export CPLUS_INCLUDE_PATH=$TD/include/:$TD/lib/gcc/arm-slackware-linux-gnueabi/4.5.1/include/
#export OBJC_INCLUDE_PATH=$TD/include/:$TD/lib/gcc/arm-slackware-linux-gnueabi/4.5.1/include/
####################################################################################
# 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.22
#BINUTILSVER=2.22.52.0.1
BINUTILSVER=2.22.52.0.2
GCCVER=4.7.1
GLIBCVER=2.15

################## 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

# Cheat and use the ones built when we built the kernel package on a
# real ARM box:
# tar xf $CWD/sources/kernel/kernel-headers*tar.xz -C $INSTLOC || exit 1

tar xf $CWD/sources/kernel/linux-*.tar.xz
cd linux* || exit 1
make mrproper
cp -f $CWD/sources/kernel/config-kirkwood .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*.tar.*
cd binutils* || exit 1

# Apply patches:
if [ -f $CWD/sources/binutils/$PKGNAM*diff* ]; then
   echo "*** Applying patches from Debian ***"
   ( xz -dc $PORTCWD/sources/binutils/$PKGNAM*diff* | patch -p1 ) || exit 1
   egrep -v '^#' debian/patches/series | while read line ; do
       auto_apply_patch debian/patches/$line || exit 1
   done
fi

# The --enable-initfini-array option was added in binutils-2.21.51.0.3.
# This option currently causes a world of hurt trying to compile glibc,
# and might break static libraries or cause other ill effects.  There
# is an upstream patch for glibc but it does not avoid all of the known
# problems (and there may be some unknown ones, too), so we will avoid
# introducing this feature for now.
# References:
# http://sourceware.org/bugzilla/show_bug.cgi?id=12343
# http://gcc.gnu.org/bugzilla/show_bug.cgi?id=46770
NO_INITFINI=" --disable-initfini-array "

# Uncomment this to include the experimental gold linker:
GOLD=" --enable-gold=yes --enable-ld=default "
# Set to ld.gold or ld.bfd:
DEFAULT_LD=ld.bfd

# Added --enable-plugins --enable-threads for binutils-2.20.51.0.11
#   --enable-64-bit-bfd \
# as suggested here: http://patchwork.openembedded.org/patch/2178/
# Configure:
./configure $GOLD $NO_INITFINI \
   --enable-werror=no \
   --prefix=$INSTLOC \
   --with-sysroot=$INSTLOC \
   --enable-shared \
   --disable-nls \
   --enable-plugins \
   --enable-threads \
   --host=$LOCALARCH-slackware-linux \
   --build=$LOCALARCH-slackware-linux \
   --target=$TARGET || exit 1

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

}

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

function build_glibc () {

cd $TMP
echo "Unpacking glibc source..."
tar xf $CWD/sources/glibc/glibc-$GLIBCVER.tar.xz
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 Slackware-specific patches -- taken from glibc.SlackBuild:
#******************************************************************************
# When pasting from ARMedslack's glibc.SlackBuild, remember to search+replace
# $CWD/ with $CWD/sources/glibc/
# and $PORTCWD with $CWD
#******************************************************************************
#
  # Reexport the RPC interfaces that were removed in glibc-2.14.
  # Sure, it's crufy code, but stuff needs it, so rather than pull the
  # rug out from under you, we'll just humbly recommend that you consider
  # transitioning away from it...  :-)
  zcat $CWD/sources/glibc/glibc-2.14-reexport-rpc-interface.patch.gz | patch -p1 --verbose || exit 1
  # Add back the NIS and RPC headers:
  zcat $CWD/sources/glibc/glibc-2.14-reinstall-nis-rpc-headers.patch.gz | patch -p1 --verbose || exit 1
  #
  # 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
  # This partial security patch still applies and might be needed:
  zcat $CWD/sources/glibc/glibc.git-96611391ad8823ba58405325d78cefeae5cdf699-CVE-2010-3847b.patch.gz | patch -p1 --verbose || exit 1
  # Make it harder for people to trick ldd into running code:
  zcat $CWD/sources/glibc/glibc.ldd.trace.through.dynamic.linker.diff.gz | patch -p1 --verbose || exit 1
  # Make glibc compile with binutils using --enable-initfini-array.
  # At this time, we do not recommend this due to probable ABI breakage.
  # The also patch needs work before it would apply.
  # ***NOT READY***
  #zcat $CWD/sources/glibc/glibc.git-4a531bb0b3b582cb693de9f76d2d97d970f9a5d5.patch.gz | patch -p1 --verbose || exit 1
  #
  # Avoid the Intel optimized asm routines for now because they break
  # the flash player.  We'll phase this in when it's safer to do so.
  zcat $CWD/sources/glibc/glibc.disable.broken.optimized.memcpy.diff.gz | patch -p1 --verbose || exit 1

  # Upstream fixes to avert Firefox crashes:
  zcat $CWD/sources/glibc/glibc-2.14.1-fixes-1.patch.gz | patch -p1 --verbose || exit 1
  # Fix an integer overflow in tzfile:
  zcat $CWD/sources/glibc/glibc.CVE-2009-5029.diff.gz | patch -p1 --verbose || exit 1

  # Apply patches from Gentoo:
  #
  mkdir gentoo-patches
  tar xvvf $CWD/sources/glibc/glibc-*-patches-*.tar.* -C gentoo-patches/
  # We could apply them all, but we won't.
  for patchfile in gentoo-patches/patches/*-arm-* ; do
    auto_apply_patch $patchfile || exit 1
  done

  # Apply Debian/Ubuntu's ARM patches:
  xz -dc $CWD/sources/glibc/eglibc*diff* | patch -p1 || exit 1
  # Add a handful taken from Ubuntu's patch set:
  # Normally this shouldn't be necessary unless I'm mixing patches from another
  # version of the glibc diff -- usually everything we need should be in the single
  # Debian/Ubuntu diff.
  # cp -fav $PORTCWD/sources/ubuntu-patches/* debian/patches/arm/
  #
  # Wipe a few since these are for glibc 2.13:
  rm -f debian/patches/arm/{cvs-syscall-mcount.diff,submitted-armhf-triplet.diff,cvs-clone-cantunwind.diff,cvs-ucontext.diff}
  #
  for i in debian/patches/arm/*diff ; do
    auto_apply_patch $i || exit 1
  done

  # For 2.15 and sfp ARM:
  auto_apply_patch $CWD/sources/glibc/softfloat-2.15.patch.xz || exit 1


# Configure:
mkdir build.dir
cd build.dir
CFLAGS="-g $SLKCFLAGS -DBOOTSTRAP_GCC" \
../configure libc_cv_forced_unwind=yes libc_cv_c_cleanup=yes libc_cv_gnu89_inline=yes \
   --prefix=$INSTLOC \
   --without-cvs \
   --disable-sanity-checks \
   --enable-add-ons=libidn,"ports nptl " \
   --disable-profile \
   --disable-debug \
   --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 || make 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
make 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 || exit 1
# Add this symlink to ease application of the patches from debian:
rm -rf src
ln -vfs gcc-$GCCVER src 
cd gcc-* || exit 1

########### Apply patches #################################################

# Apply Debian patches:
xz -dc $CWD/sources/gcc/gcc*diff.xz | patch -p1 || exit 1

# Go back into the parent directory
cd ..

# This list is taken from the Debian Build log for the particular
# patch release we're using, with a few redundant ones stripped.
for patchf in \
  svn-updates.diff \
  gcc-gfdl-build.diff \
  gcc-textdomain.diff \
  gcc-driver-extra-langs.diff \
  gcc-hash-style-both.diff \
  libstdc++-pic.diff \
  libstdc++-doclink.diff \
  libstdc++-man-3cxx.diff \
  libstdc++-test-installed.diff \
  libjava-stacktrace.diff \
  libjava-jnipath.diff \
  libjava-sjlj.diff \
  libjava-disable-plugin.diff \
  alpha-no-ev4-directive.diff \
  boehm-gc-getnprocs.diff \
  note-gnu-stack.diff \
  libgomp-omp_h-multilib.diff \
  sparc-force-cpu.diff \
  pr24619.diff \
  pr45078.diff \
  pr47818.diff \
  pr49940.diff \
  pr49944.diff \
  libffi-kfreebsd.diff \
  libffi-powerpc-sf.diff \
  libffi-powerpc-sysv-without-string-ops.diff \
  mudflapth-link.diff \
  libffi-m68k.diff \
  libgo-fix-arm.diff \
  sh4-enable-ieee.diff \
  gcc-cloog-dl.diff \
  libiberty-md5.diff \
  libgo-hardening.diff \
  pr26155.diff \
  gcc-d-lang.diff \
  libjava-armel-unwind.diff \
  arm-dynamic-linker.diff \
  libgo-mksysinfo.diff \
  pr33763.diff \
  arm-multilib-defaults.diff \
  gcc-ice-hack.diff \
  gcc-ice-apport.diff \
  libjava-fixed-symlinks.diff \
  libstdc++-arm-wno-abi.diff \
  ada-mips.diff \
  libffi-ro-eh_frame_sect.diff \
  libjava-multiarch.diff \
  libjava-nobiarch-check.diff \
  config-ml.diff \
  gcc-powerpc-undef.diff \
  gcc-no-add-needed.diff \
  mips-fix-loongson2f-nop.diff \
  libgomp-kfreebsd-testsuite.diff \
  armhf-triplet.diff ; do
      auto_apply_patch src/debian/patches/${patchf} || exit 1
  done

#
# Apply Slackware patches:
#
# Smite the fixincludes:
cd gcc-$GCCVER
zcat $CWD/sources/gcc/gcc-no_fixincludes.diff.gz | patch -p1 --verbose --backup --suffix=.orig || exit 1

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

# Back into the gcc source directory:
cd $TMP/gcc-*
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-lto \
      --enable-__cxa_atexit \
      --disable-shared \
      --without-headers \
      --disable-nls \
      --enable-languages=ada,c,c++,fortran,java,objc,lto \
      --disable-checking \
      --verbose \
      --with-arch=armv5te \
      --host=$LOCALARCH-slackware-linux \
      --build=$LOCALARCH-slackware-linux \
      --target=$TARGET || exit 1 

# Build:
make $NUMJOBS all-gcc || make 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
# Our binutils stuff is in here now:
export PATH=$PATH:$INSTLOC/bin/:$INSTLOC/arm-slackware-linux/bin/
build_glibc || exit 1
build_gcc || exit 1
finishup
