chickadee » chicken » cross-development

Cross Development

Since CHICKEN generates C code, it is relatively easy to create programs and libraries for a different architecture than the one the compiler is executing on, a process commonly called cross compiling. Basically you can simply compile Scheme code to C and then invoke your target-specific cross compiler. To automate the process of invoking the correct C compiler with the correct settings and to simplify the use of extensions, CHICKEN can be built in a special "cross-compilation" mode.

Note: in the following text we refer to the "target" as being the platform on which the software is intended to run in the end. We use the term "host" as the system that builds this software. Others use a different nomenclature or switch the meaning of the words.

Preparations

Make sure you have a cross-toolchain in your PATH. In this example, a Linux system is used to generate binaries for an ARM based embedded system.

Building the target libraries

First you need a version of the runtime system (libchicken), compiled for the target system. Obtain and unpack a tarball of the CHICKEN sources, or check out the code from the official code repository, then build the libraries and necessary development files:

make ARCH= \
    PREFIX=/usr \
    PLATFORM=linux \
    HOSTSYSTEM=arm-none-linux-gnueabi \
    DESTDIR=$HOME/target \
    TARGET_FEATURES="-no-feature x86 -feature arm" \
    install

This will build CHICKEN and install it in ~/target, which we use as a temporary place to store the target files. A few things to note:

(cond-expand
  (x86 <do this ...>)
  ...)

You should now have these files on ~/target:

|-- bin
|   |-- chicken
|   |-- chicken-bug
|   |-- chicken-install
|   |-- chicken-profile
|   |-- chicken-status
|   |-- chicken-uninstall
|   |-- csc
|   `-- csi
|-- include
|   |-- chicken-config.h
|   `-- chicken.h
|-- lib
|   |-- chicken
|   |   `-- 9
|   |       :
|   |
|   |-- libchicken.a
|   |-- libchicken.so -> libchicken.so.9
|   `-- libchicken.so.9
`-- share
    |-- chicken
    |   |-- doc
    :   ;   :
    |   |
    |   `-- setup.defaults
    `-- man
        `-- man1
            :

You should now transfer ~/target/usr/lib to the target system, and place its contents in /usr/lib. You may want to omit the static library libchicken.a if the target memory is limited.

Building the "cross chicken"

Next, we will build another chicken, one that uses the cross C compiler to generate target-specific code that uses the target-specific runtime library we have just built.

Again, unpack a CHICKEN release tarball or a source tree and run make(1) once again:

make PLATFORM=linux \
    PREFIX=$HOME/cross-chicken \
    TARGETSYSTEM=arm-none-linux-gnueabi \
    PROGRAM_PREFIX=arm- \
    TARGET_PREFIX=$HOME/target/usr \
    TARGET_RUN_PREFIX=/usr \
    install

In ~/cross-chicken, you should find the following:

|-- bin
|   |-- arm-chicken
|   |-- arm-chicken-install
|   |-- arm-chicken-profile
|   |-- arm-chicken-status
|   |-- arm-chicken-uninstall
|   |-- arm-csc
|   `-- arm-csi
|-- include
|   |-- chicken-config.h
|   `-- chicken.h
|-- lib
|   |-- chicken
|   |   `-- 9
|   |       :
|   |
|   |-- libchicken.a
|   |-- libchicken.so -> libchicken.so.9
|   `-- libchicken.so.9
`-- share
    |-- chicken
    |   |-- doc
    :   ;   :
    |   |
    |   `-- setup.defaults
    `-- man
        `-- man1
            :

To make sure that the right C compiler is used, we ask arm-csc to show the name of the cross C compiler:

 % ~/cross-chicken/bin/arm-csc -cc-name
 arm-none-linux-gnueabi-gcc

Looks good.

Special notes for Linux to Windows cross development

To cross compile from Linux to Windows, you will need to use a Linux build of MingGW-w64 with you can find in most distribution.

As far as the runtime is concerned, the procedure is the same that what have been shown before, using the platform name cross-linux-mingw.

The procedure to compile the compiler however require some more care:

In order to compile the C sources that Chicken will produce you have to make sure that you provide the right toolchain in both runtime and compiler steps.

In most cases, you want to do that by setting HOSTSYSTEM when building the runtime and TARGETSYSTEM when building the compiler to something similar to x86_64-w64-mingw32 (check you distribution binaries). For example:

# build a CHICKEN runtime for windows
> make PREFIX=$HOME/target PLATFORM=cross-linux-mingw HOSTSYSTEM=x86_64-w64-mingw32 install
> make confclean
# build a CHICKEN cross-compiler that uses the runtime above
> make PREFIX=$HOME/cross PROGRAM_PREFIX=mingw- TARGET_PREFIX=$HOME/target PLATFORM=linux TARGETSYSTEM=x86_64-w64-mingw32 TARGET_LIBRARIES="-lm -lws2_32" install

If your distribution does not stick to the PREFIX-TOOLNAME convention you may want to set some following variables (runtime step/compiler step):

You may need this trick to pick x86_64-w64-mingw32-gcc-posix over x86_64-w64-mingw32-gcc-win32 in Debian for example (those correspond to different thread APIs).

Using it

Compiling simple programs

 % ~/cross-chicken/bin/arm-csc -v hello.scm
 /home/felix/cross-chicken/arm-cross-chicken/bin/arm-chicken hello.scm -output-file hello.c -quiet
 arm-none-linux-gnueabi-gcc hello.c -o hello.o -c -fno-strict-aliasing -DHAVE_CHICKEN_CONFIG_H -g -Wall \
   -Wno-unused -I /home/felix/cross-chicken/arm-chicken/include
 rm hello.c
 arm-none-linux-gnueabi-gcc hello.o -o hello -L/home/felix/cross-chicken/arm-chicken/lib  -Wl,-R/usr/lib -lm \
   -ldl -lchicken
 rm hello.o

Is it an ARM binary?

 % file hello
 hello: ELF 32-bit LSB executable, ARM, version 1 (SYSV), for GNU/Linux 2.6.16, dynamically linked (uses shared libs), not stripped

Yes, looks good.

Compiling extensions

By default, the tools that CHICKEN provides to install, list and uninstall extensions will operate on both the host and the target repository. So running arm-chicken-install will compile and install the extension for the host system and for the cross-target. To selectively install, uninstall or list extensions for either the host or the target system use the -host and -target options for the tools.

For example, using the mingw-cross-compiler from above, we can create a standalone Windows exe file that includes the CHICKEN runtime and all egg dependencies.

# install an egg using the mingw cross compiler above
> $HOME/cross/bin/mingw-chicken-install fmt
> cat hello.scm
(import fmt chicken.platform)
(fmt #t "hello world from " (software-type) nl
     (pretty (features)))
# create a standalone exe file with the CHICKEN and fmt statically linked
> $HOME/cross/bin/mingw-csc -static hello.scm
# hello.exe should now run on Window without any runtime DLL dependencies:
> x86_64-w64-mingw32-objdump -p hello.exe | grep "DLL Name"
   DLL Name: KERNEL32.dll 
   DLL Name: msvcrt.dll 
   DLL Name: USER32.dll
   DLL Name: WS2_32.dll
# or using wine
> wine hello.exe
hello world from windows
(#:windows            #:64bit              #:cross-chicken
 #:ptables            #:dload              #:little-endian
 #:x86-64             #:gnu                #:mingw32
 ...)

"Target-only" extensions

Sometimes an extension will only be compilable for the target platform (for example libraries that use system-dependent features). In this case you will have to work around the problem that the host-compiler still may need compile-time information from the target-only extension, like the import library of modules. One option is to copy the import-library source file into the repository of the host compiler:

# optionally, you can compile the import library:
# ~/cross-chicken/bin/arm-csc -O3 -d0 -s target-only-extension.import.scm
cp target-only-extension.import.scm ~/cross-chicken/lib/chicken/9

Final notes

Cross-development is a very tricky process - it often involves countless manual steps and it is very easy to forget an important detail or mix up target and host systems. Also, full 100% platform neutrality is hard to achieve. CHICKEN tries very hard to make this transparent, but at the price of considerable complexity in the code that manages extensions.


Previous: Deployment

Next: Bugs and limitations