aboutsummaryrefslogtreecommitdiff
path: root/build-mingw64-toolchain.sh
blob: 7dbdd4422356a97398d1f50a792a00456614a071 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
#!/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
#
# See --help for usage example.

PROGNAME=$(basename $0)
PROGDIR=$(dirname $0)
PROGDIR=$(cd $PROGDIR && pwd)
PATCHES_DIR=$PROGDIR/toolchain-patches

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
       "$@" > /dev/null 2>&1
    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 moving home?
# GMP_VERSION=5.1.0
# GMP_URL=ftp://ftp.gmplib.org/pub/gmp-$GMP_VERSION/
# ..but the old one is still there:
GMP_VERSION=5.0.5
GMP_URL=http://ftp.gnu.org/gnu/gmp/

MPFR_VERSION=3.1.1
MPC_VERSION=1.0.1
BINUTILS_VERSION=2.25
GCC_VERSION=4.8.1

# Need at least revision 5166
# For reference, I've built a working NDK with 5445
# (latest as of Sun Feb 3 2013 is 5578)
MINGW_W64_VERSION=svn@5861

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*|--jobs=*) 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
    cat <<EOF
Usage: $PROGNAME [options]

This program is used to rebuild a mingw64 cross-toolchain from scratch.

It uses your host 'gcc' by default to generate a cross-toolchain named
either x86_64-w64-mingw32 or i686-w64-mingw32, depending on your compiler's
target bitness. For example:

    /path/to/build-mingw64-toolchain.sh

All toolchain binaries can generate both Win32 and Win64 executables.
The default target is Win64, but you can change this to Win32 by using
the '--target-arch=i686' option. Otherwise, use -m32 or -m64 at compile/link
time to select a specific target.

It is possible to use --binprefix=<prefix> to specify an alternative host
toolchain prefix, e.g. <prefix>-gcc to compile. For example, to generate
64-bit binaries that can run on older Linux distributions, using the Android
SDK's compatibility Linux toolchain, one can do the following:

    SDK_TOOLCHAIN=<some-dir>
    PREBUILTS=https://android.googlesource.com/platform/prebuilts
    git clone \$PREBUILTS/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.8 \\
        \$SDK_TOOLCHAIN
    PATH=\$SDK_TOOLCHAIN/bin:\$PATH \\
    /path/to/build-mingw64-toolchain.sh --binprefix=x86_64-linux

In the example above, the script will use 'x86_64-linux-gcc' and related
tools to build the final binaries.

It is recommended to use --package-dir=<path> to generate tarballs of the
generated toolchains under <path>/, for easier redistribution.

Valid options:
  -h|-?|--help                 Print this message."
  --verbose                    Increase verbosity."
  --quiet                      Decrease verbosity."
  --gcc-version=<version>      Select gcc version [$GCC_VERSION]."
  --binutil-version=<version>  Select binutils version [$BINUTILS_VERSION]."
  --gmp-version=<version>      Select libgmp version [$GMP_VERSION]."
  --mpfr-version=<version>     Select libmpfr version [$MPFR_VERSION]."
  --mpc-version=<version>      Select libmpc version [$MPC_VERSION]."
  --mingw-version=<version>    Select mingw-w64 version [$MINGW_W64_VERSION]."
  --jobs=<num>                 Run <num> build tasks in parallel [$JOBS]."
  -j<num>                      Same as --jobs=<num>."
  --binprefix=<prefix>         Specify bin prefix for host toolchain."
  --no-multilib                Disable multilib toolchain build."
  --target-arch=<arch>         Select default target architecture [$TARGET_ARCH]."
  --force-all                  Redo everything from scratch."
  --force-build                Force a rebuild (keep sources)."
  --cleanup                    Remove all temp files after build."
  --work-dir=<path>            Specify work/build directory [$TEMP_DIR]."
  --package-dir=<path>         Package toolchain to directory."

EOF
    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 [ -n "$HOST_BINPREFIX" ]; then
    # If $HOST_BINPREFIX is a directory but not ends with '/', append '/'.
    # Otherwise, append '-'.
    if [ -d "$HOST_BINPREFIX" ] ; then
        if [ -n "${HOST_BINPREFIX##*/}" ] ; then
	    HOST_BINPREFIX="${HOST_BINPREFIX}/"
	fi
    else
        HOST_BINPREFIX="${HOST_BINPREFIX}-"
    fi
    PROBE_CC=${HOST_BINPREFIX}gcc
fi
echo "Using GCC: $PROBE_CC"
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"

        LOCAL_PATCHES_DIR="$PATCHES_DIR/$PKG_BASENAME"
        if [ -d "$LOCAL_PATCHES_DIR" ] ; then
            PATCHES=$(find "$LOCAL_PATCHES_DIR" -name "*.patch" | sort)
            for PATCH in $PATCHES; do
                echo "Patching $PKG_BASENAME with $PATCH"
                (cd $SRC_DIR/$PKG_BASENAME && run patch -p1 < $PATCH)
                fail_panic "Can't patch $SRC_DIR with $PATCH"
            done
        fi
    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

INSTALL_DIR=$TEMP_DIR/install-$HOST_TAG/$TARGET_TAG
BUILD_DIR=$TEMP_DIR/build-$HOST_TAG

if [ "$FORCE_BUILD" ]; then
    rm -f $STAMP_DIR/*
    rm -rf $INSTALL_DIR
    rm -rf $BUILD_DIR
fi

# Make temp install directory
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 ${GMP_URL}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

PREFIX_FOR_TARGET=$INSTALL_DIR/$TARGET_TAG
WITH_WIDL=$INSTALL_DIR/bin
MINGW_W64_REVISION=
MINGW_W64_VERSION_NO_REV=$(echo $MINGW_W64_VERSION | awk 'BEGIN { FS="@" }; { print $1 }')
if [ "$MINGW_W64_VERSION_NO_REV" = "svn" ];  then
    MINGW_W64_REVISION=$(echo $MINGW_W64_VERSION | awk 'BEGIN { FS="@" }; { print $2 }')
    if [ ! -z "$MINGW_W64_REVISION" ] ; then
        if [ $MINGW_W64_REVISION -lt 5186 ] ; then
            PREFIX_FOR_TARGET=$INSTALL_DIR
        fi
        if [ $MINGW_W64_REVISION -lt 5252 ] ; then
            WITH_WIDL=mingw-w64-widl
        elif [ $MINGW_W64_REVISION -lt 5258 ] ; then
            WITH_WIDL=$TARGET_TAG-widl
        fi
        MINGW_W64_REVISION2=-r$MINGW_W64_REVISION
        MINGW_W64_REVISION=@${MINGW_W64_REVISION}
    fi
    MINGW_W64_SRC=$SRC_DIR/mingw-w64-svn$MINGW_W64_REVISION2
    MINGW_W64_VERSION=svn
fi

if [ -z "$MINGW_W64_REVISION" ] ; then
    # Released versions of MinGW-w64 don't provide easily accessible information
    # about the svn revision which this script needs to know.
    fail_panic "Building MinGW-w64 toolchain requires specifying an svn version"
fi

if [ ! -d $MINGW_W64_SRC ]; then
    MINGW64_SVN_URL=https://svn.code.sf.net/p/mingw-w64/code/trunk$MINGW_W64_REVISION
    echo "Checking out $MINGW64_SVN_URL $MINGW_W64_SRC"
    run svn co $MINGW64_SVN_URL $MINGW_W64_SRC
    LOCAL_PATCHES_DIR="$PATCHES_DIR/mingw-w64"
    if [ -d "$LOCAL_PATCHES_DIR" ] ; then
        PATCHES=$(find "$LOCAL_PATCHES_DIR" -name "*.patch" | sort)
        for PATCH in $PATCHES; do
            echo "Patching mingw-w64-$MINGW_W64_REVISION with $PATCH"
            (cd $MINGW_W64_SRC && run patch -p0 < $PATCH)
        done
    fi
fi

# 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
}

build_binutils_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 MAKEINFO=true
            fail_panic "Can't build $PKGNAME !!"

            log "$PKGNAME: Installing" &&
            run make install MAKEINFO=true
            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_binutils_package binutils-$BINUTILS_VERSION $BINUTILS_CONFIGURE_OPTIONS

build_mingw_tools ()
{
    local PKGNAME=$1
    echo "$STAMP_DIR/$PKGNAME"
    if [ ! -f "$STAMP_DIR/$PKGNAME" ]; then
        (
            mkdir -p $BUILD_DIR/$PKGNAME &&
            cd $BUILD_DIR/$PKGNAME &&
            log "$PKGNAME: Configuring" &&
            run $MINGW_W64_SRC/mingw-w64-tools/widl/configure --prefix=$INSTALL_DIR --target=$TARGET_TAG
            fail_panic "Can't configure mingw-64-tools"
            log "$PKGNAME: Installing" &&
            run make install -j$JOBS
        ) || exit 1
        touch $STAMP_DIR/$PKGNAME
    fi
}

# Install the right mingw64 headers into the sysroot
build_mingw_headers ()
{
    local PKGNAME=$1
    if [ ! -f "$STAMP_DIR/$PKGNAME" ]; then
        (
            # If --with-widl only identifies the program name (svn version dependent)...
            if [ $(basename "$WITH_WIDL") = "$WITH_WIDL" ] ; then
                # ...then need to add the right path too.
                export PATH=$PATH:$INSTALL_DIR/bin
            fi
            fail_panic "Can't find widl"
            mkdir -p $BUILD_DIR/$PKGNAME &&
            cd $BUILD_DIR/$PKGNAME &&
            log "$PKGNAME: Configuring" &&
            run $MINGW_W64_SRC/mingw-w64-headers/configure --prefix=$PREFIX_FOR_TARGET --host=$TARGET_TAG \
                --build=$HOST_TAG --with-widl=$WITH_WIDL --enable-sdk=all --enable-secure-api
            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
        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 $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
        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"

# A bug in MinGW-w64 forces us to build and use widl.
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"
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