Skip to content

Instantly share code, notes, and snippets.

@kotarou3
Created July 6, 2014 10:14
Show Gist options
  • Save kotarou3/93227e60418b95bf74ef to your computer and use it in GitHub Desktop.
Save kotarou3/93227e60418b95bf74ef to your computer and use it in GitHub Desktop.
#!/bin/bash
# Please place the appropiate sources in these directories:
# Directory Source Where to get
# --------------------------------------------------------
# m4/src m4 ftp://ftp.gnu.org/gnu/m4/
# gmp/src libgmp ftp://ftp.gmplib.org/pub/
# mpfr/src libmpfr http://www.mpfr.org/mpfr-current/#download
# mpc/src libmpc http://www.multiprecision.org/index.php?prog=mpc&page=download
# binutils-src binutils http://ftp.gnu.org/gnu/binutils/
# gcc-src gcc ftp://ftp.gnu.org/gnu/gcc/
# mingw-w64-headers-src mingw-w64-headers See below
# mingw-w64-crt-src mingw-w64-crt See below
#
# The MinGW-W64 runtime (mingw-w64-crt) and headers (mingw-w64-headers) can be obtained from the MinGW-W64 SVN.
# Downloading them is as simple as running these commands, with the current directory being the directory this
# script is in:
# svn export --force https://mingw-w64.svn.sourceforge.net/svnroot/mingw-w64/trunk/mingw-w64-headers mingw-w64-headers-src
# svn export --force https://mingw-w64.svn.sourceforge.net/svnroot/mingw-w64/trunk/mingw-w64-crt mingw-w64-crt-src
#
# Additionally, please place the appropiate binaries in these directories:
# Directory Binary Where to get
# ----------------------------------------
# flex flex [Windows] http://gnuwin32.sourceforge.net/downlinks/flex-bin-zip.php
# [Linux] Just install it using your package manager!
# Set this to the destination where the finished compiler is installed to. Both relative and absolute paths are okay.
# Typical destinations are `/usr/local', `~/usr' and `/opt/MinGW-W64'
DEST=MinGW-W64
############################
# Convert $DEST into a absolute path if applicable
case $DEST in
/*) ;;
*) DEST=`pwd`/$DEST ;;
esac
if [ -z $TARGET_TRIPLET ] && [ -x .currentSettings ]
then
printf "\nDo you want a fresh build [y/N]? "
read CLEAN
if [ $CLEAN ] && [ $CLEAN = "Y" -o $CLEAN = "y" ]
then
printf "Remove the destination ($DEST) [y/N]? "
read CLEANDEST
if [ $CLEANDEST ] && [ $CLEANDEST = "Y" -o $CLEANDEST = "y" ]
then
rm -rf $DEST
fi
cleanDir()
{
for DIR in `ls $1/`
do
if [ $DIR = "src" ]; then continue; fi
rm -rf $1/$DIR
done
}
cleanDir m4
cleanDir gmp
cleanDir mpfr
cleanDir mpc
unset -f cleanDir
rm -rf binutils-build
rm -rf gcc-build
rm -rf mingw-w64-headers-build
rm -rf mingw-w64-crt-build
rm -rf .currentSettings
else
./.currentSettings
exit # .currentSettings is designed to run this script with the settings set
fi
fi
if [ -z $TARGET_TRIPLET ]
then
# Detect if we are building on Windows
if [ -z $OS ] || [ $OS != "Windows_NT" ]
then
OS=Linux
fi
HOST_ARCH_DEFAULT=`uname -m 2>&-`;
while [ -z $HOST_ARCH ]
do
printf "What is the host platform (Where the compiler will run) architecture [i686/x86_64/etc] (Default: $HOST_ARCH_DEFAULT)? "
read HOST_ARCH
if [ ! $HOST_ARCH ]
then
HOST_ARCH=$HOST_ARCH_DEFAULT
fi
if ! grep -Eq "^(i[3-6]86|x86_64)$" <<< $HOST_ARCH
then
unset HOST_ARCH
echo "Unrecognised architecture. Accepted architectures are:"
echo " i386/i486/i586/i686"
echo " x86_64"
fi
done
if [ $OS = "Windows_NT" ]
then
HOST_OS_DEFAULT="mingw32"
else
HOST_OS_DEFAULT="linux-gnu"
fi
while [ -z $HOST_OS ]
do
printf "What is the host platform (Where the compiler will run) operating system [linux-gnu/mingw32] (Default: $HOST_OS_DEFAULT)? "
read HOST_OS
if [ ! $HOST_OS ]
then
HOST_OS=$HOST_OS_DEFAULT
fi
if [ $HOST_OS != "linux-gnu" ] && [ $HOST_OS != "mingw32" ]
then
unset HOST_OS
echo "Unrecognised operating system. Accepted operating systems are:"
echo " linux-gnu (Linux)"
echo " mingw32 (Windows)"
fi
done
if [ $HOST_OS = "linux-gnu" ]
then
HOST_TRIPLET="$HOST_ARCH-$HOST_OS"
else
HOST_TRIPLET="$HOST_ARCH-w64-$HOST_OS"
fi
if [ $HOST_ARCH = $HOST_ARCH_DEFAULT ] && [ $HOST_OS = $HOST_OS_DEFAULT ]
then
HOST_CONFIGURE_PARAM=""
if ! hash gcc 2>&- # Check if there is a compiler available
then
printf "\nCould not find an available compiler (gcc).\n" 1>&2
exit 1
fi
else
HOST_CONFIGURE_PARAM="--host=$HOST_TRIPLET"
if ! hash $HOST_TRIPLET-gcc 2>&- # Check if there is a cross-compiler available
then
printf "\nCould not find an available cross-compiler ($HOST_TRIPLET-gcc).\n" 1>&2
exit 1
fi
fi
while [ -z $TARGET_ARCH ]
do
printf "What is the target architecture (What the compiler will produce) [i686/x86_64/etc] (Default: `uname -m 2>&-`)? "
read TARGET_ARCH
if [ ! $TARGET_ARCH ]
then
TARGET_ARCH=`uname -m 2>&-`
fi
if ! grep -Eq "^(i[3-6]86|x86_64)$" <<< $TARGET_ARCH
then
unset TARGET_ARCH
echo "Unrecognised architecture. Accepted architectures are:"
echo " i386/i486/i586/i686"
echo " x86_64"
fi
done
TARGET_TRIPLET=$TARGET_ARCH-w64-mingw32
printf "Do you want to link non-prefixed executables to the prefixed ones (Eg. gcc -> $TARGET_TRIPLET-gcc) "
if [ $HOST_TRIPLET = $TARGET_TRIPLET ]
then
printf "[Y/n]? "
else
printf "[y/N]? "
fi
read LINK
if [ $LINK ] && [ $LINK = "Y" -o $LINK = "y" ]
then
LINK="y"
elif [ $LINK ] && [ $LINK = "N" -o $LINK = "n" ]
then
LINK="n"
elif [ $HOST_TRIPLET = $TARGET_TRIPLET ]
then
LINK="y"
else
LINK="n"
fi
printf "\nBuilding MinGW-W64 to \`$DEST'.\n"
printf "Guessed build time: 1 Hour.\n"
printf "Guessed disk usage: 2.5GB.\n"
printf "Host triplet: $HOST_TRIPLET.\n"
printf "Target triplet: $TARGET_TRIPLET.\n"
printf "Linking non-prefixed executables to prefixed? $LINK.\n"
printf "\nDo you want to continue [Y/n]? "
read START
if [ $START ] && [ $START = "N" -o $START = "n" ]
then
printf "\nStop.\n"
exit 1
fi
# Save all settings to disk
printf "#!/bin/bash\nOS=$OS HOST_CONFIGURE_PARAM=$HOST_CONFIGURE_PARAM HOST_TRIPLET=$HOST_TRIPLET TARGET_TRIPLET=$TARGET_TRIPLET LINK=$LINK ./build.sh" > .currentSettings
chmod +x .currentSettings
fi
# Get the number of virtual cores of the host
if [ $OS = "Windows_NT" ]
then
PARAMAKES=$NUMBER_OF_PROCESSORS
else
PARAMAKES=`cat /proc/cpuinfo | grep processor | wc -l`
fi
let PARAMAKES++ # Optimal number of parallel makes is No. of cores + 1
# When the host triplet is equal to the target triplet, triplet subdirectories aren't made
if [ $HOST_TRIPLET = $TARGET_TRIPLET ]
then
DEST_WITH_TRIPLET="$DEST"
else
DEST_WITH_TRIPLET="$DEST/$TARGET_TRIPLET"
fi
# Building Binutils and GCC with the host OS as Windows require /mingw/include to exist. Additionally, building GCC on Windows
# requires /mingw to contain the current GCC installation
if [ $OS = "Windows_NT" ]
then
GCCPATH=`printf "$(which gcc)" | sed "s/bin\/gcc[.a-z]*$//" | sed "s/^\/\([a-zA-Z]\)\//\1:\//"`
mount --replace "$GCCPATH" /mingw || { printf "\n\nFailed to mount the current GCC installation to /mingw\n" 1>&2; exit 1; }
unset GCCPATH
fi
if [ `sed "s/[a-zA-Z0-9_]*-//g" <<< $HOST_TRIPLET` = "mingw32" ] && [ ! -d /mingw/include ]
then
if [ `id -u` -ne 0 ]
then
printf "\nBuilding Binutils and GCC with the host OS as Windows requires /mingw/include/\n"
printf "to exist for some arcane reason, even if empty. To create the directory, I need\n"
printf "to be root, and I will remove it the directory after the script completes.\n"
for (( TRIES=0; TRIES < 3; ++TRIES ))
do
if [ $TRIES -gt 0 ]
then
printf " Incorrect password. Please try again.\n"
fi
printf " Password for `whoami`: " 1>&2
read -rs CURUSERPASS
echo
echo $CURUSERPASS | sudo -S mkdir -p /mingw/include 2>&-
if [ $? -eq 0 ]
then
break
fi
done
if [ $TRIES -eq 3 ]
then
printf "Too many incorrect password attempts. Cannot continue.\n"
exit 1
fi
else
mkdir -p /mingw/include
fi
MADEROOTMINGWINCLUDE=true
fi
# Check if flex exists
if ! hash flex 2>&-
then
if [ $OS = "Windows_NT" ]
then
if [ ! -x "`pwd`/flex/bin/flex.exe" ]
then
printf "\n\nCould not locate flex\n" 1>&2
exit 1
else
PATH=$PATH:"`pwd`/flex/bin"
fi
else
printf "\n\nCould not locate flex\n" 1>&2
exit 1
fi
fi
# Check if m4 exists, and build it if it does not
if ! hash m4 2>&-
then
M4DIR=`pwd`/m4
if [ ! -x "$M4DIR/bin/m4" ] && [ ! -x "$M4DIR/bin/m4.exe" ] # Check if already built
then
cd m4
mkdir -p build
cd build
../src/configure --prefix="$M4DIR"
make -j$PARAMAKES || { printf "\n\nFailed to build m4\n" 1>&2; exit 1; }
make install || { printf "\n\nFailed to build m4\n" 1>&2; exit 1; }
cd ../..
fi
PATH=$PATH:$M4DIR/bin
unset M4DIR
fi
# Build libgmp
GMPDIR=`pwd`/gmp
if [ ! -r "$GMPDIR/lib/libgmp.a" ] # Check if already built
then
cd gmp
mkdir -p build
cd build
if [ ! -r "Makefile" ] # Check if already configured
then
../src/configure $HOST_CONFIGURE_PARAM --prefix="$GMPDIR" --disable-shared
fi
TRIES=0
while [ $TRIES -lt 2 ] # Building libgmp has the tendancy to fail the first time, but succeed the second without any changes
do
# libgmp seems to conflict with libkernel32.a when compiling test applications under Linux for Windows
if [ `sed "s/[a-zA-Z0-9_]*-//g" <<< $HOST_TRIPLET` = "mingw32" ] && [ $OS = "Linux" ]
then
make -j$PARAMAKES
else
make all check -j$PARAMAKES
fi
if [ $? = 0 ]; then break; fi
let TRIES++
done
if [ $TRIES -ge 2 ]
then
printf "\n\nFailed to build libgmp\n"
exit 1
fi
unset TRIES
make install || { printf "\n\nFailed to build libgmp\n" 1>&2; exit 1; }
cd ../..
fi
# Build libmpfr
MPFRDIR=`pwd`/mpfr
if [ ! -r "$MPFRDIR/lib/libmpfr.a" ] # Check if already built
then
cd mpfr
mkdir -p build
cd build
if [ ! -r "Makefile" ] # Check if already configured
then
../src/configure $HOST_CONFIGURE_PARAM --prefix="$MPFRDIR" --disable-shared --with-gmp="$GMPDIR"
fi
make all install || { printf "\n\nFailed to build libmpfr\n" 1>&2; exit 1; } # -j is left out here because build fails with it on
cd ../..
fi
# Build libmpc
MPCDIR=`pwd`/mpc
if [ ! -r "$MPCDIR/lib/libmpc.a" ] # Check if already built
then
cd mpc
mkdir -p build
cd build
if [ ! -r "Makefile" ] # Check if already configured
then
../src/configure $HOST_CONFIGURE_PARAM --prefix="$MPCDIR" --disable-shared --with-gmp="$GMPDIR" --with-mpfr="$MPFRDIR"
fi
make all install || { printf "\n\nFailed to build libmpc\n" 1>&2; exit 1; } # -j is left out here because build fails with it on
cd ../..
fi
# Build binutils
if [ ! -x "$DEST/bin/$TARGET_TRIPLET-ld" ] && [ ! -x "$DEST/bin/$TARGET_TRIPLET-ld.exe" ] # Check if already built
then
mkdir -p binutils-build
cd binutils-build
if [ ! -r "Makefile" ] # Check if already configured
then
../binutils-src/configure $HOST_CONFIGURE_PARAM --target=$TARGET_TRIPLET --prefix="$DEST" --with-gmp="$GMPDIR" --with-mpfr="$MPFRDIR" --with-mpc="$MPCDIR" --disable-multilib # Can't seem to get a cross compiler with multilib
fi
make profiledbootstrap || make -j$PARAMAKES || { printf "\n\nFailed to build binutils\n" 1>&2; exit 1; } # Try to bootstrap first. If it fails, do a normal build
make install || { printf "\n\nFailed to build binutils\n" 1>&2; exit 1; }
cd ..
# Binutils names its executables without the target triplet prepended when $HOST_TRIPLET = $TARGET_TRIPLET so we move it so it does
if [ $HOST_TRIPLET = $TARGET_TRIPLET ]
then
pushd $DEST/bin > /dev/null
# $HOST_TRIPLET = $TARGET_TRIPLET will only happen when the host OS is Windows so we don't need to check for OS
prependTargetTriplet()
{
mv "$1.exe" "$TARGET_TRIPLET-$1.exe"
}
prependTargetTriplet "addr2line"
prependTargetTriplet "ar"
prependTargetTriplet "as"
prependTargetTriplet "c++filt"
prependTargetTriplet "dlltool"
prependTargetTriplet "dllwrap"
prependTargetTriplet "elfedit"
prependTargetTriplet "gprof"
prependTargetTriplet "ld.bfd"
prependTargetTriplet "ld"
prependTargetTriplet "nm"
prependTargetTriplet "objcopy"
prependTargetTriplet "objdump"
prependTargetTriplet "ranlib"
prependTargetTriplet "readelf"
prependTargetTriplet "size"
prependTargetTriplet "strings"
prependTargetTriplet "strip"
prependTargetTriplet "windmc"
prependTargetTriplet "windres"
popd > /dev/null
fi
fi
# Build mingw-w64-headers
if [ ! -r "$DEST_WITH_TRIPLET/include/stdio.h" ] # Check if already built
then
mkdir -p mingw-w64-headers-build
cd mingw-w64-headers-build
if [ ! -r "Makefile" ] # Check if already configured
then
../mingw-w64-headers-src/configure --host=$TARGET_TRIPLET --target=$TARGET_TRIPLET --prefix="$DEST_WITH_TRIPLET" --enable-sdk=all
fi
make -j$PARAMAKES || { printf "\n\nFailed to build mingw-w64-headers\n" 1>&2; exit 1; }
make install || { printf "\n\nFailed to build mingw-w64-headers\n" 1>&2; exit 1; }
cd ..
fi
# Build minimal gcc
if [ ! -x "$DEST/bin/$TARGET_TRIPLET-gcc" ] && [ ! -x "$DEST/bin/$TARGET_TRIPLET-gcc.exe" ] # Check if already built
then
mkdir -p gcc-build
cd gcc-build
if [ ! -r "Makefile" ] # Check if already configured
then
../gcc-src/configure $HOST_CONFIGURE_PARAM --target=$TARGET_TRIPLET --prefix="$DEST" --with-gmp="$GMPDIR" --with-mpfr="$MPFRDIR" --with-mpc="$MPCDIR" --enable-languages=c,c++ --disable-win32-registry --enable-checking=release --enable-shared --disable-multilib # Can't seem to get a cross compiler with multilib
fi
make all-gcc -j$PARAMAKES || { printf "\n\nFailed to build minimal gcc\n" 1>&2; exit 1; }
make install-gcc || { printf "\n\nFailed to build minimal gcc\n" 1>&2; exit 1; }
cd ..
fi
# Use the newly built gcc for the rest of this script
export PATH="$DEST/bin":$PATH
# Build mingw-w64-crt
if [ ! -r "$DEST_WITH_TRIPLET/lib/libkernel32.a" ] # Check if already built
then
mkdir -p mingw-w64-crt-build
cd mingw-w64-crt-build
if [ ! -r "Makefile" ] # Check if already configured
then
../mingw-w64-crt-src/configure --host=$TARGET_TRIPLET --target=$TARGET_TRIPLET --prefix="$DEST_WITH_TRIPLET" #--enable-lib32 --enable-lib64
fi
make -j$PARAMAKES || { printf "\n\nFailed to build mingw-w64-crt\n" 1>&2; exit 1; }
make install || { printf "\n\nFailed to build mingw-w64-crt\n" 1>&2; exit 1; }
cd ..
fi
# Build full gcc
if [ ! -r "$DEST_WITH_TRIPLET/lib/libstdc++.a" ] # Check if already built
then
cd gcc-build
make all all-target -j$PARAMAKES || { printf "\n\nFailed to build gcc\n" 1>&2; exit 1; }
make profiledbootstrap
make install || { printf "\n\nFailed to build gcc\n" 1>&2; exit 1; }
cd ..
fi
printf "\n\nMinGW-W64 built successfully!\n" 1>&2
if [ $MADEROOTMINGWINCLUDE ]
then
if [ `id -u` -ne 0 ]
then
echo $CURUSERPASS | sudo -S rmdir -p /mingw/include 2>&-
else
rmdir -p /mingw/include
fi
fi
if [ $LINK = "n" ]
then
exit 0
fi
pushd $DEST/bin > /dev/null
for BIN in `ls`
do
grep -Eq "^$TARGET_TRIPLET-" <<< $BIN || continue
rm -rf `echo $BIN | sed "s/^$TARGET_TRIPLET-//"`
ln $BIN `echo $BIN | sed "s/^$TARGET_TRIPLET-//"`
done
popd > /dev/null
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment