• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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