diff --git a/maintainers/docs/cross.txt b/maintainers/docs/cross.txt new file mode 100644 index 000000000000..f4c128bad4c9 --- /dev/null +++ b/maintainers/docs/cross.txt @@ -0,0 +1,265 @@ +Setting up a cross compiler with Nix + +"Cross compilation" means compiling a program on one machine for another +type of machine. A typical use of cross compilation is to compile programs +for embedded devices. These devices often don't have the computing power +and memory to compile programs natively. + +For a fully working cross compiler the following are needed: + +* cross binutils: assembler, archiver, linker, etcetera that understand +the format of the target system + +* cross compiler: a compiler that can generate binary code and object files +for the target platform + +* cross C library: a library to link object files with to create fully +functional programs + +Cross compilers are difficult to set up. A lot of people report that they +cannot succeed in building a cross toolchain successfully. The answers +usually consist of "download this pre-built toolchain", which is equally +unhelpful. + +A toolchain is set up in five steps: + +1. build binutils to that can run on the host platform, but generate code +for the target platform + +2. build Linux kernel headers for the target platform + +3. build a minimal C only version of GCC, that can run on the host platform +and generate code for the target platform + +4. build a C library for the target platform. This includes the dynamic +linker, C library, etc. + +5. build a full GCC + +**** +NB: + +Keep in mind that many programs are not very well suited for cross +compilation. Either they are not intended to run on other platforms, +because the code is highly platform specific, or the configuration proces +is not written with cross compilation in mind. + +Nix will not solve these problems for you! +*** + +This document describes to set up a cross compiler to generate code for +arm-linux with uClibc and runs on i686-linux. The "stdenv" used is the +default from the standard Nix packages collection. + +Step 1: build binutils for arm-linux in the stdenv for i686-linux + +--- +{stdenv, fetchurl, noSysDirs}: + +stdenv.mkDerivation { + name = "binutils-2.16.1-arm"; + builder = ./builder.sh; + src = fetchurl { + url = http://ftp.nluug.nl/gnu/binutils/binutils-2.16.1.tar.bz2; + md5 = "6a9d529efb285071dad10e1f3d2b2967"; + }; + inherit noSysDirs; + configureFlags = "--target=arm-linux"; +} +--- + +This will compile binutils that will run on i686-linux, but knows the +format used by arm-linux. + +Step 2: build kernel headers for the target architecture + + default.nix for kernel-headers-arm: + +--- +{stdenv, fetchurl}: + +assert stdenv.system == "i686-linux"; + +stdenv.mkDerivation { + name = "linux-headers-2.6.13.4-arm"; + builder = ./builder.sh; + src = fetchurl { + url = http://www.kernel.org/pub/linux/kernel/v2.6/linux-2.6.13.4.tar.bz2; + md5 = "94768d7eef90a9d8174639b2a7d3f58d"; + }; +} +--- + + builder.sh for kernel-headers-arm: + +--- +source $stdenv/setup + + +buildPhase() { + make include/linux/version.h +} + +buildPhase=buildPhase + + +installPhase() { + mkdir $out + mkdir $out/include + #cd $out/include + #ln -s asm-arm asm + make include/asm ARCH=arm + cp -prvd include/linux include/asm include/asm-arm include/asm-generic $out/include + echo -n > $out/include/linux/autoconf.h +} + +installPhase=installPhase + + +genericBuild +--- + +Step 3: build a minimal GCC + +Extra/different parameters include the target platform and the kernel +headers argument (this needs a major cleanup, as well as the name, it +needs to be different!). Profiled compilers are disabled. The tarball +used here is just gcc-core. For some reason it doesn't install nicely +if the whole tarball is used (or is this some braino on my side? -- AH). + +Only C is used, because for other languages (such as C++) extra libraries +need to be compiled, for which libraries compiled for the target system +are needed. + +--- +{ stdenv, fetchurl, noSysDirs +, langC ? true, langCC ? true, langF77 ? false +, profiledCompiler ? false +, binutilsArm +, kernelHeadersArm +}: + +assert langC; + +stdenv.mkDerivation { + name = "gcc-4.0.2-arm"; + builder = ./builder.sh; + src = fetchurl { + url = ftp://ftp.nluug.nl/pub/gnu/gcc/gcc-4.0.2/gcc-core-4.0.2.tar.bz2; + md5 = "f7781398ada62ba255486673e6274b26"; + #url = ftp://ftp.nluug.nl/pub/gnu/gcc/gcc-4.0.2/gcc-4.0.2.tar.bz2; + #md5 = "a659b8388cac9db2b13e056e574ceeb0"; + }; + # !!! apply only if noSysDirs is set + patches = [./no-sys-dirs.patch ./gcc-inhibit.patch]; + inherit noSysDirs langC langCC langF77 profiledCompiler; + buildInputs = [binutilsArm]; + inherit kernelHeadersArm binutilsArm; + platform = "arm-linux"; +} +--- + + +--- +source $stdenv/setup + + +export NIX_FIXINC_DUMMY=$NIX_BUILD_TOP/dummy +mkdir $NIX_FIXINC_DUMMY + + +if test "$noSysDirs" = "1"; then + + if test "$noSysDirs" = "1"; then + # Figure out what extra flags to pass to the gcc compilers + # being generated to make sure that they use our glibc. + if test -e $NIX_GCC/nix-support/orig-glibc; then + glibc=$(cat $NIX_GCC/nix-support/orig-glibc) + # Ugh. Copied from gcc-wrapper/builder.sh. We can't just + # source in $NIX_GCC/nix-support/add-flags, since that + # would cause *this* GCC to be linked against the + # *previous* GCC. Need some more modularity there. + extraCFlags="-B$glibc/lib -isystem $glibc/include" + extraLDFlags="-B$glibc/lib -L$glibc/lib -Wl,-s \ + -Wl,-dynamic-linker,$glibc/lib/ld-linux.so.2" + + # Oh, what a hack. I should be shot for this. + # In stage 1, we should link against the previous GCC, but + # not afterwards. Otherwise we retain a dependency. + # However, ld-wrapper, which adds the linker flags for the + # previous GCC, is also used in stage 2/3. We can prevent + # it from adding them by NIX_GLIBC_FLAGS_SET, but then + # gcc-wrapper will also not add them, thereby causing + # stage 1 to fail. So we use a trick to only set the + # flags in gcc-wrapper. + hook=$(pwd)/ld-wrapper-hook + echo "NIX_GLIBC_FLAGS_SET=1" > $hook + export NIX_LD_WRAPPER_START_HOOK=$hook + fi + + export NIX_EXTRA_CFLAGS=$extraCFlags + export NIX_EXTRA_LDFLAGS=$extraLDFlags + export CFLAGS=$extraCFlags + export CXXFLAGS=$extraCFlags + export LDFLAGS=$extraLDFlags + fi + +else + patches="" +fi + + +preConfigure=preConfigure +preConfigure() { + + # Determine the frontends to build. + langs="c" + if test -n "$langCC"; then + langs="$langs,c++" + fi + if test -n "$langF77"; then + langs="$langs,f77" + fi + + # Cross compiler evilness + ensureDir $out + ensureDir $out/arm-linux + ensureDir $out/arm-linux/bin + ln -s $binutilsArm/arm-linux/bin/as $out/arm-linux/bin/as + ln -s $binutilsArm/arm-linux/bin/ld $out/arm-linux/bin/ld + ln -s $binutilsArm/arm-linux/bin/ar $out/arm-linux/bin/ar + ln -s $binutilsArm/arm-linux/bin/ranlib $out/arm-linux/bin/ranlib + + # Perform the build in a different directory. + mkdir ../build + cd ../build + + configureScript=../$sourceRoot/configure + configureFlags="--enable-languages=$langs --target=$platform --disable-threads --disable-libmudflap --disable-shared --with-headers=$kernelHeadersArm/include --disable-multilib" +} + + +postInstall=postInstall +postInstall() { + # Remove precompiled headers for now. They are very big and + # probably not very useful yet. + find $out/include -name "*.gch" -exec rm -rf {} \; -prune + + # Remove `fixincl' to prevent a retained dependency on the + # previous gcc. + rm -rf $out/libexec/gcc/*/*/install-tools +} + + +#if test -z "$profiledCompiler"; then + #makeFlags="bootstrap" +#else + #makeFlags="profiledbootstrap" +#fi + +genericBuild +--- + +This is enough to compile a C library. In our case we take uClibc. It's +intended to be a small sized replacement for glibc. It is widely used in +embedded environments.