#!/bin/bash
ulimit -s unlimited
shopt -s extglob

###########################################################################################
# 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 LIBDIR=lib
          export SLKCFLAGS="-O2" ;;
  x86_64) export LOCALARCH=$( uname -m )
          export LIBDIR=lib64
          export SLKCFLAGS="-O2 -fPIC" ;;
esac


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

# Versions of packages:
#BINUTILSVER=2.35.2
#BINUTILSVER=2.36.1
BINUTILSVER=2.37
GCCVER=11.2.0
GLIBCVER=2.33

################## 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 () {

# Kernel architecture name.  We keep these as armv7 (ARM 32bit), armv8 (AArch64/ARM 64bit)
case ${TARGET%%-*} in
   arm)     SLKKERNELARCH=armv7;;
   aarch64) SLKKERNELARCH=armv8;;
esac

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-$SLKKERNELARCH .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.?z
cd binutils*/ || exit 1

# Various upstream patches:
# *****************************************************************************************
# ***** Remember: we prefix the patch file name with $CWD/sources/binutils/patches/ *******
# *****************************************************************************************
#

# These were shipped empty and will need to be regenerated:
rm -f binutils/doc/*.1 binutils/doc/*.man gprof/gprof.1 ld/ld.1 gas/doc/as.1

# Export the demangle.h header file (I suspect this is obsolete...):
zcat $CWD/sources/binutils/patches/binutils-export-demangle.h.patch.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.patch.gz | patch -p1 --verbose || exit 1

# Set %version to something halfway meaningful:
zcat $CWD/sources/binutils/patches/binutils-version.patch.gz | patch -p1 --verbose || exit 1
sed -i -e 's/%''{release}/slack15/g' bfd/Makefile{.am,.in}

# Use the "unsigned long long" type for pointers on hosts where long is a
# 32-bit type but pointers are a 64-bit type.  Necessary because users expect
# to be able to install both the x86 and x86_64 versions of binutils on the
# same machine, so they need identical versions of the bfd.h header file.
zcat $CWD/sources/binutils/patches/binutils-use-long-long.patch.gz | patch -p1 --verbose || exit 1

# Work around a bug caused by binutils using an ancient libtool:
zcat $CWD/sources/binutils/patches/binutils-libtool-lib64.patch.gz | patch -p1 --verbose || exit 1

# Various regression fixes:
zcat $CWD/sources/binutils/patches/binutils-2.27-aarch64-ifunc.patch.gz | patch -p1 --verbose || exit 1
zcat $CWD/sources/binutils/patches/binutils-CVE-2019-1010204.patch.gz | patch -p1 --verbose || exit 1
zcat $CWD/sources/binutils/patches/binutils-bfd-close-fds.patch.gz | patch -p1 --verbose || exit 1
zcat $CWD/sources/binutils/patches/binutils-do-not-link-with-static-libstdc++.patch.gz | patch -p1 --verbose || exit 1
zcat $CWD/sources/binutils/patches/binutils-dwarf-5-dir0.patch.gz | patch -p1 --verbose || exit 1
zcat $CWD/sources/binutils/patches/binutils-filename-in-error-messages.patch.gz | patch -p1 --verbose || exit 1
zcat $CWD/sources/binutils/patches/binutils-fix-testsuite-failures.patch.gz | patch -p1 --verbose || exit 1
zcat $CWD/sources/binutils/patches/binutils-gold-i386-gnu-property-notes.patch.gz | patch -p1 --verbose || exit 1
zcat $CWD/sources/binutils/patches/binutils-gold-mismatched-section-flags.patch.gz | patch -p1 --verbose || exit 1
zcat $CWD/sources/binutils/patches/binutils-gold-warn-unsupported.patch.gz | patch -p1 --verbose || exit 1
zcat $CWD/sources/binutils/patches/binutils-missing-man-pages.patch.gz | patch -p1 --verbose || exit 1
zcat $CWD/sources/binutils/patches/binutils-readelf-other-sym-info.patch.gz | patch -p1 --verbose || exit 1
zcat $CWD/sources/binutils/patches/binutils-revert-PLT-elision.patch.gz | patch -p1 --verbose || exit 1
zcat $CWD/sources/binutils/patches/binutils-special-sections-in-groups.patch.gz | patch -p1 --verbose || exit 1
zcat $CWD/sources/binutils/patches/binutils-testsuite-fixes.patch.gz | patch -p1 --verbose || exit 1

# Thanks to Fedora:
# Dependencies are not set up to rebuild the configure files
# in the subdirectories.  So we just rebuild the ones we care
# about after applying the configure patches
pushd libiberty
autoconf
popd
pushd intl
autoconf
popd

# Slackware ARM patches:
# None presently

#
#for pf in \
#  \
#  binutils-2.22.52.0.1-relro-on-by-default.patch \
#  binutils-2.26-fix-compile-warnings.patch \
#  binutils-2.29-skip-rp14918-test-for-arm.patch \
#  binutils-2.29-filename-in-error-messages.patch \
#  binutils-2.28-ignore-gold-duplicates.patch \
#  \
#   ; do
# auto_apply_patch $CWD/sources/binutils/${pf}.xz || exit 1
#   done
# This one needs to be applied in order for the patches lower down in the set to apply under dry-run conditions
# so we just apply this manually:
#xzcat $CWD/sources/binutils/binutils-2.26-lto.patch.xz | 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/
#    --enable-lto \
# Configure:
./configure $GOLD $NO_INITFINI \
   --enable-werror=no \
   --prefix=$INSTLOC \
   --with-sysroot=$INSTLOC \
   --enable-shared \
   --disable-libssp \
   --enable-gnu-unique-object \
   --disable-werror \
   --disable-nls \
   --enable-plugin \
   --enable-plugins \
   --enable-threads \
   --enable-threads=posix \
   --disable-compressed-debug-sections \
   --enable-deterministic-archives \
   --enable-install-libiberty \
  \
   --enable-relro=yes \
   --enable-lto \
  \
   --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
  # Support ru_RU.CP1251 locale:
  zcat $CWD/sources/glibc/glibc.ru_RU.CP1251.diff.gz | patch -p1 --verbose || exit 1
  # Add a C.UTF-8 locale:
  zcat $CWD/sources/glibc/glibc-c-utf8-locale.patch.gz | patch -p1 --verbose || exit 1
  # Don't use AM/PM format for date(1). That's just plain crazy.
  zcat $CWD/sources/glibc/glibc-2.32.en_US.no.am.pm.date.format.diff.gz | patch -p1 --verbose || exit 1
  # Other regression fixes from git:
  for git_patch in $CWD/sources/glibc/patches/*.patch.gz ; do
    zcat $git_patch | patch -p1 --verbose || exit 1
  done

# We don't apply them to the cross compiler since long ago with the 14.2 soft float release.
# They also were not applied during the bootstrapping of the hard float port either.
  # Apply Debian/Ubuntu's ARM patches:
  #
#  tar xvvf $CWD/sources/glibc/glibc_*debian*xz || exit 1
#
# # Apply:
#  for pf in \
#      \
#      arm/local-arm-futex.diff \
#     \
#     ; do
#        auto_apply_patch debian/patches/$pf || 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 \
   --disable-profile \
   --disable-werror \
   --disable-debug \
   --without-selinux \
   --with-tls \
   --enable-kernel=3.2 \
   --with-headers=$INSTLOC/include/ \
   --enable-hacker-mode \
   --host=$LOCALARCH-slackware-linux \
   --build=$LOCALARCH-slackware-linux \
   --target=$TARGET || exit 1
make cross-compiling=yes install_root=$INSTLOC prefix="" CFLAGS="-g $SLKCFLAGS -DBOOTSTRAP_GCC" install-headers || exit 1
#make -j$(nproc) csu/subdir_lib || exit 1
#install csu/crt1.o csu/crti.o csu/crtn.o $INSTLOC/lib/
#mkdir -p $INSTLOC/include/gnu
#touch $INSTLOC/include/gnu/stubs.h
#gcc-x86slka -nostdlib -nostartfiles -shared -x c /dev/null -o $INSTLOC/lib/libc.so

#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
##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-[0-9]*.tar.!(*sign|*asc|*sig) || exit 1
# Add this symlink to ease application of the patches from debian:
mv -fv gcc-* src
ln -vfs src gcc-$GCCVER
cd src/ || 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
#  [ -f $CWD/sources/gcc/gcc-[0-9]*_*[0-9]*-*diff*z ] && 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 \
#[..]
#  \
#   ; do auto_apply_patch src/debian/patches/$patchf || exit 1
# done

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

# Fix regressions in 10.3.0 (and presumably 11.1.0 since they still apply cleanly):
zcat $SLKGCCPATCHPATH/PR100102-2.a1b3484a8e6c53c8084723e3f1738d402374198e.patch.gz | patch -p1 --verbose --backup --suffix=.orig || exit 1

# Exit is for when testing if patches apply: Normally should be commented.
#exit

# ARM patches:

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

# Back into the gcc source directory:
cd $TMP/src
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 \

# Architecture configuration options:
case ${TARGET%%-*} in
   arm)     SLKCONFARGS_GCC="
            --with-arch=armv7-a \
            --with-float=hard \
            --with-tune=generic-armv7-a \
            --with-fpu=vfpv3-d16" ;;
   aarch64) SLKCONFARGS_GCC="--with-arch=armv8-a" ;;
esac
echo "GCC configuration options for $TARGET: $SLKCONFARGS_GCC"

# Configure - Note that for ARM we add --with-arch=armv7-a --with-float=hard --with-fpu=vfpv3-d16 --with-tune=generic-armv7-a
# otherwise you get errors during package building such as:
# /usr/lib/gcc/arm-slackware-linux-gnueabi/5.4.0/../../../../arm-slackware-linux-gnueabi/bin/ld: failed to merge target specific data of file libnetutil/libnetutil.a(FragmentHeader.o)
#
../configure $STACKPROT \
      --verbose \
      \
      --prefix=$INSTLOC \
      --with-local-prefix=$INSTLOC \
      \
      --enable-threads=posix \
      --enable-symvers=gnu \
      --enable-lto \
      --enable-install-libiberty \
      --enable-__cxa_atexit \
      --enable-languages=c,c++ \
      --enable-shared \
      --enable-gnu-unique-object \
      --enable-libstdcxx-dual-abi \
      --enable-clocale=gnu \
      \
      --disable-multilib \
      --disable-shared \
      --disable-nls \
      --disable-libstdcxx-pch \
      --disable-checking \
      --disable-werror \
      --disable-libssp \
      --disable-libunwind-exceptions \
      \
      --without-headers \
      \
      --with-newlib \
      --with-isl \
      --with-system-zlib \
      --with-gnu-ld \
      --with-default-libstdcxx-abi=new \
      \
      $SLKCONFARGS_GCC \
      \
      --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
ln -vfs $TARGET-{gcc,cc}

}

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

CWD=$PWD

# Build toolchain for the supported Slackware platforms:
for platform in \
   arm-slackware-linux-gnueabihf \
   aarch64-slackware-linux-gnu ; do

   # Compiler target:
   TARGET=$platform

   echo "Host & build architecture: $LOCALARCH"
   echo "Target architecture......: $platform"

   # Installation location
   #INSTLOC=/opt/devel/build-servers/x86_64/$LOCALARCH
   #
   # Here we have i486 & x86_64 so we have arch specific toolchains for these two:
   INSTLOC=/devel/build-servers/$LOCALARCH/${platform%%-*}
   rm -rf $INSTLOC
   mkdir -vpm755 $INSTLOC

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

   # Create some framework:
   # needs a lib64 here.  Well, it doesn't really. Let's try without first.
   mkdir -p $INSTLOC/lib
   mkdir -p $INSTLOC/usr/lib
   mkdir -p $INSTLOC/include

   # Use gcc first to test whether the patches apply. Normally this will be
   # commented out.
   #build_gcc || exit 1
   #exit

   # Build:
   build_kernel_headers || exit 1
   build_binutils || exit 1
   # Our binutils stuff is in here now:
   export PATH=$PATH:$INSTLOC/bin/:$INSTLOC/$platform/bin/
   build_glibc || exit 1
   build_gcc || exit 1
   finishup
done

# Clean up:
if [ ! -z "$1" ]; then
   echo "** Leaving temporary build directory '$TMP' in place"
 else
   echo "** Removing temporary build directory '$TMP'"
   rm -rf $TMP
fi
