1#!/bin/sh 2# 3# Copyright (C) 2016 The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16# 17# Rebuild the mingw64 cross-toolchain from scratch 18# 19# See --help for usage example. 20 21PROGNAME=$(basename $0) 22PROGDIR=$(dirname $0) 23PROGDIR=$(cd $PROGDIR && pwd) 24ANDROID_BUILD_TOP=$(realpath $PROGDIR/../..) 25TOOLCHAIN_DIR=$(realpath $ANDROID_BUILD_TOP/toolchain) 26 27HELP= 28VERBOSE=2 29 30# This will be reset later. 31LOG_FILE=/dev/null 32 33panic () 34{ 35 1>&2 echo "Error: $@" 36 exit 1 37} 38 39fail_panic () 40{ 41 if [ $? != 0 ]; then 42 panic "$@" 43 fi 44} 45 46var_value () 47{ 48 eval echo \"$1\" 49} 50 51var_append () 52{ 53 local _varname=$1 54 local _varval=$(var_value $_varname) 55 shift 56 if [ -z "$_varval" ]; then 57 eval $_varname=\"$*\" 58 else 59 eval $_varname=\$$_varname\" $*\" 60 fi 61} 62 63run () 64{ 65 if [ "$VERBOSE" -gt 0 ]; then 66 echo "COMMAND: >>>> $@" >> $LOG_FILE 67 fi 68 if [ "$VERBOSE" -gt 1 ]; then 69 echo "COMMAND: >>>> $@" 70 fi 71 if [ "$VERBOSE" -gt 1 ]; then 72 "$@" 73 else 74 "$@" > /dev/null 2>&1 75 fi 76} 77 78log () 79{ 80 if [ "$LOG_FILE" ]; then 81 echo "$@" >> $LOG_FILE 82 fi 83 if [ "$VERBOSE" -gt 0 ]; then 84 echo "$@" 85 fi 86} 87 88NUM_CORES=$(grep -c -e '^processor' /proc/cpuinfo) 89JOBS=$(( $NUM_CORES * 2 )) 90 91GMP_VERSION=5.0.5 92MPFR_VERSION=3.1.1 93MPC_VERSION=1.0.1 94BINUTILS_VERSION=2.27 95GCC_VERSION=4.8.3 96MINGW_W64_VERSION=v6.x 97 98TARGET_ARCH=x86_64 99TARGET_MULTILIBS=true # not empty to enable multilib 100CLEANUP= 101 102OPT_GCC_VERSION= 103 104for opt; do 105 optarg=`expr "x$opt" : 'x[^=]*=\(.*\)'` 106 case $opt in 107 -h|-?|--help) HELP=true;; 108 --verbose) VERBOSE=$(( $VERBOSE + 1 ));; 109 --quiet) VERBOSE=$(( $VERBOSE - 1 ));; 110 -j*|--jobs=*) JOBS=$optarg;; 111 --target-arch=*) TARGET_ARCH=$optarg;; 112 --no-multilib) TARGET_MULTILIBS="";; 113 --gcc-version=*) OPT_GCC_VERSION=$optarg;; 114 --cleanup) CLEANUP=true;; 115 -*) panic "Unknown option '$opt', see --help for list of valid ones.";; 116 *) panic "This script doesn't take any parameter, see --help for details.";; 117 esac 118done 119 120 121if [ "$HELP" ]; then 122 cat <<EOF 123Usage: $PROGNAME [options] 124 125This program is used to rebuild a mingw64 cross-toolchain from scratch. 126 127All toolchain binaries can generate both Win32 and Win64 executables. Use -m32 128or -m64 at compile/link time to select a specific target. 129 130Valid options: 131 -h|-?|--help Print this message." 132 --verbose Increase verbosity." 133 --quiet Decrease verbosity." 134 --jobs=<num> Run <num> build tasks in parallel [$JOBS]." 135 -j<num> Same as --jobs=<num>." 136 --no-multilib Disable multilib toolchain build." 137 --target-arch=<arch> Select default target architecture [$TARGET_ARCH]." 138 --gcc-version=<version> Select alternative GCC version [$GCC_VERSION]" 139EOF 140 exit 0 141fi 142 143if [ "$OPT_GCC_VERSION" ]; then 144 GCC_VERSION=$OPT_GCC_VERSION 145fi 146 147GCC_SRC_DIR=$TOOLCHAIN_DIR/gcc/gcc-$GCC_VERSION 148if [ ! -d "$GCC_SRC_DIR" ]; then 149 panic "Missing GCC source directory: $GCC_SRC_DIR" 150fi 151 152GCC_MAJOR_MINOR=$(echo "$GCC_VERSION" | cut -d. -f1-2) 153 154# Top level out directory. 155OUT_DIR=$ANDROID_BUILD_TOP/out 156 157# Name of the directory inside the package. 158PACKAGE_DIR=x86_64-w64-mingw32-$GCC_MAJOR_MINOR 159 160# Directory to isolate the install package from any similarly named directories. 161OUTER_INSTALL_DIR=$OUT_DIR/install 162 163# Actual install path. 164INSTALL_DIR=$OUTER_INSTALL_DIR/$PACKAGE_DIR 165 166# Install directory for build dependencies that are not in the final package 167# (gmp and whatnot). 168SUPPORT_DIR=$INSTALL_DIR 169 170# For the final artifacts. Will be archived on the build server. 171if [ -z "$DIST_DIR" ]; then 172 DIST_DIR=$OUT_DIR/dist 173fi 174 175BUILD_TAG64=x86_64-linux-gnu 176BUILD_TAG32=i686-linux-gnu 177 178# We don't want debug executables 179BUILD_CFLAGS="-O2 -fomit-frame-pointer -s" 180BUILD_LDFLAGS="" 181 182rm -rf $OUT_DIR 183mkdir -p $OUT_DIR 184mkdir -p $INSTALL_DIR 185mkdir -p $DIST_DIR 186mkdir -p $SUPPORT_DIR 187 188LOG_FILE=$OUT_DIR/build.log 189rm -f $LOG_FILE && touch $LOG_FILE 190if [ "$VERBOSE" -eq 1 ]; then 191 echo "To follow build, use in another terminal: tail -F $LOG_FILE" 192fi 193 194case $TARGET_ARCH in 195 x86_64) TARGET_BITS=64;; 196 i686) TARGET_BITS=32;; 197 *) panic "Invalid --target parameter. Valid values are: x86_64 i686";; 198esac 199TARGET_TAG=$TARGET_ARCH-w64-mingw32 200log "Target arch: $TARGET_TAG" 201log "Target bits: $TARGET_BITS" 202 203HOST_ARCH=x86_64 204HOST_BITS=64 205 206HOST_TAG=$HOST_ARCH-linux-gnu 207log "Host arch: $HOST_TAG" 208 209# Copy this script 210cp $0 $INSTALL_DIR/ && 211echo "This file has been automatically generated on $(date) with the following command:" > $INSTALL_DIR/README && 212echo "$PROGNAME $@" >> $INSTALL_DIR/README && 213echo "" >> $INSTALL_DIR/README && 214echo "The MD5 hashes for the original sources packages are:" >> $INSTALL_DIR/README 215fail_panic "Could not copy script to installation directory." 216 217PREFIX_FOR_TARGET=$INSTALL_DIR/$TARGET_TAG 218WITH_WIDL=$INSTALL_DIR/bin 219MINGW_W64_SRC=$TOOLCHAIN_DIR/mingw/mingw-w64-$MINGW_W64_VERSION 220 221setup_host_build_env () 222{ 223 local BINPREFIX=$ANDROID_BUILD_TOP/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/bin/x86_64-linux- 224 225 CC=${BINPREFIX}gcc 226 CXX=${BINPREFIX}g++ 227 LD=${BINPREFIX}ld 228 AS=${BINPREFIX}as 229 AR=${BINPREFIX}ar 230 RANLIB=${BINPREFIX}ranlib 231 STRIP=${BINPREFIX}strip 232 export CC CXX LD AS AR RANLIB STRIP 233 234 export CFLAGS="$BUILD_CFLAGS" 235 export CXXFLAGS="$BUILD_CFLAGS" 236 export LDFLAGS="$BUILD_LDFLAGS" 237} 238 239setup_mingw_build_env () 240{ 241 local BINPREFIX=$INSTALL_DIR/bin/x86_64-w64-mingw32- 242 243 CC=${BINPREFIX}gcc 244 CXX=${BINPREFIX}g++ 245 LD=${BINPREFIX}ld 246 AS=${BINPREFIX}as 247 AR=${BINPREFIX}ar 248 RANLIB=${BINPREFIX}ranlib 249 RC=${BINPREFIX}windres 250 STRIP=${BINPREFIX}strip 251 export CC CXX LD AS AR RANLIB RC STRIP 252 253} 254 255setup_install_env () 256{ 257 export PATH=$INSTALL_DIR/bin:$PATH 258} 259 260build_binutils_package () 261{ 262 local PKGNAME=$1 263 shift 264 265 ( 266 mkdir -p $OUT_DIR/$PKGNAME && 267 cd $OUT_DIR/$PKGNAME && 268 setup_host_build_env && 269 log "$PKGNAME: Configuring" && 270 run $TOOLCHAIN_DIR/binutils/$PKGNAME/configure "$@" 271 fail_panic "Can't configure $PKGNAME !!" 272 273 log "$PKGNAME: Building" && 274 run make -j$JOBS MAKEINFO=true 275 fail_panic "Can't build $PKGNAME !!" 276 277 log "$PKGNAME: Installing" && 278 run make install MAKEINFO=true 279 fail_panic "Can't install $PKGNAME" 280 ) || exit 1 281} 282 283# The GCC build in Android is insane and stores gmp and friends as tarballs and 284# extracts them as a part of the build step (in the meta-configure of all 285# places). No one understands how any of that mess works, so just deal with 286# extracting them here... 287EXTRACTED_PACKAGES=$OUT_DIR/packages 288mkdir -p $EXTRACTED_PACKAGES 289fail_panic "Could not create directory for packages." 290 291log "gmp-$GMP_VERSION: Extracting" && 292tar xf $TOOLCHAIN_DIR/gmp/gmp-$GMP_VERSION.tar.bz2 -C $EXTRACTED_PACKAGES 293log "mpfr-$MPFR_VERSION: Extracting" && 294tar xf $TOOLCHAIN_DIR/mpfr/mpfr-$MPFR_VERSION.tar.bz2 -C $EXTRACTED_PACKAGES 295log "mpc-$MPC_VERSION: Extracting" && 296tar xf $TOOLCHAIN_DIR/mpc/mpc-$MPC_VERSION.tar.gz -C $EXTRACTED_PACKAGES 297 298build_host_package () 299{ 300 local PKGNAME=$1 301 shift 302 303 ( 304 mkdir -p $OUT_DIR/$PKGNAME && 305 cd $OUT_DIR/$PKGNAME && 306 setup_host_build_env && 307 log "$PKGNAME: Configuring" && 308 run $EXTRACTED_PACKAGES/$PKGNAME/configure "$@" 309 fail_panic "Can't configure $PKGNAME !!" 310 311 log "$PKGNAME: Building" && 312 run make -j$JOBS 313 fail_panic "Can't build $PKGNAME !!" 314 315 log "$PKGNAME: Installing" && 316 run make install 317 fail_panic "Can't install $PKGNAME" 318 ) || exit 1 319} 320 321export ABI=$HOST_BITS 322SUPPORT_INSTALL= 323BASE_HOST_OPTIONS="--prefix=$SUPPORT_DIR --disable-shared" 324build_host_package gmp-$GMP_VERSION $BASE_HOST_OPTIONS 325var_append BASE_HOST_OPTIONS "--with-gmp=$SUPPORT_DIR" 326 327build_host_package mpfr-$MPFR_VERSION $BASE_HOST_OPTIONS 328var_append BASE_HOST_OPTIONS "--with-mpfr=$SUPPORT_DIR" 329 330build_host_package mpc-$MPC_VERSION $BASE_HOST_OPTIONS 331var_append BASE_HOST_OPTIONS "--with-mpc=$SUPPORT_DIR" 332 333BINUTILS_CONFIGURE_OPTIONS=$BASE_HOST_OPTIONS 334var_append BINUTILS_CONFIGURE_OPTIONS "--target=$TARGET_TAG --disable-nls" 335if [ "$TARGET_MULTILIBS" ]; then 336 var_append BINUTILS_CONFIGURE_OPTIONS "--enable-targets=x86_64-w64-mingw32,i686-w64-mingw32" 337fi 338 339var_append BINUTILS_CONFIGURE_OPTIONS "--with-sysroot=$INSTALL_DIR" 340var_append BINUTILS_CONFIGURE_OPTIONS "--enable-lto --enable-plugin --enable-gold" 341 342build_binutils_package binutils-$BINUTILS_VERSION $BINUTILS_CONFIGURE_OPTIONS 343 344build_mingw_tools () 345{ 346 local PKGNAME=$1 347 348 ( 349 mkdir -p $OUT_DIR/$PKGNAME && 350 cd $OUT_DIR/$PKGNAME && 351 log "$PKGNAME: Configuring" && 352 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/ 353 fail_panic "Can't configure mingw-64-tools" 354 log "$PKGNAME: Installing" && 355 run make install -j$JOBS 356 ) || exit 1 357} 358 359build_mingw_pthreads_lib () 360{ 361 local PKGNAME=$1 362 local CONFIGURE_EXTRA_ARGS=$2 363 364 ( 365 mkdir -p $OUT_DIR/$PKGNAME && 366 cd $OUT_DIR/$PKGNAME && 367 setup_mingw_build_env && 368 log "$PKGNAME (32-bit): Configuring" && 369 run $MINGW_W64_SRC/mingw-w64-libraries/winpthreads/configure --prefix=$PREFIX_FOR_TARGET --target=$TARGET_TAG --host=$TARGET_TAG $CONFIGURE_EXTRA_ARGS && 370 fail_panic "Can't configure $PKGNAME" 371 ) || exit 1 372 373 # run it once so fakelib/libgcc.a is created and make subsequently fails 374 # while looking for libpthread.a. Copy libgcc.a to libpthread.a and 375 # retry. 376 cd $OUT_DIR/$PKGNAME && run make install -j$JOBS 377 378 ( 379 cd $OUT_DIR/$PKGNAME 380 cp fakelib/libgcc.a fakelib/libpthread.a && 381 log "$PKGNAME: Installing" && 382 run make install -j$JOBS 383 ) || exit 1 384} 385 386build_mingw_pthreads () 387{ 388 local PKGNAME=$1 389 local LDFLAGS_COMMON="-Wl,--dynamicbase -Wl,--nxcompat" 390 local LDFLAGS_32="$LDFLAGS_COMMON -m32" 391 local LDFLAGS_64="$LDFLAGS_COMMON -Wl,--high-entropy-va" 392 393 ( 394 CFLAGS="$CFLAGS -m32" 395 CXXFLAGS="$CXXFLAGS -m32" 396 LDFLAGS=$LDFLAGS_32 397 RCFLAGS="-F pe-i386" 398 export CFLAGS CXXFLAGS LDFLAGS RCFLAGS 399 build_mingw_pthreads_lib $PKGNAME-32 "--build=$BUILD_TAG32 --libdir=$PREFIX_FOR_TARGET/lib32" 400 (run cp $PREFIX_FOR_TARGET/bin/libwinpthread-1.dll $PREFIX_FOR_TARGET/lib32) || exit 1 401 ) 402 403 LDFLAGS=$LDFLAGS_64 404 export LDFLAGS 405 build_mingw_pthreads_lib $PKGNAME-64 "--build=$BUILD_TAG64" 406} 407 408# Install the right mingw64 headers into the sysroot 409build_mingw_headers () 410{ 411 local PKGNAME=$1 412 413 ( 414 mkdir -p $OUT_DIR/$PKGNAME && 415 cd $OUT_DIR/$PKGNAME && 416 log "$PKGNAME: Configuring" && 417 run $MINGW_W64_SRC/mingw-w64-headers/configure --prefix=$PREFIX_FOR_TARGET --host=$TARGET_TAG \ 418 --build=$HOST_TAG --enable-sdk=all --enable-secure-api --with-default-msvcrt=ucrt 419 fail_panic "Can't configure mingw-64-headers" 420 421 run make 422 log "$PKGNAME: Installing" && 423 run make install -j$JOBS && 424 run cd $INSTALL_DIR && 425 run ln -s $TARGET_TAG mingw && 426 run cd $INSTALL_DIR/mingw && 427 run ln -s lib lib$TARGET_BITS 428 fail_panic "Can't install mingw-64-headers" 429 ) || exit 1 430} 431 432# Slightly different from build_host_package because we need to call 433# 'make all-gcc' and 'make install-gcc' as a special case. 434# 435build_core_gcc () 436{ 437 local PKGNAME=$1 438 shift 439 440 ( 441 mkdir -p $OUT_DIR/$PKGNAME && 442 cd $OUT_DIR/$PKGNAME && 443 setup_host_build_env && 444 log "core-$PKGNAME: Configuring" && 445 run $TOOLCHAIN_DIR/gcc/$PKGNAME/configure "$@" 446 fail_panic "Can't configure $PKGNAME !!" 447 448 log "core-$PKGNAME: Building" && 449 run make -j$JOBS all-gcc 450 fail_panic "Can't build $PKGNAME !!" 451 452 log "core-$PKGNAME: Installing" && 453 run make -j$JOBS install-gcc 454 fail_panic "Can't install $PKGNAME" 455 ) || exit 1 456} 457 458 459# Build and install the C runtime files needed by the toolchain 460build_mingw_crt () 461{ 462 local PKGNAME=$1 463 shift 464 465 ( 466 mkdir -p $OUT_DIR/$PKGNAME && 467 cd $OUT_DIR/$PKGNAME && 468 export PATH=$INSTALL_DIR/bin:$PATH 469 log "$PKGNAME: Configuring" && 470 run $MINGW_W64_SRC/mingw-w64-crt/configure "$@" 471 fail_panic "Can't configure $PKGNAME !!" 472 473 log "$PKGNAME: Building" && 474 run make -j$JOBS 475 fail_panic "Can't build $PKGNAME !!" 476 477 log "$PKGNAME: Installing" && 478 run make -j$JOBS install 479 fail_panic "Can't install $PKGNAME" 480 ) || exit 1 481} 482 483 484build_libgcc () 485{ 486 local PKGNAME=$1 487 shift 488 489 ( 490 # No configure step here! We're resuming work that was started 491 # in build_core_gcc. 492 cd $OUT_DIR/$PKGNAME && 493 setup_host_build_env && 494 log "libgcc-$PKGNAME: Building" && 495 run make -j$JOBS 496 fail_panic "Can't build libgcc-$PKGNAME !!" 497 498 log "libgcc-$PKGNAME: Installing" && 499 run make -j$JOBS install 500 fail_panic "Can't install libgcc-$PKGNAME" 501 ) || exit 1 502} 503 504GCC_CONFIGURE_OPTIONS=$BASE_HOST_OPTIONS 505var_append GCC_CONFIGURE_OPTIONS "--target=$TARGET_TAG" 506if [ "$TARGET_MULTILIBS" ]; then 507 var_append GCC_CONFIGURE_OPTIONS "--enable-targets=all" 508fi 509var_append GCC_CONFIGURE_OPTIONS "--enable-languages=c,c++" 510var_append GCC_CONFIGURE_OPTIONS "--with-sysroot=$INSTALL_DIR" 511var_append GCC_CONFIGURE_OPTIONS "--enable-threads=posix" 512var_append GCC_CONFIGURE_OPTIONS "--enable-shared=libgcc" 513 514build_mingw_tools mingw-w64-tools 515build_mingw_headers mingw-w64-headers 516 517build_core_gcc gcc-$GCC_VERSION $GCC_CONFIGURE_OPTIONS 518 519CRT_CONFIGURE_OPTIONS="--host=$TARGET_TAG --with-sysroot=$INSTALL_DIR --prefix=$PREFIX_FOR_TARGET --with-default-msvcrt=ucrt" 520if [ "$TARGET_MULTILIBS" ]; then 521 var_append CRT_CONFIGURE_OPTIONS "--enable-lib32" 522fi 523 524build_mingw_crt mingw-w64-crt $CRT_CONFIGURE_OPTIONS 525 526# Build winpthreads 527build_mingw_pthreads mingw-w64-pthreads 528 529build_libgcc gcc-$GCC_VERSION 530 531check_dlls () { 532 local objdump=$INSTALL_DIR/bin/x86_64-w64-mingw32-objdump 533 local dlls=($INSTALL_DIR/x86_64-w64-mingw32/bin/libwinpthread-1.dll 534 $INSTALL_DIR/x86_64-w64-mingw32/lib32/libwinpthread-1.dll 535 $INSTALL_DIR/x86_64-w64-mingw32/lib/libgcc_s_seh-1.dll 536 $INSTALL_DIR/x86_64-w64-mingw32/lib32/libgcc_s_sjlj-1.dll 537 ) 538 local dllflags=(00000160 00000140 00000160 00000140) 539 540 # Verify that there are 4 dlls in $INSTALL_DIR 541 four_dlls_found=`find $INSTALL_DIR -iname *.dll | wc -l | grep "^4$"` 542 fail_panic "Number of DLL files in $INSTALL_DIR is not equal to 4" 543 544 # Verify that each DLL has the expected flags for ASLR, DEP set. 545 for index in "${!dlls[@]}" 546 do 547 local dll=${dlls[index]} 548 local flag=${dllflags[index]} 549 found=`${objdump} -x ${dll} | grep DllCharacteristics | grep ${flag}` 550 fail_panic "Expected DllCharacteristics ${flag} not found for ${dll}" 551 done 552} 553 554check_dlls 555 556# Let's generate the licenses/ directory 557LICENSE_DIRS="$SRC_DIR" 558var_append LICENSE_DIRS "$TOOLCHAIN_DIR/binutils/binutils-$BINUTILS_VERSION" 559var_append LICENSE_DIRS "$GCC_SRC_DIR" 560var_append LICENSE_DIRS "$EXTRACTED_PACKAGES" 561 562echo > $INSTALL_DIR/NOTICE 563for LICENSE in $(find $LICENSE_DIRS -name "COPYING*"); do 564 cat $SRC_DIR/$LICENSE >> $INSTALL_DIR/NOTICE 565done 566 567touch $INSTALL_DIR/MODULE_LICENSE_GPL 568 569# The build server generates a repo.prop file that contains the current SHAs of 570# each project. 571REPO_PROP_PATH=$INSTALL_DIR/repo.prop 572if [ -f $DIST_DIR/repo.prop ]; then 573 cp $DIST_DIR/repo.prop $REPO_PROP_PATH 574else 575 # Generate our own if we're building locally. 576 # The pushd/popd is to ensure that we're at least somewhere within our 577 # source tree. There aren't any assumptions made about our CWD. 578 pushd $ANDROID_BUILD_TOP 579 repo forall \ 580 -c 'echo $REPO_PROJECT $(git rev-parse HEAD)' > $REPO_PROP_PATH 581 popd 582fi 583 584PACKAGE_NAME=$DIST_DIR/$TARGET_TAG-linux-x86_64.tar.bz2 585log "Packaging $TARGET_TAG toolchain to $PACKAGE_NAME" 586run tar cjf $PACKAGE_NAME -C $OUTER_INSTALL_DIR $PACKAGE_DIR/ 587fail_panic "Could not package $TARGET_TAG toolchain!" 588log "Done. See $DIST_DIR:" 589ls -l $PACKAGE_NAME 590 591exit 0 592