#!/bin/bash

###########################################################################################
# Build cross binutils for use with 'distcc' for ARM targets
# 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.arm.slackware.com/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
###########################################################################################
# Recent changelog
###########################################################################################
# 25-Dec-2012
# * Disabled 'gold' linker for binutils-2.23.51.0.6 because it FTBFS with a powerpc target
#   Gcc: Removed langs 'ada,fortran,java,objc,lto' as we'll never use x-compile any of them
###########################################################################################

# Automatically determine architecture for build & packaging:
case "$( uname -m )" in
  # The i486 here just means "x86".
  i?86)   export LOCALARCH=i586
          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.24.51.0.3
#BINUTILSVER=2.25.1
BINUTILSVER=2.26
GCCVER=5.3.0
GLIBCVER=2.23

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


function build_kernel_headers () {

cat << "EOF"
###########################################################################
################## Extract ARM Linux Kernel headers #######################
###########################################################################
EOF

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-armv5 .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
}


function build_binutils () {

cat << "EOF"
###########################################################################
################## Build binutils #########################################
###########################################################################
EOF

# 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/$PKGNAM*diff* ]; then
   echo "*** Applying patches from Debian ***"
   ( xz -dc $PORTCWD/sources/$PKGNAM*diff* | patch -p1 ) || exit 1
       for pf in \
       \
       pr-ld-19263.diff \
       pr-ld-19264.diff ; do
       auto_apply_patch debian/patches/$pf || exit 1
   done
fi
#

# Various upstream patches:
zcat $CWD/sources/binutils/patches/binutils-2.20.51.0.2-libtool-lib64.patch | patch -p1 --verbose || exit 1
zcat $CWD/sources/binutils/patches/binutils-2.20.51.0.10-ppc64-pie.patch | patch -p1 --verbose || exit 1
zcat $CWD/sources/binutils/patches/binutils-2.25-version.patch | patch -p1 --verbose || exit 1
zcat $CWD/sources/binutils/patches/binutils-2.25-set-long-long.patch | patch -p1 --verbose || exit 1
zcat $CWD/sources/binutils/patches/binutils-2.20.51.0.10-copy-osabi.patch | patch -p1 --verbose || exit 1
zcat $CWD/sources/binutils/patches/binutils-2.20.51.0.10-sec-merge-emit.patch | patch -p1 --verbose || exit 1
zcat $CWD/sources/binutils/patches/binutils-2.22.52.0.1-relro-on-by-default.patch | patch -p1 --verbose || exit 1
zcat $CWD/sources/binutils/patches/binutils-2.23.52.0.1-addr2line-dynsymtab.patch | patch -p1 --verbose || exit 1
zcat $CWD/sources/binutils/patches/binutils-2.24-ldforcele.patch | patch -p1 --verbose || exit 1
zcat $CWD/sources/binutils/patches/binutils-2.25.1-cleansweep.patch | patch -p2 --verbose || exit 1
zcat $CWD/sources/binutils/patches/binutils-2.26-formatting.patch | patch -p2 --verbose || exit 1
zcat $CWD/sources/binutils/patches/binutils-2.26-fix-compile-warnings.patch | patch -p1 --verbose || exit 1
zcat $CWD/sources/binutils/patches/binutils-2.26-Bsymbolic_PIE.patch | patch -p1 --verbose || exit 1
zcat $CWD/sources/binutils/patches/binutils-2.26-lto.patch | patch -p1 --verbose || exit 1
zcat $CWD/sources/binutils/patches/binutils-rh1312151.patch | patch -p1 --verbose || exit 1
zcat $CWD/sources/binutils/patches/binutils-2.26-fix-GOT-offset-calculation.patch | patch -p1 --verbose || exit 1

# Export the demangle.h header file:
zcat $CWD/sources/binutils/patches/binutils.export.demangle.h.diff.gz | patch -p1 --verbose || exit 1
# Don't check to see if "config.h" was included in the installed headers:
zcat $CWD/sources/binutils/patches/binutils.no-config-h-check.diff.gz | patch -p1 --verbose || exit 1

# 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 "
GOLD=" --enable-gold=no --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-libssp \
   --disable-nls \
   --enable-plugins \
   --enable-threads \
   --enable-threads=posix \
   --enable-install-libiberty \
   --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

}


function build_glibc () {
cat << "EOF"
###########################################################################
################## Build glibc headers ####################################
###########################################################################
EOF

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

# Add ports:
# This is bundled with glibc in 2.17.
#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
#******************************************************************************
#
  # 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 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 -l --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
  #
  # 2014-05:  We'll try building with the stock asm...
  ## 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


  # Apply Debian/Ubuntu's ARM patches:
  #
  # Some binaries were incorrectly tagged with the wrong ABI:
  #auto_apply_patch $CWD/sources/glibc/sources/glibc/glibc-rh1009145.patch.xz || exit 1

  #tar xvvf $CWD/sources/glibc/eglibc*tar.xz || 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/
  #
  #
  #for i in debian/patches/arm/*diff ; do
  #  auto_apply_patch $i || exit 1
  #done

# 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-obsolete-rpc \
   --disable-profile \
   --disable-werror \
   --disable-debug \
   --without-selinux \
   --with-tls \
   --enable-kernel=2.6.32 \
   --with-headers=$INSTLOC/include/ \
   --enable-hacker-mode \
   --host=$LOCALARCH-slackware-linux \
   --build=$LOCALARCH-slackware-linux \
   --target=$TARGET || exit 1
# Not for glibc 2.21:
#   --enable-add-ons=libidn,"ports nptl " \

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/

}

function build_gcc () {

cat << "EOF"
###########################################################################
################## Build gcc ##############################################
###########################################################################
EOF

# 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:
mv gcc-$GCCVER src
ln -vfs src gcc-$GCCVER
cd gcc-* || exit 1

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

# Apply Debian patches:
#tar xvvf $CWD/sources/gcc/patches-*gcc*.xz || exit 1
# Debian switched back to a diff file for gcc 4.9 and changed the dir structure:
( cd $TMP
  mkdir gcc-dpatches ; cd gcc-dpatches
  auto_apply_patch $CWD/sources/gcc/gcc-[0-9]*_*[0-9]*-*diff*z || exit 1
  mv */debian ../src/ )

# Go back into the parent directory: needed because Debian's patch set
# includes the 'src' parent dir and 'patch' won't let us patch through
# symlinks:
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-gnu.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 \
 pr47818.diff \
 libgo-testsuite.diff \
 gcc-target-include-asm.diff \
 libgo-revert-timeout-exp.diff \
 libgo-setcontext-config.diff \
 gcc-auto-build.diff \
 kfreebsd-unwind.diff \
 kfreebsd-boehm-gc.diff \
 hurd-boehm-gc.diff \
 libitm-no-fortify-source.diff \
 sparc64-biarch-long-double-128.diff \
 gcc-ia64-bootstrap-ignore.diff \
 gotools-configury.diff \
 go-escape-analysis.diff \
 go-escape-analysis2.diff \
 go-escape-analysis3.diff \
 go-escape-analysis4.diff \
 go-escape-analysis5.diff \
 go-escape-analysis6.diff \
 pr66368.diff \
 pr67508.diff \
 pr67590.diff \
 ada-gnattools-ldflags.diff \
 libjit-ldflags.diff \
 pr69144.diff \
 pr66904.diff \
 pr68273.diff \
 pr70218.diff \
 libstdc++-functexcept.diff \
 ada-arm.diff \
 libjava-armel-unwind.diff \
 sys-auxv-header.diff \
 libcilkrts-targets.diff \
 go-use-gold.diff \
 arm-multilib-defaults.diff \
 gcc-ice-dump.diff \
 gcc-ice-apport.diff \
 skip-bootstrap-multilib.diff \
 libjava-fixed-symlinks.diff \
 libffi-ro-eh_frame_sect.diff \
 pr67899.diff \
 libjava-multiarch.diff \
 libjava-nobiarch-check.diff \
 canonical-cpppath.diff \
 libgomp-kfreebsd-testsuite.diff \
 go-testsuite.diff \
 ada-749574.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-install-libiberty \
      --enable-__cxa_atexit \
      --disable-shared \
      --without-headers \
      --disable-nls \
      --enable-languages=c,c++ \
      --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
