#!/bin/sh

# gcc universal build script for FSF gcc
# (C)2007 Simon Urbanek; licensed under GPL
#
# run "gcc-universal.sh -h" for help


if [ "$1" = "-h" ]; then
  echo ""
  echo " This script builds full cross-compiling gcc compilers for Mac OS X"
  echo " supporting all four architectures and featuring universal compilers"
  echo " plus Apple's darwin driver."
  echo ""
  echo " It will try hard to fetch all it needs as necessary from the web."
  echo ""
  echo " Some interesting environment variables it honors:"
  echo " GPRE   - [/usr/local] install prefix for gcc, gmp/mpfr and tool stubs,"
  echo "          this directory must exist."
  echo " GBB    - gcc build base, the root directory of where everything happens"
  echo "          by default it is the current directory when this script is run"
  echo " GVER   - [4.2] version suffix to be used by the compiler and driver."
  echo "          it should be of the form x.y"
  echo " gsrc   - [\$GBB/gcc-4.2] path to gcc sources. If that path doesn't exist,"
  echo "          the script attempts to fetch gcc-4.2 branch via SVN"
  echo " BIN    - [\$GBB/bin] path containing stubs for cross-compiling tools"
  echo "          (such as as, ar, ranlib, gcc..). Those stubs are usually"
  echo "          generated in the course of Apple's gcc build."
  echo " DRIVER - [\$GBB/driver] path to the Makefile that builds the corresponding"
  echo "          Apple driver"
  echo " LANGS  - [fortran] - languages to build. This value is passed to configure"
  echo "          as --enable-languages flag to gcc."
  echo ""
  echo " The script attempts to recover from previous failures, so you can re-run it"
  echo " and it won't re-build what it already built, but you have to clean"
  echo " \${GPRE}. The only expection is if you want to build the drivers only"
  echo " in which case you can run this script with -driver parameter"
  exit 0
fi

if [ `arch` = ppc ]; then
  echo ""
  echo " Sorry, this script works only on Intel Macs. The reason is that we want"
  echo " to build native compilers for both i686 and ppc which is possible only"
  echo " on Intel Macs using Rosetta such that we can run both ppc and i686 code."
  echo ""
  echo " If you know what you're doing, you can build the i686 native compiler"
  echo " and cross-compilers some other way and modify this script accordingly."
  exit 1
fi

: ${GVER=4.2}
: ${GPRE="/usr/local"}
: ${GBB=`pwd`}

: ${BIN="${GBB}/bin"}
: ${DRIVER="${GBB}/driver"}
: ${I2I="${GBB}/i2i"}
: ${P2P="${GBB}/p2p"}
: ${I2P="${GBB}/i2p"}
: ${P2I="${GBB}/p2i"}

: ${LANGS=fortran}

: ${gsrc="${GBB}/gcc-4.2"}

echo " +----------------------------------------------"
echo " |  target prefix: ${GPRE}"
echo " |  driver binary: gcc-${GVER}"
echo " |  building in  : ${GBB}"
echo " |"
echo ''

cd "${GBB}"

if [ "$1" != -driver ]; then # XXX


if [ -e "${GPRE}/lib/libgcc_s.1.dylib" ]; then
  echo "**ERROR: There are already libraries and/or compilers in ${GPRE}" >&2
  echo "         Please start with a clean ${GPRE} (except for gmp+mpfr)!" >&2
  echo ''
  echo " For example you may want to move your existing ${GPRE} aside for this compilation"
  echo ""
  echo " If this script succeeded before up to the driver, you can use"
  echo " $0 -driver"
  echo " to re-run the driver compilation and tests."
  echo ""
  exit 1
fi

mkdir -p ${GPRE}/lib 2>/dev/null
rm -f ${GPRE}/lib/.wt
echo "test" > ${GPRE}/lib/.wt
if [ ! -e "${GPRE}/lib/.wt" ]; then
  echo "**ERROR: You don't have write permission in ${GPRE}!"
  echo "         You have to run this script as root (e.g. sudo gcc-universal.sh)"
  echo "         If you are concerned about security, make yourself owner of ${GPRE}"
  echo "         run this script and change the ownership back to root."
  exit 1
fi
rm -f ${GPRE}/lib/.wt

auxlib=ok
if [ ! -e "${GPRE}/lib/libgmp.a" ]; then
  auxlib=fail
fi
if [ ! -e "${GPRE}/lib/libmpfr.a" ]; then
  auxlib=fail
fi
if [ ${auxlib} = fail ]; then
  echo "-- Missing gmp/mpfr!"
  cd "${GBB}"
  if [ ! -e "gmp-mpfr-biarch.tar.gz" ]; then
    echo "   Attempting to get them from R site ..."
    curl -O -s -S http://r.research.att.com/gmp-mpfr-biarch.tar.gz
  fi
  pax -r -z -s ",usr/local,${GPRE}/," -f gmp-mpfr-biarch.tar.gz
fi
auxlib=ok
if [ ! -e "${GPRE}/lib/libgmp.a" ]; then
  auxlib=fail
fi
if [ ! -e "${GPRE}/lib/libmpfr.a" ]; then
  auxlib=fail
fi
if [ ${auxlib} = fail ]; then
  echo "** Missing gmp/mpfr! Please install static gmp and mpfr before proceeding." >&2
  exit 1
fi

if [ ! -e "${BIN}/i686-apple-darwin8-as" ]; then
  echo "-- Missing cross-compile stubs."
  echo "   attempting to get it from the R site.."
  cd "${GBB}"
  curl -s -S http://r.research.att.com/gcc-bin-stubs.tar.gz | pax -r -z -s ",usr/local,${GPRE}/,"
fi
if [ ! -e "${BIN}/i686-apple-darwin8-as" ]; then
  echo "** ERROR: BIN doesn't point to the cross-compile stubs." >&2
  echo "   Install them first and set BIN accordingly" >&2
  exit 1
fi

if [ ! -e "${gsrc}/gcc/version.c" ]; then
  echo "=== gcc sources not found, attempting to fetch them via SVN ..."
  mkdir "${gsrc}"
  cd "${gsrc}"
  svn co svn://gcc.gnu.org/svn/gcc/branches/gcc-4_2-branch .
  cd "${GBB}"
fi

if [ ! -e "${gsrc}/gcc/version.c" ]; then
  echo "** ERROR: cannot find gcc sources in ${gsrc}" >&2
  echo "   Fetch them and/or set gsrc" >&2
  exit 1
fi

echo "-- gcc sources are OK"
cd "${GBB}"

# === build all compilers and cross-compilers

# build i2i
if [ -e "${I2I}/config.status" ]; then
  echo "--> i686->i686 compiler seems configured already, skipping"
  echo "    (remove ${I2I}/config.status to force re-configuration)"
  cd "${I2I}"
else
  mkdir -p "${I2I}"
  cd "${I2I}"
  ${gsrc}/configure --prefix=${GPRE} --mandir=/share/man '--program-transform-name=/^[cg][^.-]*$/s/$/-'${GVER}/ --build=i686-apple-darwin8 --host=i686-apple-darwin8 --target=i686-apple-darwin8 --enable-languages=${LANGS}
fi

if make -j5; then
  echo "+-------------------------------------------------------------+"
  echo "| i686->i686 build complete.                                  |"
  echo "+-------------------------------------------------------------+"
else
  echo "ERROR: i686->i686 compiler build failed. Please check build output before proceeding."
  exit 1
fi

# build p2p
if [ -e "${P2P}/config.status" ]; then
  echo "--> powerpc->powerpc compiler seems configured already, skipping"
  echo "    (remove ${I2I}/config.status to force re-configuration)"
  cd "${P2P}"
else
  mkdir -p "${P2P}"
  cd "${P2P}"
  CC='gcc -arch ppc' ${gsrc}/configure --prefix=${GPRE} --mandir=/share/man '--program-transform-name=/^[cg][^.-]*$/s/$/-'${GVER}/ --build=powerpc-apple-darwin8 --host=powerpc-apple-darwin8 --target=powerpc-apple-darwin8 --enable-languages=${LANGS}
fi

if make -j5; then
  echo "+-------------------------------------------------------------+"
  echo "| powerpc->powerpc build complete.                            |"
  echo "+-------------------------------------------------------------+"
else
  echo "ERROR: powerpc->powerpc compiler build failed. Please check build output before proceeding."
  exit 1
fi

# note: cross-compilers don't have to build cleanly;
# if they fail in libgfortran or any other auxiliary library, it doesn't matter,
# because we use libraries from native builds. 

# build i2p
if [ -e "${I2P}/gcc/xgcc" ]; then
  echo "---> i686->powerpc cross-compiler appears to have been built already, skipping."
  echo"      (remove ${I2P}/gcc/xgcc to force a re-build)"
else
  if [ -e "${I2P}/config.status" ]; then
    echo "---> i686->powerpc cross-compiler is already configured, skipping configure step."
    echo "     (Remove ${I2P}/config.status to force configuration)"
    cd "${I2P}"
  else
    mkdir -p "${I2P}" 2>/dev/null
    cd "${I2P}"
    ${gsrc}/configure --prefix=${GPRE} --mandir=/share/man '--program-transform-name=/^[cg][^.-]*$/s/$/-'${GVER}/ --build=i686-apple-darwin8 --host=i686-apple-darwin8 --target=powerpc-apple-darwin8 --enable-languages=${LANGS}
  fi

  make -j5

  if [ ! -e "${I2P}/gcc/xgcc" ]; then
    echo "**ERROR: i686->powerpc cross-compiler could not be built. Check above build output."
    exit 1
  fi
fi


# build p2i
if [ -e "${P2I}/gcc/xgcc" ]; then
  echo "---> powerpc->i686 cross-compiler appears to have been built already, skipping."
  echo"      (remove ${P2I}/gcc/xgcc to force a re-build)"
else
  if [ -e "${P2I}/config.status" ]; then
    echo "---> powerpc->i686 cross-compiler is already configured, skipping configure step."
    echo "     (Remove ${P2I}/config.status to force configuration)"
    cd "${P2I}"
  else
    mkdir -p "${P2I}" 2>/dev/null
    cd "${P2I}"
    CC='gcc -arch ppc' ${gsrc}/configure --prefix=${GPRE} --mandir=/share/man '--program-transform-name=/^[cg][^.-]*$/s/$/-'${GVER}/ --build=powerpc-apple-darwin8 --host=powerpc-apple-darwin8 --target=i686-apple-darwin8 --enable-languages=${LANGS}
  fi
  make -j5

  if [ ! -e "${P2I}/gcc/xgcc" ]; then
    echo "**ERROR: powerpc->i686 cross-compiler could not be built. Check above build output."
    exit 1
  fi
fi

echo "---------------------------------------------------------------"
echo ""
echo "           BUILDS COMPLETED - TIME TO INSTALL !!"
echo ""
echo "---------------------------------------------------------------"
echo ""
echo "+-------------------------------------------------------------+"
echo "| i686-to-i686 compiler is about to be installed              |"
echo "+-------------------------------------------------------------+"

# i2i, install
cd "${I2I}"
if make install; then
  echo "+-------------------------------------------------------------+"
  echo "| i686-to-i686 compiler installed                             |"
  echo "+-------------------------------------------------------------+"
else
  echo "**ERROR: i686 native compiler failed to install. Check output above." >&2
  exit 1
fi

if [ ! -e "${GPRE}/bin/gcc-${GVER}" ]; then
  echo "**ERROR: Cannot find i686 native compiler binaries in ${GPRE}/bin." >&2
  echo "         This script works only with ${GPRE} prefix. You have to modify it to use another prefix." >&2
  exit 1
fi

cd ${GPRE}/bin
mkdir i386
mv * i386/
cd ${GPRE}/lib
mkdir i386
mv * i386/

echo "+-------------------------------------------------------------+"
echo "| powerpc-to-powerpc compiler is about to be installed        |"
echo "+-------------------------------------------------------------+"

# p2p, install
cd "${P2P}"
if make install; then
  echo "+-------------------------------------------------------------+"
  echo "| powerpc-to-powerpc compiler installed                       |"
  echo "+-------------------------------------------------------------+"
else
  echo "**ERROR: powerpc native compiler failed to install. Check output above." >&2
  exit 1
fi

if [ ! -e "${GPRE}/bin/gcc-${GVER}" ]; then
  echo "**ERROR: Cannot find powerpc native compiler binaries in ${GPRE}/bin." >&2
  echo "         This script works only with ${GPRE} prefix. You have to modify it to use another prefix." >&2
  exit 1
fi

cd ${GPRE}/bin
mkdir ppc
mv * ppc/
mv ppc/i386 .
cd ${GPRE}/lib
mkdir ppc
mv * ppc/
mv ppc/i386 .

echo "+-------------------------------------------------------------+"
echo "| fixing libraries ...                                        |"
echo "+-------------------------------------------------------------+"

mv i386/libgmp.* .
mv i386/libmpfr.* .

# get the list of binary files: non-link shlibs and archives
cd ${GPRE}/lib/i386
binfiles=`ls -l *dylib *.a|grep ^-|awk '{print $9}'`
cd ${GPRE}/lib

# create common libs
cd ${GPRE}/lib
for i in ${binfiles}; do
  lipo -create i386/$i ppc/$i -o $i;
  if [ -e i386/x86_64/$i ]; then
    lipo -create $i i386/x86_64/$i -o $i
    rm -f i386/x86_64/$i
    ln -s ../$i i386/x86_64/$i
  fi
  if [ -e ppc/ppc64/$i ]; then
    lipo -create $i ppc/ppc64/$i -o $i
    rm -f ppc/ppc64/$i
    ln -s ../$i ppc/ppc64/$i
  fi
  if otool -D $i|sed -n '2 p'|grep ^${GPRE} >/dev/null; then
    # change the id to point to ${GPRE}/lib
    ln=`otool -D $i|sed -n '2 p'`
    ln=`basename $ln`
    install_name_tool -id ${GPRE}/lib/$ln $i
  fi
done

# other files (they should be the same for both archs, so we take them from the i386 branch)
for i in `ls i386/`; do
 if [ ! -e $i ]; then
   if [ -L "i386/$i" ]; then
     ln -s `readlink i386/$i` $i
   else
     if [ -f "i386/$i" ]; then
       cp -p "i386/$i" "$i"
     fi
   fi
 fi
done

# do ranlib on all archs
for i in `ls *.a`; do ranlib "$i" >/dev/null; done

mv i386/x86_64 .
mv ppc/ppc64 .

rm -rf gcc # should not be necessary ...

mkdir gcc
mv i386/gcc/* gcc/
mv ppc/gcc/* gcc/

# clean up intermediate files/directories
rm -rf i386 ppc common

echo "+-------------------------------------------------------------+"
echo "| building universal compiler binaries ...                    |"
echo "+-------------------------------------------------------------+"

# done with the libs, let's fix bin
cd ${GPRE}/bin

mv i386/i686* .
mv ppc/power* .

# we ignore everything that doesn't use our driver - if you disagree, change it
rm -rf i386 ppc


# -- ok, now we should have a working compiler for each platform
# next step is to take the cross-compilers and wedge them in

cd ${GPRE}/bin
# remove stray copies (we want ...-4.2 only, but there is -4.2.0 sometimes)
rm -f *-apple-darwin*-*-?.?.?
for comp in gfortran g++; do
if [ -e "${I2P}/gcc/${comp}" ]; then
  fn=`ls powerpc-apple-darwin*-${comp}-*`
  lipo -create ${fn} ${I2P}/gcc/${comp} -o ${fn}
  fn=`ls i686-apple-darwin*-${comp}-*`
  lipo -create ${fn} ${P2I}/gcc/${comp} -o ${fn}
fi
done
fn=`ls powerpc-apple-darwin*-gcc-*`
lipo -create ${fn} ${I2P}/gcc/xgcc -o ${fn}
fn=`ls i686-apple-darwin*-gcc-*`
lipo -create ${fn} ${P2I}/gcc/xgcc -o ${fn}

cd ${GPRE}/libexec/gcc/powerpc-apple-darwin*/*
for i in f951 cc1 collect2; do
lipo -create $i ${I2P}/gcc/$i -o $i
strip $i
done
cd ${GPRE}/libexec/gcc/i686-apple-darwin*/*
for i in f951 cc1 collect2; do
lipo -create $i ${P2I}/gcc/$i -o $i
strip $i
done

fi #XXX

echo "+-------------------------------------------------------------+"
echo "| setup the driver ...                                        |"
echo "+-------------------------------------------------------------+"

if [ ! -e "${DRIVER}/Makefile" ]; then
  echo "-- Missing Makefile for the Darwin driver"
  echo "   attempting to get it from the R site.."
  cd "${GBB}"
  curl -s -S http://r.research.att.com/gcc-darwin-driver.tar.gz | tar fxz -
fi
if [ ! -e "${DRIVER}/Makefile" ]; then
  echo "** ERROR: Missing Makefile for the Darwin driver"
  echo "   Fetch the driver manually as necessary or set DRIVER accordingly"
  echo "   run this script with the -driver option to re-run from this point."
  exit 1
fi

cd "${DRIVER}"
# just to be safe, re-build the driver in any case
make clean
make VER=${GVER} SFX=-${GVER}
if [ ! -e "${DRIVER}/gfortran" ]; then
  echo "** ERROR: Darwin driver could not be built."
  exit 1
fi

for i in gcc g++ gfortran; do mv $i ${GPRE}/bin/; mv $i-?.? ${GPRE}/bin/; done

cd ${GPRE}/bin

echo "+-------------------------------------------------------------+"
echo "| sanity check ...                                            |"
echo "+-------------------------------------------------------------+"

cd "${GBB}"
cat > foo.c <<EOF
#include <stdio.h>
int main() {
  printf("OK\n");
  return 0;
}
EOF
for a in i386 x86_64 ppc ppc64; do
rm -f foo
${GPRE}/bin/gcc -arch $a foo.c -o foo -O3
if [ ! -e foo ]; then
  echo "**ERROR: unable to compile C program for $a"
  exit 1
fi
if [ x`./foo` = "xOK" ]; then
  echo " C  $a ... OK"
else
  echo " C  $a ... runtime failure"
fi
done
rm -f foo
${GPRE}/bin/gcc -arch ppc -arch i386 -arch x86_64 -arch ppc64 foo.c -o foo -O3
if [ ! -e foo ]; then
  echo "**ERROR: unable to compile quad-arch C program"
  exit 1
fi
echo " C  quad-arch ... OK"
rm -f foo foo.c
if [ -e ${GPRE}/bin/gfortran ]; then
cat > foo.f <<EOF
      PROGRAM HELLO
      DO 10, I=1,10
         PRINT *,"Hello World"
 10   CONTINUE
      STOP
      END PROGRAM HELLO
EOF
${GPRE}/bin/gfortran  -arch ppc -arch i386 -arch x86_64 -arch ppc64 foo.f -o foo
if [ ! -e foo ]; then
  echo "**ERROR: unable to compile quad-arch Fortran program"
  exit 1
fi
echo " Fortran quad-arch ... OK"
if ./foo|grep Hello >/dev/null; then
  echo " Fortran runtime ... OK"
  rm -f foo foo.f
else
  echo " Fortran runtime ... failed"
  exit 1
fi
fi

if [ -e ${GPRE}/bin/g++ ]; then
cat > foo.cc <<EOF
#include <iostream>

int main()
{
    std::cout << "Hello World!";
    return 0;
}
EOF
${GPRE}/bin/g++  -arch ppc -arch i386 -arch x86_64 -arch ppc64 foo.cc -o foo
if [ ! -e foo ]; then
  echo "**ERROR: unable to compile quad-arch C++ program"
  exit 1
fi
echo " C++ quad-arch ... OK"
if ./foo|grep Hello >/dev/null; then
  echo " C++ runtime ... OK"
  rm -f foo foo.cc
else
  echo " C++ runtime ... failed"
  exit 1
fi
fi

echo "+-------------------------------------------------------------+"
echo "| Done, it looks good!                                        |"
echo "+-------------------------------------------------------------+"

