Cross-compiling Haskell

These patches (for GHC HEAD) would not have been possible without the first iphone-ghc patch by Stephen Blackheath. He did a really big work on that patch. So, anything smart in this new version is from Stephen and any problem is probably coming from me.

For lots of reasons, I decided to port Stephen's patch to the new GHC build system and do some cleaning (like adding compilation flags and makefile variables). When I started I did not know anything about this new build system and I was not aware that Cabal was so smart and doing so many things. So, what is emerging from this work is that I absolutely don't understand Cabal and only very partially the new build system. The patch will need lot of work to be clean and generic. Some problems were solved with ad'hoc assumptions likely to be wrong in different environments.

But, at least, it is building on Snow Leopard and Ubuntu and cross-compiling to iPhone, iPhone Simulator and OMAP3 platform. OMAP3 is the chip inside the new Motorola Droid, the Nokia N900 and the Palm Pre. So, GHC can now target some of the best mobile phones. Since the iPhone Simulator is in this list, it means I had to extend the meaning of cross compilation. The autoconf triplet is not enough to make a distinction between Snow Leopard and the iPhone Simulator environment so I finally adopted the Stephen's way of doing it by adding flags to configure.

Here is an highlight of the main changes. For more details, you'll have to look for IPHONE and GhcCrossCompile in the files. If something is not clear, ask and I'll improve this document (which obviously will have to be improved but I wanted to make a first release of the patch quickly).

1. CrossCompilation Folder

I have created a CrossCompilation folder containing some scripts, some configuration files and the build.mk file for each target. You'll have to customize a bit the OMAP3 one. Each subfolder is containing some configuration files that have to be generated on the target : DerivedConstants.h, GHCConstants.h (the same file but in Haskell format).

HsBaseConfig.h may not be needed but at the beginning of my experiment I had problems with this file and the one generated by configure was wrong. So, now it is just copied during the build and overwriting the one generated by configure. That's why you have the variable TARGET_CONFIG_DIR in the build.mk. To tell the build system where to find this file. The copy is done in rules/build-package-data.mk.

It is the first ugly hack. Don't worry. There will be many other ones.

DerivedConstants and GHCConstant are generated with mkDerivedConstants : a tool you'll find inside the includes folder of the distribution.

1.1. build.mk

Two new makefile variables are introduced : GhcCrossCompile and IPHONE.

CROSS_AR is needed because it was causing problem when relying only on the configure script. I am sure it could be cleaned and removed from the build.mk.

1.2. build.sh

It is the script to configure. Normally, once you have downloaded a darcs version of GHC and typed sh boot, you're ready to configure with something like sh CrossCompile/iPhoneDevice/build.sh for instance.

It will copy the needed target configuration files, copy the build.mk to the mk folder (because it is too easy to forget to do it) and set lot of new command line options for configure. Most should be obvious but not the last one : with-toolchain-name.

When using cabal-install and installing with --user, you don't want all binaries to be mixed in the same folder ~/.cabal/bin.

So, the customized cabal-install provided with this system is creating other .cabal folders : .cabal-iPhoneDevice for instance.

It is also important when building to the iPhoneSimulator since the autoconf triplet is the same but there are some differences in the generated code due to some iPhone specificities (explained later)

The only problem of this approach : you'll have to do a cabal update for each folder (cabal-iphone update, cabal-iphone-simulator-update, cabal-OMAP3 update etc ...)

2. compiler

iPhone is forbidding code generation at runtime. So, there is a problem for the adjustor used to implement the wrapper callbacks of the FFI. I reused the code from Stephen for this:

This iPhone port solves the problem by pre-compiling a pool of functions, and allocating from it.

If the pool is too small for a given application, you can increase it by using a {-# POOLSIZE x #-} pragma, which must appear after the "wrapper" token. e.g.

foreign import ccall safe "wrapper" {-# POOLSIZE 100 #-} 
   mkDelegate :: IO () -> IO (FunPtr (IO ()))

2.1. ghc.mk

This makefile is changed to introduce the new compilation flags.

The makefile is generating some configuration files included during the build. These files are containing different flags for compiling and linking on the chosen target. Instead of using the words function as initially done to convert the flags (a string) to an Haskell list, a commandLineOptions function is introduced for options containing a space (like -isysroot /....). The implementation of this function is very preliminary.

The TOOLCHAIN name is also introduced so that the compiler can find the packages in the .cabal-TOOLCHAIN folder by default

The build system is installing the phase 2 compiler but for the cross compiler only phase 1 is built. So, the dirty hack is to copy the phase 1 compiler to phase 2 destination to avoid having to change too many things in the install part of the make (that I don't understand very well)

2.2. Lexer.x.pp

Lexer.x is now generated from Lexer.x.pp to be able to take into account the new compilation flags

2.3. DriverPhases.hs

Now, ObjectiveC .m files can be used on the GHC command line

2.4. DriverPipeline.hs

On iPhone we want to use XCode. But, it is really annoying to have to add all Haskell libraries by hand to an XCode project (like libHSrts.a, libHSrts_debug_.a).

So, the compiler is changed to generate a .a library when building an executable ! This .a library is containing all needed Haskell librairies (not the case when you build a library with cabal for instance)

libtool is used for that but some options not recognized by libtool must be removed. It is done by the temporary removeDynamicallyLinkedLibrariesAndOptions function. Temporary because only covering the cases I had.

This patch is coming from Stephen who is adding : "libtool gives lots of warnings which can apparently be safely ignored. We filter libtool's output so the user doesn't see them".

2.5. main/Packages.lhs

TOOLCHAIN name is used to select the right .cabal directory when looking for packages

3. configure.ac

New options for configure have been added. In case ARM is the target, the size of primitive datatypes is forced.

A new ToolChain.hs file is generated in the Cabal folder

4. ghc.mk

The customized cabal-install is added to the list of tools to be built. This customized cabal-install must be installed in the utils folder.

haskeline removed (because not really tested by me)

5. ghc

Some hacks for "building" phase 2

6. rts

Lots of changes for the Adjustor on iPhone.

6.1. Linker.c

The RTS_SYMBOLS table had to be modified. createAdjustor replaced with iPhoneCreateAdjustor to be coherent with other parts of the code.

It was not present in Stephen's patch. So, I hope something is not wrong in the Adjustor code I got from Stephen's patch (missing file ?).

6.2. main.c

Using Haskell_main as entry point on iPhone because we'd like main to be the general entry point.

6.3. sm/GC.c

Parallel GC disabled

7. libffi

7.1. ghc.mk

Modified to cross compile (new options for the configure step)

8. mk

8.1. config.mk.in

GHCi, Interpreter disabled

Introducing new variables for cabal-install

Modifying the definition of GHC_STAGE2 and GHC_STAGE3 because we want to build with the host compiler

New makefile variables, corresponding to the new configure options, are introduced

8.2. tree.mk

New variables for the new cabal-install folder in utils

9. rules

9.1. build-package-data.mk

This is an ugly hack and the result of some experiments. I am not really understanding everything here.

The new inplace cabal is used to configure the packages. The boot packages used to build ghc need to be compiled with the host tools.

Other packages need to be built with the target tools. The configuration phase can involve some autoconf script and the generation of some C code compiled and linked to check dependencies.

Finally, during the build, hsc2hs is used to convert some files. It is generating C code which is executed on host to generate the Haskell file.

Due to all of these reasons, the configure step is taking a long list of options on the command line.

9.2. shell-wrapper.mk

Needed some changes to install the wrappers for some tools located at a different place than assumed by the original wrapper makefile

10. utils

10.1. Makefile

cabal-install added to the list of tools to build

10.2. ghc-pkg

Added the TOOLCHAIN name to select the right .cabal folder

10.3. cabal-install

I dropped the darcs version of cabal-install in this folder and customized it.

First, it is using the TOOLCHAIN name for .cabal folder.

The source was changed to be able to build directly with the new Cabal included in this GHC distribution (some changes for exception handling).

10.3.1. Distribution/Client/SetupWrapper.hs

Changed to build the Setup.hs file with the host ghc.

The Setup.hs is linked with the customized Cabal contained in this Ghc distribution. Hence the need to install it somewhere.

During the build of cabal-install, the customized cabal is also built : CustomCabal. When installing, it will be installed in a folder bootstrapping at same level as the other shared, bin and lib folders of the prefix you chose. It can't be in bin because bin is containing target binaries and this CustomCabal is for the host and cross-compilation.

Setup.hs will always be linked with this CustomCabal so that it can recognize the new options introduced for the configuration (target tools and host tools)

10.3.2. Distribution/Compat

The files in this folder are no more needed since the code was changed to link with the CustomizedCabal.

10.3.3. Main.hs

Configuration is calling configureAllKnownPrograms to force the loading of all default tools that were passed on the configure command line when building this GHC distribution. So, the default host and target tools. They will be passed to the setup configure step where setup has been built as explained above

10.3.4. ghc.mk

A bit ugly.

11. libraries

11.1. Cabal

Force GHCiLib to False. No .o library is ever generated nor installed for the cross compilation

Other changes are from Stephen's patch like the addition of a new ToolChain.hs file containing the settings for host and target

I may have changed a little the command line options passed to some tools to be able to configure and build on the several plaforms that I used for testing.

11.1.1. Distribution/Simple/Configure.hs

Add the default the target LDFlags to the configure step

12. What need to be improved

In short : A LOT !

First, testing must be improved but I need help for this. I am not sure the FFI is fully ok. I have had no problems on many tests but I have a project which is not working well. I don't yet know if the problem is really FFI related. I am still working on it.

Once testing is enough, the next step will be to clean this patch. I'll also need help. My knowledge of Cabal and the GHC build system is really too weak and I don't have enough free time.

Then, for future I hope we'll be able to use the LLVM codegen to generate ARM code and finally, a nice to have but not mandatory : Trac ticket 698. Indeed, on most smartphones, the swap is disabled and physical memory very constrained.

I have not tested the patches attached to this post :-( Sorry for that. So, in case I made a mistake, you can get the full source code from my Dropbox and for a LIMITED time only. I hope someone can host the source code somewhere.

To know how to configure XCode, refer to te original iphone-ghc patch. There is a PDF documentation. I'll release an XCode template when I have time.

13. How to build

Download the above source or a darcs version of GHC head (not tested with GHC 6.12).

sh boot

Customize a bit the build.mk and build.sh in the target subfolder of the CrossCompile folder

sh CrossCompile/Target/build.sh

make and cross fingers

make install

Tested on SnowLeopard with iPhone SDK 3.1 and on Ubuntu with OMAP development board and a GHC 6.10.4 to build.

14. Updates

14.1. 22 Nov 2009

Problem with Posix.getFileStatus detected. Probably a problem with hsc2hs not called with the right compilation flags. I am working on it ...

(This post was imported from my old blog. The date is the date of the import. The comments where not imported.)

blog comments powered by Disqus