summaryrefslogtreecommitdiff
path: root/scripts/bootstrap
blob: cee3e67dc7cd31c894a83c7c9e09019ea0e0b6be (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
#!/bin/sh

# Build an intermediate build-essential stratum using the host's tools
#  -- everything is installed into $PREFIX inside a chroot
#  -- a "cross-compiler" toolchain is built first, as suggested by LFS, and
#     this builds the actual build-essential tools.
#  -- unlike LFS, the initial toolchain goes into a separate prefix. This is
#     because programs in the target chroot cannot be executed outside of the
#     chroot without creating a symlink in the root directory of the host
#     system, which is not pleasant.
#  -- this also means we effectively always cross-compile, because nothing in
#     the chroot is executable during the build process. This is good, because
#     build-essential must be cross-compilable so that we can port Baserock
#     to new architectures.
#  -- we can't build g++, because since it's effectively a cross build we can't
#     bootstrap the compiler, and although theoretically we could build one in
#     the temporary toolchain, that doesn't actually work. Since the next step
#     is a native build we can get a c++ compiler in stage 2 of the bootstrap.

# In future Morph will handle the build of build-essential that is currently
# done by this script.

# Disable hashing, so that newly built tools are picked up in PATH immediately
set +h

set -eu

## Configuration

# Installing to a different sysroot (e.g. /tools) is supported, but in order
# to use the result as a staging filler Morph needs to know to create /bin and
# /lib{64} symlinks in the staging area if not present (creating the symlinks
# in the filler itself will conflict with the fhs-dirs chunk)
PREFIX=/usr

export MAKEFLAGS="-j 4"
export CFLAGS="-O2"

# i686
#TARGET="i686-baserock-linux-gnu"
#TARGET_GCC_CONFIG=
#export ARCH=i386

# x86_64
TARGET="x86_64-baserock-linux-gnu"
TARGET_GCC_CONFIG=
export ARCH="x86_64"

# Little-endian ARM
#TARGET="armv7l-baserock-linux-gnueabi"
#TARGET_GCC_CONFIG="--with-arch=armv7-a"
#export ARCH="arm"

# Big-endian ARM
#   No stable GCC release as of January 2013 can default to big-endian linking.
#   You must backport a patch from trunk to make this work. See:
#       http://gcc.gnu.org/bugzilla/show_bug.cgi?id=16350
#   Correct linking also depends on the correct --with-arch= flag being given.
#TARGET="armv7leb-baserock-linux-gnueabi"
#TARGET_GCC_CONFIG="--with-arch=armv7-a"
#export ARCH="arm"


## Setup

if [ "$#" -ne "1" ]; then
    echo "Usage: $0 BASE_DIR"
    echo
    echo "    Compiles a build-essential chroot in BASE_DIR. The directory"
    echo "    BASE_DIR/gits is expected to contain checkouts of all source"
    echo "    code required for the builds; you will currently have to"
    echo "    assemble this yourself based on the source code of this script."
    exit 1
fi

BASE_DIR="$1"

if [ ! -d "$BASE_DIR" ]; then
    echo "$BASE_DIR does not exist"
    exit 1
fi

BASE_DIR="$(readlink -f $BASE_DIR)"

TOOLCHAIN_DIR="$BASE_DIR"/toolchain
CHROOT_DIR="$BASE_DIR"/chroot

if [ ! -d "$BASE_DIR" ]; then
    echo "$BASE_DIR does not exist"
    exit 1
fi

if [ ! -d "$BASE_DIR/gits" ]; then
    echo "$BASE_DIR/gits does not exist"
    exit 1
fi

if [ ! -d "$TOOLCHAIN_DIR" ]; then
    mkdir -p "$TOOLCHAIN_DIR"
fi

if [ ! -d "$CHROOT_DIR" ]; then
    mkdir -p "$CHROOT_DIR"
fi


## Architecture-specific hacks

fix_chroot_for_target() {
    case "$TARGET" in
        x86_64*)
            # eglibc 2.15 is belligerant about putting things into /lib64,
            # especially when prefix is /usr. Currently if PREFIX is NOT /usr
            # we force ld.so to be in PREFIX/lib/ld.so; but eglibc installs it
            # in /lib64 anyway.
            if [ ! -e $CHROOT_DIR/lib64/ld-linux-x86-64.so.2 ]; then
                mkdir -p $CHROOT_DIR$PREFIX/lib64
                ln -sf $CHROOT_DIR$PREFIX/lib/ld-linux-x86-64.so.2 $CHROOT_DIR$PREFIX/lib64
            fi ;;
    esac
}

## Build process

assert_branch() {
    branch="$1"
    if which git > /dev/null &&
        [ $(git rev-parse HEAD) != $(git rev-parse "$branch") ]; then
        echo "Expected to be building '$branch' branch in $(basename $(pwd))" \
            >&2
        exit 1
    fi
}

touch_tree() {
    # git doesn't preserve mtimes, but this causes rebuilds of things that
    # haven't changed which breaks builds if the correct tools are not
    # available
    #date=$(date '+%C%y%m%d%H%M.%S')
    #find -type f -exec touch -m -t $date \{} \;
    return 0
}

# build-essential

build_binutils() {
    pass="$1"

    source_dir="$BASE_DIR"/gits/binutils-redhat
    build_dir="$BASE_DIR"/builds/binutils-"$pass"

    cd "$source_dir"
    assert_branch "baserock/build-essential"
    touch_tree

    rm -Rf "$build_dir" && mkdir -p "$build_dir"
    cd "$build_dir"

    # Note for shell escape purists: quotes around --with-lib-path's argument
    # do not work correctly
    if [ "$pass" == "pass1" ]; then
        extra_config="--target=$TARGET --with-sysroot=\"$CHROOT_DIR\" \
            --with-lib-path=$CHROOT_DIR$PREFIX/lib"
    elif [ "$pass" == "pass2" ]; then
        extra_config="--host=$TARGET --with-lib-path=$PREFIX/lib "
    else
        echo "Invalid pass for binutils: $pass"
        exit 1
    fi

    # Note that the root configure script's --help doesn't display the
    # arguments for sub-configure scripts, but it does pass on arguments to
    # them
    "$source_dir"/configure \
        --prefix=$PREFIX --disable-nls --disable-werror \
        $extra_config
    make
    make DESTDIR="$DESTDIR" install
}

build_busybox() {
    source_dir="$BASE_DIR"/gits/busybox
    build_dir="$BASE_DIR"/builds/busybox

    cd "$source_dir"
    assert_branch "baserock/build-essential"
    touch_tree

    rm -Rf "$build_dir" && mkdir -p "$build_dir"
    cd "$build_dir"

    # Busybox's default config includes basically everything
    make KBUILD_SRC="$source_dir" -f "$source_dir"/Makefile defconfig

    if [ "$PREFIX" != "/usr" ]; then
        # Install everything into DESTDIR instead of putting some of it into
        # DESTDIR/usr/. For compatibility with the old Baserock staging
        # fillers, if the prefix is /usr we still do the traditional /bin vs.
        # /usr/bin split for now.
        sed -e 's/.*CONFIG_INSTALL_NO_USR.*/CONFIG_INSTALL_NO_USR=y/' -i \
            .config
    fi

    sed -e 's/.*CONFIG_STATIC.*/CONFIG_STATIC=y/' -i .config

    # Requires stuff that was removed after eglibc 2.14
    sed -e 's/.*CONFIG_INETD.*/# CONFIG_INETD is not set/' -i .config

    # Disable all module tools. BusyBox's depmod isn't sufficient for Linux
    # builds, but Linux will build OK if depmod isn't present at all. Also, we
    # have kmod in Baserock which can do all this stuff anyway.
    sed -e 's/.*MODPROBE_SMALL=.*/# CONFIG_MODPROBE_SMALL is not set/' -i .config
    sed -e 's/.*INSMOD=.*/CONFIG_INSMOD=y/' -i .config
    sed -e 's/.*RMMOD=.*/CONFIG_RMMOD=y/' -i .config
    sed -e 's/.*LSMOD=.*/CONFIG_LSMOD=y/' -i .config
    sed -e 's/.*DEPMOD=.*/CONFIG_DEPMOD=y/' -i .config
    sed -e 's/.*MODPROBE=.*/CONFIG_MODPROBE=y/' -i .config

    sed -e 's/.*CONFIG_INETD.*/# CONFIG_INETD is not set/' -i .config


    make CROSS_COMPILE="$TARGET-"

    if [ "$PREFIX" != "/usr" ]; then
        make CROSS_COMPILE="$TARGET-" CONFIG_PREFIX="$DESTDIR$PREFIX" install
    else
        make CROSS_COMPILE="$TARGET-" CONFIG_PREFIX="$DESTDIR" install
    fi
}

build_eglibc() {
    source_dir="$BASE_DIR"/gits/eglibc2
    build_dir="$BASE_DIR"/builds/eglibc

    # Necessary for ARM port.
    if [ ! -e "$source_dir/libc/ports" ]; then
        ln -s "$source_dir/ports $source_dir/libc/ports"
    fi

    cd "$source_dir"
    assert_branch "baserock/2.15-build-essential"
    touch_tree

    rm -Rf "$build_dir" && mkdir -p "$build_dir"
    cd "$build_dir"

    # If prefix is set to /usr, eglibc otherwise decides to install its
    # libraries in /usr/lib64 on some Linux
    extra_config="--libdir=$PREFIX/lib"

    # Suggested by Linux From Scratch. Is the default really to build with
    # profiling??
    extra_config="--disable-profile $extra_config"

    # Minimum kernel version that this eglibc will be usable with.
    extra_config="--enable-kernel=2.6.25 $extra_config"

    # Location of headers
    extra_config="--with-headers=$CHROOT_DIR$PREFIX/include $extra_config"

    # Force configure flags of certain things that can't be detected in a
    # cross-compile.
    extra_config="$extra_config \
        libc_cv_c_cleanup=yes libc_cv_ctors_header=yes \
        libc_cv_forced_unwind=yes libc_cv_ssp=no"

    # + --without-fp for ARM
    "$source_dir"/libc/configure --prefix=$PREFIX --host="$TARGET" \
        --build=$("$source_dir"/libc/scripts/config.guess) \
        --enable-add-ons=nptl,ports $extra_config
    make
    make "install_root=$DESTDIR" install
}

build_gcc() {
    pass="$1"
    source_dir="$BASE_DIR"/gits/gcc-tarball
    build_dir="$BASE_DIR"/builds/gcc-"$pass"

    cd "$source_dir"
    assert_branch "baserock/build-essential"
    touch_tree

    # This hack is to prevent the multilib configuration of the host OS
    # leaking into Baserock, which does not use multilib at all. Without this,
    # the pass2 gcc will install its libraries into $PREFIX/lib64 instead of
    # $PREFIX/lib, because its configure script decides ${toolexeclibdir}
    # based on the output of gcc -print-multi-os-directory
    if [ "$(echo $TARGET | cut -c -6)" = "x86_64" ]; then
        sed -i "$source_dir/gcc/config/i386/t-linux64" \
            -e "/^MULTILIB_OSDIRNAMES/ c\
                MULTILIB_OSDIRNAMES = ."
    fi

    rm -Rf "$build_dir" && mkdir -p "$build_dir"
    cd "$build_dir"

    if [ "$pass" == "pass1" ]; then
        # In pass 1 we build a crosscompiler for $TARGET.
        extra_config="--target=$TARGET"

        # The pass 1 compiler needs to find the libraries we build in pass 2.
        # Include path must be set explicility, because it defaults to
        # $CHROOT_DIR/usr/include.
        extra_config="$extra_config \
            --with-sysroot="$CHROOT_DIR" \
            --with-native-system-header-dir=\"$CHROOT_DIR$PREFIX/include\""

        # Disable stuff that doesn't work when building a cross compiler
        # without an existing libc, and generally try to keep this build as
        # simple as possible.
        extra_config="$extra_config \
            --enable-languages=c \
            --disable-decimal-float --disable-libmudflap \
            --disable-libquadmath --disable-libssp --disable-shared \
            --disable-threads --disable-target-libiberty \
            --disable-target-zlib --without-headers --with-newlib \
            --with-system-zlib"

        # way too slow with this, but it maybe a good idea / required
        extra_config="--disable-bootstrap $extra_config"
    elif [ "$pass" == "pass2" ]; then
        # Pass 1 gcc's fixincludes process created a limits.h before there was
        # a real limits.h available for the target. This step (taken from
        # Linux From Scratch) creates a better one so gcc can compile.
        libgcc_dir=$(dirname $($TARGET-gcc -print-libgcc-file-name))
        cat "$source_dir/gcc/limitx.h" "$source_dir/gcc/glimits.h" \
                "$source_dir/gcc/limity.h" \
            > "$libgcc_dir/include-fixed/limits.h"

        # This time we are creating a native compiler running on $TARGET
        extra_config="--host=$TARGET"

        # I'm not sure why this is needed. target should default to host,
        # but if we don't pass this explicitly some of the files that should
        # go in $PREFIX/lib/gcc/$TARGET/4.6.2 end up in
        # $PREFIX/lib/gcc/$TARGET/4.6.3 instead. Weird.
        extra_config="--target=$TARGET $extra_config"

        # The two-step compiler process means we don't need to bootstrap now
        # (and couldn't anyway, since build != host for pass 2).
        extra_config="--disable-bootstrap $extra_config"

        # C++ doesn't build without a C++ compiler, I think, so we still
        # can't have that yet. We don't need one anyway.
        extra_config="$extra_config \
            --enable-clocale=gnu \
            --enable-languages=c --enable-shared \
            --enable-threads=posix"
    else
        echo "Invalid pass for gcc: $pass"
        exit 1
    fi

    # An attempt to stop anything going in $PREFIX/lib64
    extra_config=" --libdir=$PREFIX/lib $extra_config"

    # Disable searching /usr/local/include for headers
    extra_config="--with-local-prefix=$PREFIX $extra_config"

    # It makes no sense for Baserock to use multilib.
    extra_config="--disable-multilib $extra_config"

    # General stuff that we don't need / won't work right now
    extra_config="$extra_config \
        --disable-libgomp --without-cloog --without-ppl"

    # mpfr is built as part of gcc, but we need to point latter components
    # to the result of this build.
    extra_config="$extra_config \
        --with-mpfr-include="$source_dir"/mpfr/src \
        --with-mpfr-lib="$build_dir"/mpfr/src/.libs"

    "$source_dir"/configure --prefix=$PREFIX --disable-nls \
        $TARGET_GCC_CONFIG $extra_config
    make
    make DESTDIR="$DESTDIR" install
}

build_linux_api_headers() {
    source_dir="$BASE_DIR"/gits/linux
    build_dir="$BASE_DIR"/builds/linux-api-headers

    cd "$source_dir"
    assert_branch "baserock/build-essential"
    touch_tree

    rm -Rf "$build_dir" && mkdir -p "$build_dir"

    # We don't achieve a real out of tree build here :(
    make O="$build_dir" mrproper
    #make O="$build_dir" headers_check
    make O="$build_dir" INSTALL_HDR_PATH="$DESTDIR$PREFIX" headers_install
}

# build-essential-plus

build_gawk() {
    source_dir="$BASE_DIR"/gits/gawk
    build_dir="$BASE_DIR"/builds/gawk

    cd "$source_dir"
    assert_branch "baserock/build-essential"
    touch_tree

    rm -Rf "$build_dir" && mkdir -p "$build_dir"
    cd "$build_dir"

    $source_dir/configure --prefix=$PREFIX --host=$TARGET --disable-nls
    make
    make DESTDIR="$DESTDIR" install
}

build_make() {
    source_dir="$BASE_DIR"/gits/make
    build_dir="$BASE_DIR"/builds/make

    cd "$source_dir"
    assert_branch "baserock/build-essential"
    touch_tree

    rm -Rf "$build_dir" && mkdir -p "$build_dir"
    cd "$build_dir"

    $source_dir/configure --prefix=$PREFIX --host=$TARGET --disable-nls
    make
    make DESTDIR="$DESTDIR" install
}


## 1. Build a "cross-compiler"
#
# We always do two builds of the compiler for now; hopefully the rebootstrap
# process in Baserock will allow us to optimise this out when not
# cross-compiling (we will never be cross-compiling except when bootstrapping
# a new architecture).

DESTDIR="$TOOLCHAIN_DIR"
export CC="gcc"
export PATH="$TOOLCHAIN_DIR$PREFIX/bin":"$PATH"

build_binutils pass1
build_gcc pass1

if ! [ -x "$(which $TARGET-gcc)" ]; then
    echo "Missing $TARGET-gcc in PATH: something went wrong"
    exit 1
fi

# A hack so that we can build eglibc with a no-shared-libs gcc; libgcc_eh is
# referenced in the eglibc build, but its static version contains all of the
# necessary symbols anyway.
for f in `find "$TOOLCHAIN_DIR" -name libgcc.a`; do \
    EH="`echo "$f" | sed 's/libgcc/&_eh/'`" && if [ ! -e "$EH" ]; then
        ln -s libgcc.a "$EH";
    fi;
done

## 2. Build the actual intermediate build-essential chroot
##
DESTDIR="$CHROOT_DIR"
export CC=

# We don't want to customise the specs until we actually have eglibc installed.
specs_dir="$(dirname $($TARGET-gcc --print-libgcc-file-name))"
rm -f "$specs_dir/specs"

build_linux_api_headers
build_eglibc

# Passing --with-lib-path to our pass1 linker gives it the correct library
# search path, but this doesn't work for the startup files. The startup files
# (crt*.o) are searched for by gcc itself, and passed to ld as absolute paths
# if found or just filenames if not (in which case, ld will not search for
# them either because they are .o files, not libraries).
#
# One alternative to this hack is to pass -B $CHROOT_DIR$PREFIX/lib to gcc, but
# that option often gets eaten by libtool and by gcc's nested configure
# scripts so it's not fully effective.
$TARGET-gcc -dumpspecs | \
    sed -e "s@\(crt1\|gcrt1\|Scrt1\|crti\|crtn\)\.o@$CHROOT_DIR$PREFIX/lib/&@g" \
    > "$specs_dir/specs"

if [ "$PREFIX" != "/usr" ]; then
    # eglibc (in sysdeps/unix/sysv/linux/configure) puts some files in /lib64
    # if PREFIX is /usr, so for now we go with this and avoid doing any fixups.
    # For other prefixes, everything is installed correctly into the sysroot.
    #
    # In the future we should fix eglibc to put all files into PREFIX/lib at
    # which point this fix will always be necessary, until GCC grows a better
    # way to specify the location of ld.so.
    sed -i "$specs_dir/specs" -e "s@/lib\(64\)\?/ld@$PREFIX/&@g"
fi

fix_chroot_for_target

echo
echo "Testing pass 1 compiler and chroot ..."

cat <<EOF > $CHROOT_DIR/build-test.c
#include <stdio.h>
int main() { printf("OK"); return 0; }
EOF
$TARGET-gcc $CFLAGS "$CHROOT_DIR/build-test.c" -o "$CHROOT_DIR/build-test"

if [ "$PREFIX" != "/usr" ]; then
    # Check that we successfully forced use of the ld.so inside the sysroot
    if ! readelf -l "$CHROOT_DIR"/build-test | grep -q "interpreter: $PREFIX" ;
        then
        echo "Wrong interpreter in output of pass 1 C compiler"
        exit 1
    fi
fi

rm "$CHROOT_DIR/build-test" "$CHROOT_DIR/build-test.c"

export CC=$TARGET-gcc
export AR=$TARGET-ar
export RANLIB=$TARGET-ranlib

build_binutils pass2
build_busybox

# This is hardcoded into gcc as NATIVE_SYSTEM_HEADERS. It's used only when
# running fixincludes, which only makes sense when cross-compiling anyway.
# Compilation fails if this directory is missing anyway, unfortunately.
created_native_system_header_dir="no"
if [ ! -d /usr/include ]; then
    mkdir -p /usr/include
    created_native_system_header_dir="yes"
fi

build_gcc pass2

if [ ! -e "$DESTDIR$PREFIX/bin/cc" ]; then
    ln -s gcc "$DESTDIR$PREFIX/bin/cc"
fi

if [ "$created_native_system_header_dir" == "yes" ]; then
    rm /usr/include
    rm /usr
fi

build_gawk
build_make

echo
echo "Complete! You now have a build-essential for $TARGET in "
echo "$CHROOT_DIR."
echo
echo "Before it can be used as a staging filler for Morph, you need to "
echo "manually run the install-commands from the 'fhs-dirs' chunk as root to "
echo "set up the necessary files and device nodes."