#!/bin/sh # # Copyright (C) 2016 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 # # See --help for usage example. PROGNAME=$(basename $0) PROGDIR=$(dirname $0) PROGDIR=$(cd $PROGDIR && pwd) ANDROID_BUILD_TOP=$(realpath $PROGDIR/../..) TOOLCHAIN_DIR=$(realpath $ANDROID_BUILD_TOP/toolchain) HELP= VERBOSE=2 # 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 "$@" > /dev/null 2>&1 fi } log () { if [ "$LOG_FILE" ]; then echo "$@" >> $LOG_FILE fi if [ "$VERBOSE" -gt 0 ]; then echo "$@" fi } NUM_CORES=$(grep -c -e '^processor' /proc/cpuinfo) JOBS=$(( $NUM_CORES * 2 )) GMP_VERSION=5.0.5 MPFR_VERSION=3.1.1 MPC_VERSION=1.0.1 BINUTILS_VERSION=2.27 GCC_VERSION=4.8.3 MINGW_W64_VERSION=v6.x TARGET_ARCH=x86_64 TARGET_MULTILIBS=true # not empty to enable multilib CLEANUP= OPT_GCC_VERSION= for opt; do optarg=`expr "x$opt" : 'x[^=]*=\(.*\)'` case $opt in -h|-?|--help) HELP=true;; --verbose) VERBOSE=$(( $VERBOSE + 1 ));; --quiet) VERBOSE=$(( $VERBOSE - 1 ));; -j*|--jobs=*) JOBS=$optarg;; --target-arch=*) TARGET_ARCH=$optarg;; --no-multilib) TARGET_MULTILIBS="";; --gcc-version=*) OPT_GCC_VERSION=$optarg;; --cleanup) CLEANUP=true;; -*) 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 cat < Run build tasks in parallel [$JOBS]." -j Same as --jobs=." --no-multilib Disable multilib toolchain build." --target-arch= Select default target architecture [$TARGET_ARCH]." --gcc-version= Select alternative GCC version [$GCC_VERSION]" EOF exit 0 fi if [ "$OPT_GCC_VERSION" ]; then GCC_VERSION=$OPT_GCC_VERSION fi GCC_SRC_DIR=$TOOLCHAIN_DIR/gcc/gcc-$GCC_VERSION if [ ! -d "$GCC_SRC_DIR" ]; then panic "Missing GCC source directory: $GCC_SRC_DIR" fi GCC_MAJOR_MINOR=$(echo "$GCC_VERSION" | cut -d. -f1-2) # Top level out directory. OUT_DIR=$ANDROID_BUILD_TOP/out # Name of the directory inside the package. PACKAGE_DIR=x86_64-w64-mingw32-$GCC_MAJOR_MINOR # Directory to isolate the install package from any similarly named directories. OUTER_INSTALL_DIR=$OUT_DIR/install # Actual install path. INSTALL_DIR=$OUTER_INSTALL_DIR/$PACKAGE_DIR # Install directory for build dependencies that are not in the final package # (gmp and whatnot). SUPPORT_DIR=$INSTALL_DIR # For the final artifacts. Will be archived on the build server. if [ -z "$DIST_DIR" ]; then DIST_DIR=$OUT_DIR/dist 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="" rm -rf $OUT_DIR mkdir -p $OUT_DIR mkdir -p $INSTALL_DIR mkdir -p $DIST_DIR mkdir -p $SUPPORT_DIR LOG_FILE=$OUT_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" HOST_ARCH=x86_64 HOST_BITS=64 HOST_TAG=$HOST_ARCH-linux-gnu log "Host arch: $HOST_TAG" # 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." PREFIX_FOR_TARGET=$INSTALL_DIR/$TARGET_TAG WITH_WIDL=$INSTALL_DIR/bin MINGW_W64_SRC=$TOOLCHAIN_DIR/mingw/mingw-w64-$MINGW_W64_VERSION setup_host_build_env () { local BINPREFIX=$ANDROID_BUILD_TOP/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/bin/x86_64-linux- 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 export CFLAGS="$BUILD_CFLAGS" export CXXFLAGS="$BUILD_CFLAGS" export LDFLAGS="$BUILD_LDFLAGS" } setup_mingw_build_env () { local BINPREFIX=$INSTALL_DIR/bin/x86_64-w64-mingw32- CC=${BINPREFIX}gcc CXX=${BINPREFIX}g++ LD=${BINPREFIX}ld AS=${BINPREFIX}as AR=${BINPREFIX}ar RANLIB=${BINPREFIX}ranlib RC=${BINPREFIX}windres STRIP=${BINPREFIX}strip export CC CXX LD AS AR RANLIB RC STRIP } setup_install_env () { export PATH=$INSTALL_DIR/bin:$PATH } build_binutils_package () { local PKGNAME=$1 shift ( mkdir -p $OUT_DIR/$PKGNAME && cd $OUT_DIR/$PKGNAME && setup_host_build_env && log "$PKGNAME: Configuring" && run $TOOLCHAIN_DIR/binutils/$PKGNAME/configure "$@" fail_panic "Can't configure $PKGNAME !!" log "$PKGNAME: Building" && run make -j$JOBS MAKEINFO=true fail_panic "Can't build $PKGNAME !!" log "$PKGNAME: Installing" && run make install MAKEINFO=true fail_panic "Can't install $PKGNAME" ) || exit 1 } # The GCC build in Android is insane and stores gmp and friends as tarballs and # extracts them as a part of the build step (in the meta-configure of all # places). No one understands how any of that mess works, so just deal with # extracting them here... EXTRACTED_PACKAGES=$OUT_DIR/packages mkdir -p $EXTRACTED_PACKAGES fail_panic "Could not create directory for packages." log "gmp-$GMP_VERSION: Extracting" && tar xf $TOOLCHAIN_DIR/gmp/gmp-$GMP_VERSION.tar.bz2 -C $EXTRACTED_PACKAGES log "mpfr-$MPFR_VERSION: Extracting" && tar xf $TOOLCHAIN_DIR/mpfr/mpfr-$MPFR_VERSION.tar.bz2 -C $EXTRACTED_PACKAGES log "mpc-$MPC_VERSION: Extracting" && tar xf $TOOLCHAIN_DIR/mpc/mpc-$MPC_VERSION.tar.gz -C $EXTRACTED_PACKAGES build_host_package () { local PKGNAME=$1 shift ( mkdir -p $OUT_DIR/$PKGNAME && cd $OUT_DIR/$PKGNAME && setup_host_build_env && log "$PKGNAME: Configuring" && run $EXTRACTED_PACKAGES/$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 } export ABI=$HOST_BITS SUPPORT_INSTALL= BASE_HOST_OPTIONS="--prefix=$SUPPORT_DIR --disable-shared" build_host_package gmp-$GMP_VERSION $BASE_HOST_OPTIONS var_append BASE_HOST_OPTIONS "--with-gmp=$SUPPORT_DIR" build_host_package mpfr-$MPFR_VERSION $BASE_HOST_OPTIONS var_append BASE_HOST_OPTIONS "--with-mpfr=$SUPPORT_DIR" build_host_package mpc-$MPC_VERSION $BASE_HOST_OPTIONS var_append BASE_HOST_OPTIONS "--with-mpc=$SUPPORT_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" var_append BINUTILS_CONFIGURE_OPTIONS "--enable-lto --enable-plugin --enable-gold" build_binutils_package binutils-$BINUTILS_VERSION $BINUTILS_CONFIGURE_OPTIONS build_mingw_tools () { local PKGNAME=$1 ( mkdir -p $OUT_DIR/$PKGNAME && cd $OUT_DIR/$PKGNAME && log "$PKGNAME: Configuring" && run $MINGW_W64_SRC/mingw-w64-tools/widl/configure --prefix=$INSTALL_DIR --target=$TARGET_TAG --includedir=$OUT_DIR/install/x86_64-w64-mingw32-4.8/x86_64-w64-mingw32/ fail_panic "Can't configure mingw-64-tools" log "$PKGNAME: Installing" && run make install -j$JOBS ) || exit 1 } build_mingw_pthreads_lib () { local PKGNAME=$1 local CONFIGURE_EXTRA_ARGS=$2 ( mkdir -p $OUT_DIR/$PKGNAME && cd $OUT_DIR/$PKGNAME && setup_mingw_build_env && log "$PKGNAME (32-bit): Configuring" && run $MINGW_W64_SRC/mingw-w64-libraries/winpthreads/configure --prefix=$PREFIX_FOR_TARGET --target=$TARGET_TAG --host=$TARGET_TAG $CONFIGURE_EXTRA_ARGS && fail_panic "Can't configure $PKGNAME" ) || exit 1 # run it once so fakelib/libgcc.a is created and make subsequently fails # while looking for libpthread.a. Copy libgcc.a to libpthread.a and # retry. cd $OUT_DIR/$PKGNAME && run make install -j$JOBS ( cd $OUT_DIR/$PKGNAME cp fakelib/libgcc.a fakelib/libpthread.a && log "$PKGNAME: Installing" && run make install -j$JOBS ) || exit 1 } build_mingw_pthreads () { local PKGNAME=$1 local LDFLAGS_COMMON="-Wl,--dynamicbase -Wl,--nxcompat" local LDFLAGS_32="$LDFLAGS_COMMON -m32" local LDFLAGS_64="$LDFLAGS_COMMON -Wl,--high-entropy-va" ( CFLAGS="$CFLAGS -m32" CXXFLAGS="$CXXFLAGS -m32" LDFLAGS=$LDFLAGS_32 RCFLAGS="-F pe-i386" export CFLAGS CXXFLAGS LDFLAGS RCFLAGS build_mingw_pthreads_lib $PKGNAME-32 "--build=$BUILD_TAG32 --libdir=$PREFIX_FOR_TARGET/lib32" (run cp $PREFIX_FOR_TARGET/bin/libwinpthread-1.dll $PREFIX_FOR_TARGET/lib32) || exit 1 ) LDFLAGS=$LDFLAGS_64 export LDFLAGS build_mingw_pthreads_lib $PKGNAME-64 "--build=$BUILD_TAG64" } # Install the right mingw64 headers into the sysroot build_mingw_headers () { local PKGNAME=$1 ( mkdir -p $OUT_DIR/$PKGNAME && cd $OUT_DIR/$PKGNAME && log "$PKGNAME: Configuring" && run $MINGW_W64_SRC/mingw-w64-headers/configure --prefix=$PREFIX_FOR_TARGET --host=$TARGET_TAG \ --build=$HOST_TAG --enable-sdk=all --enable-secure-api --with-default-msvcrt=ucrt fail_panic "Can't configure mingw-64-headers" run make 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 install mingw-64-headers" ) || exit 1 } # 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 ( mkdir -p $OUT_DIR/$PKGNAME && cd $OUT_DIR/$PKGNAME && setup_host_build_env && log "core-$PKGNAME: Configuring" && run $TOOLCHAIN_DIR/gcc/$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 } # Build and install the C runtime files needed by the toolchain build_mingw_crt () { local PKGNAME=$1 shift ( mkdir -p $OUT_DIR/$PKGNAME && cd $OUT_DIR/$PKGNAME && export PATH=$INSTALL_DIR/bin:$PATH log "$PKGNAME: Configuring" && run $MINGW_W64_SRC/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 } build_libgcc () { local PKGNAME=$1 shift ( # No configure step here! We're resuming work that was started # in build_core_gcc. cd $OUT_DIR/$PKGNAME && setup_host_build_env && 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 } 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" var_append GCC_CONFIGURE_OPTIONS "--enable-threads=posix" var_append GCC_CONFIGURE_OPTIONS "--enable-shared=libgcc" build_mingw_tools mingw-w64-tools 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=$PREFIX_FOR_TARGET --with-default-msvcrt=ucrt" if [ "$TARGET_MULTILIBS" ]; then var_append CRT_CONFIGURE_OPTIONS "--enable-lib32" fi build_mingw_crt mingw-w64-crt $CRT_CONFIGURE_OPTIONS # Build winpthreads build_mingw_pthreads mingw-w64-pthreads build_libgcc gcc-$GCC_VERSION check_dlls () { local objdump=$INSTALL_DIR/bin/x86_64-w64-mingw32-objdump local dlls=($INSTALL_DIR/x86_64-w64-mingw32/bin/libwinpthread-1.dll $INSTALL_DIR/x86_64-w64-mingw32/lib32/libwinpthread-1.dll $INSTALL_DIR/x86_64-w64-mingw32/lib/libgcc_s_seh-1.dll $INSTALL_DIR/x86_64-w64-mingw32/lib32/libgcc_s_sjlj-1.dll ) local dllflags=(00000160 00000140 00000160 00000140) # Verify that there are 4 dlls in $INSTALL_DIR four_dlls_found=`find $INSTALL_DIR -iname *.dll | wc -l | grep "^4$"` fail_panic "Number of DLL files in $INSTALL_DIR is not equal to 4" # Verify that each DLL has the expected flags for ASLR, DEP set. for index in "${!dlls[@]}" do local dll=${dlls[index]} local flag=${dllflags[index]} found=`${objdump} -x ${dll} | grep DllCharacteristics | grep ${flag}` fail_panic "Expected DllCharacteristics ${flag} not found for ${dll}" done } check_dlls # Let's generate the licenses/ directory LICENSE_DIRS="$SRC_DIR" var_append LICENSE_DIRS "$TOOLCHAIN_DIR/binutils/binutils-$BINUTILS_VERSION" var_append LICENSE_DIRS "$GCC_SRC_DIR" var_append LICENSE_DIRS "$EXTRACTED_PACKAGES" echo > $INSTALL_DIR/NOTICE for LICENSE in $(find $LICENSE_DIRS -name "COPYING*"); do cat $SRC_DIR/$LICENSE >> $INSTALL_DIR/NOTICE done touch $INSTALL_DIR/MODULE_LICENSE_GPL # The build server generates a repo.prop file that contains the current SHAs of # each project. REPO_PROP_PATH=$INSTALL_DIR/repo.prop if [ -f $DIST_DIR/repo.prop ]; then cp $DIST_DIR/repo.prop $REPO_PROP_PATH else # Generate our own if we're building locally. # The pushd/popd is to ensure that we're at least somewhere within our # source tree. There aren't any assumptions made about our CWD. pushd $ANDROID_BUILD_TOP repo forall \ -c 'echo $REPO_PROJECT $(git rev-parse HEAD)' > $REPO_PROP_PATH popd fi PACKAGE_NAME=$DIST_DIR/$TARGET_TAG-linux-x86_64.tar.bz2 log "Packaging $TARGET_TAG toolchain to $PACKAGE_NAME" run tar cjf $PACKAGE_NAME -C $OUTER_INSTALL_DIR $PACKAGE_DIR/ fail_panic "Could not package $TARGET_TAG toolchain!" log "Done. See $DIST_DIR:" ls -l $PACKAGE_NAME exit 0