Created
July 6, 2014 10:14
-
-
Save kotarou3/93227e60418b95bf74ef to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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