#!/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