#!/bin/sh
#
# Copyright (C) 2012 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Rebuild the mingw64 cross-toolchain from scratch
#
PROGNAME=$(basename $0)
HELP=
VERBOSE=1
# This will be reset later.
LOG_FILE=/dev/null
panic ()
{
1>&2 echo "Error: $@"
exit 1
}
fail_panic ()
{
if [ $? != 0 ]; then
panic "$@"
fi
}
var_value ()
{
eval echo \"$1\"
}
var_append ()
{
local _varname=$1
local _varval=$(var_value $_varname)
shift
if [ -z "$_varval" ]; then
eval $_varname=\"$*\"
else
eval $_varname=\$$_varname\" $*\"
fi
}
run ()
{
if [ "$VERBOSE" -gt 0 ]; then
echo "COMMAND: >>>> $@" > $LOG_FILE
fi
if [ "$VERBOSE" -gt 1 ]; then
echo "COMMAND: >>>> $@"
fi
if [ "$VERBOSE" -gt 1 ]; then
"$@"
else
&>$LOG_FILE "$@"
fi
}
log ()
{
if [ "$LOG_FILE" ]; then
echo "$@" > $LOG_FILE
fi
if [ "$VERBOSE" -gt 0 ]; then
echo "$@"
fi
}
# For now, only tested on Linux
OS=$(uname -s)
EXEEXT= # executable extension
case $OS in
Linux) OS=linux;;
Darwin) OS=darwin;;
CYGWIN*|*_NT-*) OS=windows;
if [ "$OSTYPE" = cygwgin ]; then
OS=cygwin
fi
EXEEXT=.exe
;;
esac
ARCH=$(uname -m)
case $ARCH in
i?86) ARCH=i686;;
amd64) ARCH=x86_64;;
esac
case $OS in
linux)
NUM_CORES=$(grep -c -e '^processor' /proc/cpuinfo)
;;
darwin|freebsd)
NUM_CORES=`sysctl -n hw.ncpu`
;;
windows|cygwin)
NUM_CORES=$NUMBER_OF_PROCESSORS
;;
*) # let's play safe here
NUM_CORES=1
;;
esac
# Warn our users, because the script probably fails on anything but Linux
# at that point (e.g. there are strange libtool build breakages on darwin).
if [ "$OS" != "linux" ]; then
echo "WARNING: WARNING: WARNING: THIS SCRIPT PROBABLY ONLY WORKS ON LINUX!!"
fi
GMP_VERSION=5.0.4
MPFR_VERSION=3.1.0
MPC_VERSION=0.8.2
BINUTILS_VERSION=2.22
GCC_VERSION=4.6.3
MINGW_W64_VERSION=v2.0.2
JOBS=$(( $NUM_CORES * 2 ))
HOST_BINPREFIX=
TARGET_ARCH=x86_64
TARGET_MULTILIBS=true # not empty to enable multilib
PACKAGE_DIR=
FORCE_ALL=
FORCE_BUILD=
CLEANUP=
TEMP_DIR=/tmp/build-mingw64-toolchain-$USER
for opt; do
optarg=`expr "x$opt" : 'x[^=]*=\(.*\)'`
case $opt in
-h|-?|--help) HELP=true;;
--verbose) VERBOSE=$(( $VERBOSE + 1 ));;
--quiet) VERBOSE=$(( $VERBOSE - 1 ));;
--binprefix=*) HOST_BINPREFIX=$optarg;;
-j*|--jobjs=*) JOBS=$optarg;;
--target-arch=*) TARGET_ARCH=$optarg;;
--no-multilib) TARGET_MULTILIBS="";;
--force-build) FORCE_BUILD=true;;
--force-all) FORCE_ALL=true;;
--work-dir=*) TEMP_DIR=$optarg;;
--package-dir=*) PACKAGE_DIR=$optarg;;
--cleanup) CLEANUP=true;;
--gcc-version=*) GCC_VERSION=$optarg;;
--binutils-version=*) BINUTILS_VERSION=$optarg;;
--gmp-version=*) GMP_VERSION=$optarg;;
--mpfr-version=*) MPFR_VERSION=$optarg;;
--mpc-version=*) MPC_VERSION=$optarg;;
--mingw-version=*) MINGW_W64_VERSION=$optarg;;
-*) panic "Unknown option '$opt', see --help for list of valid ones.";;
*) panic "This script doesn't take any parameter, see --help for details.";;
esac
done
if [ "$HELP" ]; then
echo "Usage: $PROGNAME [options]"
echo ""
echo "This program is used to rebuild a mingw64 cross-toolchain from scratch."
echo ""
echo "Valid options:"
echo " -h|-?|--help Print this message."
echo " --verbose Increase verbosity."
echo " --quiet Decrease verbosity."
echo " --gcc-version=<version> Select gcc version [$GCC_VERSION]."
echo " --binutil-version=<version> Select binutils version [$BINUTILS_VERSION]."
echo " --gmp-version=<version> Select libgmp version [$GMP_VERSION]."
echo " --mpfr-version=<version> Select libmpfr version [$MPFR_VERSION]."
echo " --mpc-version=<version> Select libmpc version [$MPC_VERSION]."
echo " --mingw-version=<version> Select mingw-w64 version [$MINGW_W64_VERSION]."
echo " --jobs=<num> Run <num> build tasks in parallel [$JOBS]."
echo " -j<num> Same as --jobs=<num>."
echo " --binprefix=<prefix> Specify bin prefix for host toolchain."
echo " --no-multilib Disable multilib toolchain build."
echo " --arch=<arch> Select default target architecture [$TARGET_ARCH]."
echo " --force-all Redo everything from scratch."
echo " --force-build Force a rebuild (keep sources)."
echo " --cleanup Remove all temp files after build."
echo " --work-dir=<path> Specify work/build directory [$TEMP_DIR]."
echo " --package-dir=<path> Package toolchain to directory."
echo ""
exit 0
fi
if [ "$CLEANUP" ]; then
if [ -z "$PACKAGE_DIR" ]; then
panic "You should only use --cleanup with --package-dir=<path> !".
fi
fi
BUILD_TAG64=x86_64-linux-gnu
BUILD_TAG32=i686-linux-gnu
# We don't want debug executables
BUILD_CFLAGS="-O2 -fomit-frame-pointer -s"
BUILD_LDFLAGS=""
# On Darwin, we want to use the 10.4 / 10.5 / 10.6 SDKs to generate binaries
# that work on "old" platform releases.
if [ "$OS" = darwin ]; then
# Use the check for the availability of a compatibility SDK in Darwin
# this can be used to generate binaries compatible with either Tiger or
# Leopard.
#
# $1: SDK root path
# $2: MacOS X minimum version (e.g. 10.4)
check_darwin_sdk ()
{
if [ -d "$1" ] ; then
var_append BUILD_CFLAGS "-isysroot $1 -mmacosx-version-min=$2 -DMAXOSX_DEPLOYEMENT_TARGET=$2"
var_append BUILD_LDFLAGS "-Wl,-syslibroot,$sdk -mmacosx-version-min=$2"
return 0 # success
fi
return 1
}
if check_darwin_sdk /Developer/SDKs/MacOSX10.4.sdku 10.4; then
log "Generating Tiger-compatible binaries!"
elif check_darwin_sdk /Developer/SDKs/MacOSX10.5.sdk 10.5; then
log "Generating Leopard-compatible binaries!"
elif check_darwin_sdk /Developer/SDKs/MacOSX10.6.sdk 10.6; then
log "Generating Snow Leopard-compatible binaries!"
else
osx_version=`sw_vers -productVersion`
log "Generating $osx_version-compatible binaries!"
fi
fi
mkdir -p $TEMP_DIR
if [ "$FORCE_ALL" ]; then
log "Cleaning up work directory..."
rm -rf $TEMP_DIR/*
fi
LOG_FILE=$TEMP_DIR/build.log
rm -f $LOG_FILE && touch $LOG_FILE
if [ "$VERBOSE" -eq 1 ]; then
echo "To follow build, use in another terminal: tail -F $LOG_FILE"
fi
case $TARGET_ARCH in
x86_64) TARGET_BITS=64;;
i686) TARGET_BITS=32;;
*) panic "Invalid --target parameter. Valid values are: x86_64 i686";;
esac
TARGET_TAG=$TARGET_ARCH-w64-mingw32
log "Target arch: $TARGET_TAG"
log "Target bits: $TARGET_BITS"
# Determine bitness of host architecture
PROBE_CC=${CC:-gcc}
if [ "$HOST_BINPREFIX" ]; then
PROBE_CC=$HOST_BINPREFIX-gcc
fi
echo "int main() { return 0; }" > $TEMP_DIR/test-host-cc.c
$PROBE_CC -c $TEMP_DIR/test-host-cc.c -o $TEMP_DIR/test-host-cc.o > /dev/null
fail_panic "Host compiler doesn't work: $PROBE_CC"
file $TEMP_DIR/test-host-cc.o | grep -q -e "x86[_-]64"
if [ $? != 0 ]; then
log "Host compiler generates 32-bit code: $PROBE_CC"
HOST_ARCH=i686
HOST_BITS=32
else
log "Host compiler generates 64-bit code: $PROBE_CC"
HOST_ARCH=x86_64
HOST_BITS=64
fi
case $OS in
linux) HOST_TAG=$HOST_ARCH-linux-gnu;;
darwin) HOST_TAG=$HOST_ARCH-apple-darwinx11;;
cygwin) HOST_TAG=$HOST_ARCH-pc-cygwin;;
*) panic "Unsupported host operating system!"
esac
log "Host arch: $HOST_TAG"
download_package ()
{
# Assume the packages are already downloaded under $ARCHIVE_DIR
local PKG_URL=$1
local PKG_NAME=$(basename $PKG_URL)
case $PKG_NAME in
*.tar.bz2)
PKG_BASENAME=${PKG_NAME%%.tar.bz2}
;;
*.tar.gz)
PKG_BASENAME=${PKG_NAME%%.tar.gz}
;;
*)
panic "Unknown archive type: $PKG_NAME"
esac
if [ ! -f "$ARCHIVE_DIR/$PKG_NAME" ]; then
log "Downloading $PKG_URL..."
(cd $ARCHIVE_DIR && run curl -L -o "$PKG_NAME" "$PKG_URL")
fail_panic "Can't download '$PKG_URL'"
fi
MD5SUM=$(md5sum $ARCHIVE_DIR/$PKG_NAME | cut -d" " -f1)
echo "$MD5SUM $PKG_URL" >> $INSTALL_DIR/README
if [ ! -d "$SRC_DIR/$PKG_BASENAME" ]; then
log "Uncompressing $PKG_URL into $SRC_DIR"
case $PKG_NAME in
*.tar.bz2)
run tar xjf $ARCHIVE_DIR/$PKG_NAME -C $SRC_DIR
;;
*.tar.gz)
run tar xzf $ARCHIVE_DIR/$PKG_NAME -C $SRC_DIR
;;
*)
panic "Unknown archive type: $PKG_NAME"
;;
esac
fail_panic "Can't uncompress $ARCHIVE_DIR/$PKG_NAME"
fi
}
# Download and unpack source packages from official sites
ARCHIVE_DIR=$TEMP_DIR/archive
SRC_DIR=$TEMP_DIR/src
STAMP_DIR=$TEMP_DIR/timestamps
mkdir -p $ARCHIVE_DIR
mkdir -p $SRC_DIR
mkdir -p $STAMP_DIR
if [ "$FORCE_BUILD" ]; then
rm -f $STAMP_DIR/*
rm -rf $INSTALL_DIR
rm -rf $BUILD_DIR
fi
# Make temp install directory
INSTALL_DIR=$TEMP_DIR/install-$HOST_TAG/x86_64-w64-mingw32
BUILD_DIR=$TEMP_DIR/build-$HOST_TAG
mkdir -p $INSTALL_DIR
mkdir -p $BUILD_DIR
# Copy this script
cp $0 $INSTALL_DIR/ &&
echo "This file has been automatically generated on $(date) with the following command:" > $INSTALL_DIR/README &&
echo "$PROGNAME $@" >> $INSTALL_DIR/README &&
echo "" >> $INSTALL_DIR/README &&
echo "The MD5 hashes for the original sources packages are:" >> $INSTALL_DIR/README
fail_panic "Could not copy script to installation directory."
download_package http://ftp.gnu.org/gnu/gmp/gmp-$GMP_VERSION.tar.bz2
download_package http://ftp.gnu.org/gnu/mpfr/mpfr-$MPFR_VERSION.tar.bz2
download_package http://www.multiprecision.org/mpc/download/mpc-$MPC_VERSION.tar.gz
download_package http://ftp.gnu.org/gnu/binutils/binutils-$BINUTILS_VERSION.tar.bz2
download_package http://ftp.gnu.org/gnu/gcc/gcc-$GCC_VERSION/gcc-$GCC_VERSION.tar.bz2
download_package http://downloads.sourceforge.net/project/mingw-w64/mingw-w64/mingw-w64-release/mingw-w64-$MINGW_W64_VERSION.tar.gz
# Let's generate the licenses/ directory
LICENSES_DIR=$INSTALL_DIR/licenses/
mkdir -p $LICENSES_DIR
if [ ! -f $STAMP_DIR/licenses ]; then
LICENSE_FILES=$(cd $SRC_DIR && find . -name "COPYING*")
# Copy all license files to $LICENSES_DIR
(tar cf - -C $SRC_DIR $LICENSE_FILES) | (tar xf - -C $LICENSES_DIR)
touch $STAMP_DIR/licenses
fi
setup_build_env ()
{
local BINPREFIX=$1
if [ "$BINPREFIX" ]; then
CC=$BINPREFIX-gcc
CXX=$BINPREFIX-g++
LD=$BINPREFIX-ld
AS=$BINPREFIX-as
AR=$BINPREFIX-ar
RANLIB=$BINPREFIX-ranlib
STRIP=$BINPREFIX-strip
export CC CXX LD AS AR RANLIB STRIP
elif [ "$OS" = darwin ]; then
# Needed on OS X otherwise libtool will try to use gcc and $BUILD_CFLAGS
LD=ld
fi
export CFLAGS="$BUILD_CFLAGS"
export CXXFLAGS="$BUILD_CFLAGS"
export LDFLAGS="$BUILD_LDFLAGS"
}
setup_install_env ()
{
export PATH=$INSTALL_DIR/bin:$PATH
}
build_host_package ()
{
local PKGNAME=$1
shift
if [ ! -f $STAMP_DIR/$PKGNAME ]; then
(
mkdir -p $BUILD_DIR/$PKGNAME &&
cd $BUILD_DIR/$PKGNAME &&
setup_build_env $HOST_BINPREFIX &&
log "$PKGNAME: Configuring" &&
run $SRC_DIR/$PKGNAME/configure "$@"
fail_panic "Can't configure $PKGNAME !!"
log "$PKGNAME: Building" &&
run make -j$JOBS
fail_panic "Can't build $PKGNAME !!"
log "$PKGNAME: Installing" &&
run make install
fail_panic "Can't install $PKGNAME"
) || exit 1
touch $STAMP_DIR/$PKGNAME
fi
}
export ABI=$HOST_BITS
BASE_HOST_OPTIONS="--prefix=$INSTALL_DIR --disable-shared"
build_host_package gmp-$GMP_VERSION $BASE_HOST_OPTIONS
var_append BASE_HOST_OPTIONS "--with-gmp=$INSTALL_DIR"
build_host_package mpfr-$MPFR_VERSION $BASE_HOST_OPTIONS
var_append BASE_HOST_OPTIONS "--with-mpfr=$INSTALL_DIR"
build_host_package mpc-$MPC_VERSION $BASE_HOST_OPTIONS
var_append BASE_HOST_OPTIONS "--with-mpc=$INSTALL_DIR"
BINUTILS_CONFIGURE_OPTIONS=$BASE_HOST_OPTIONS
var_append BINUTILS_CONFIGURE_OPTIONS "--target=$TARGET_TAG --disable-nls"
if [ "$TARGET_MULTILIBS" ]; then
var_append BINUTILS_CONFIGURE_OPTIONS "--enable-targets=x86_64-w64-mingw32,i686-w64-mingw32"
fi
var_append BINUTILS_CONFIGURE_OPTIONS "--with-sysroot=$INSTALL_DIR"
build_host_package binutils-$BINUTILS_VERSION $BINUTILS_CONFIGURE_OPTIONS
# Install the right mingw64 headers into the sysroot
build_mingw_headers ()
{
local PKGNAME=$1
if [ ! -f "$STAMP_DIR/$PKGNAME" ]; then
(
mkdir -p $BUILD_DIR/$PKGNAME &&
cd $BUILD_DIR/$PKGNAME &&
log "$PKGNAME: Configuring" &&
run $SRC_DIR/mingw-w64-$MINGW_W64_VERSION/mingw-w64-headers/configure --prefix=$INSTALL_DIR --host=$TARGET_TAG --build=$HOST_TAG
fail_panic "Can't configure mingw-64-headers"
log "$PKGNAME: Installing" &&
run make install -j$JOBS &&
run cd $INSTALL_DIR &&
run ln -s $TARGET_TAG mingw &&
run cd $INSTALL_DIR/mingw &&
run ln -s lib lib$TARGET_BITS
fail_panic "Can't configure mingw-64-headers"
) || exit 1
touch $STAMP_DIR/$PKGNAME
fi
}
# Slightly different from build_host_package because we need to call
# 'make all-gcc' and 'make install-gcc' as a special case.
#
build_core_gcc ()
{
local PKGNAME=$1
shift
if [ ! -f "$STAMP_DIR/core-$PKGNAME" ]; then
(
mkdir -p $BUILD_DIR/$PKGNAME &&
cd $BUILD_DIR/$PKGNAME &&
setup_build_env $HOST_BINPREFIX &&
log "core-$PKGNAME: Configuring" &&
run $SRC_DIR/$PKGNAME/configure "$@"
fail_panic "Can't configure $PKGNAME !!"
log "core-$PKGNAME: Building" &&
run make -j$JOBS all-gcc
fail_panic "Can't build $PKGNAME !!"
log "core-$PKGNAME: Installing" &&
run make -j$JOBS install-gcc
fail_panic "Can't install $PKGNAME"
) || exit 1
touch $STAMP_DIR/core-$PKGNAME
fi
}
# Build and install the C runtime files needed by the toolchain
build_mingw_crt ()
{
local PKGNAME=$1
shift
if [ ! -f $STAMP_DIR/$PKGNAME ]; then
(
mkdir -p $BUILD_DIR/$PKGNAME &&
cd $BUILD_DIR/$PKGNAME &&
export PATH=$INSTALL_DIR/bin:$PATH
log "$PKGNAME: Configuring" &&
run $SRC_DIR/mingw-w64-$MINGW_W64_VERSION/mingw-w64-crt/configure "$@"
fail_panic "Can't configure $PKGNAME !!"
log "$PKGNAME: Building" &&
run make -j$JOBS
fail_panic "Can't build $PKGNAME !!"
log "$PKGNAME: Installing" &&
run make -j$JOBS install
fail_panic "Can't install $PKGNAME"
) || exit 1
touch $STAMP_DIR/$PKGNAME
fi
}
build_libgcc ()
{
local PKGNAME=$1
shift
if [ ! -f "$STAMP_DIR/libgcc-$PKGNAME" ]; then
(
# No configure step here! We're resuming work that was started
# in build_core_gcc.
cd $BUILD_DIR/$PKGNAME &&
setup_build_env $HOST_BINPREFIX &&
log "libgcc-$PKGNAME: Building" &&
run make -j$JOBS
fail_panic "Can't build libgcc-$PKGNAME !!"
log "libgcc-$PKGNAME: Installing" &&
run make -j$JOBS install
fail_panic "Can't install libgcc-$PKGNAME"
) || exit 1
touch "$STAMP_DIR/libgcc-$PKGNAME"
fi
}
GCC_CONFIGURE_OPTIONS=$BASE_HOST_OPTIONS
var_append GCC_CONFIGURE_OPTIONS "--target=$TARGET_TAG"
if [ "$TARGET_MULTILIBS" ]; then
var_append GCC_CONFIGURE_OPTIONS "--enable-targets=all"
fi
var_append GCC_CONFIGURE_OPTIONS "--enable-languages=c,c++"
var_append GCC_CONFIGURE_OPTIONS "--with-sysroot=$INSTALL_DIR"
build_mingw_headers mingw-w64-headers
build_core_gcc gcc-$GCC_VERSION $GCC_CONFIGURE_OPTIONS
CRT_CONFIGURE_OPTIONS="--host=$TARGET_TAG --with-sysroot=$INSTALL_DIR --prefix=$INSTALL_DIR"
if [ "$TARGET_MULTILIBS" ]; then
var_append CRT_CONFIGURE_OPTIONS "--enable-lib32"
fi
build_mingw_crt mingw-w64-crt $CRT_CONFIGURE_OPTIONS
build_libgcc gcc-$GCC_VERSION
if [ "$PACKAGE_DIR" ]; then
mkdir -p $PACKAGE_DIR
fail_panic "Could not create packaging directory: $PACKAGE_DIR"
PACKAGE_NAME=$PACKAGE_DIR/$TARGET_TAG-$OS-$HOST_ARCH.tar.bz2
log "Packaging $TARGET_TAG toolchain to $PACKAGE_NAME"
run tar cjf $PACKAGE_NAME -C $(dirname $INSTALL_DIR) $TARGET_TAG/
fail_panic "Could not package $TARGET_TAG toolchain!"
log "Done. See $PACKAGE_DIR:"
ls -l $PACKAGE_NAME
else
log "Done. See: $INSTALL_DIR"
fi
if [ "$CLEANUP" ]; then
log "Cleaning up..."
rm -rf $TEMP_DIR/*
fi
exit 0