<Edit>I have figured out how to get Mono building in the Android build environment fairly easily. Click here for the updated instructions.</Edit>
As I mentioned in previous posts, I managed to get Mono compiled and running under Debian running side by side with Android. I built Mono "natively" by setting up the Debian environment in the emulator. For the sake of having all the instructions in one place on how to set up a native build environment under Debian, I will be consolidating and tweaking the directions from Saurik's post on how to set up Debian on the G1.
Update 1: If you want an installable Mono APK, which is trimmed down to bare minimum dependencies (3MB) and works on non rooted phones, click here or get it off Market. I have updated the instructions below to reflect the configuration and steps I took to build Mono to create this APK.
Update 0: If you just want a premade SD card image for your phone, skip to the very end of this post about deploying Mono to a real phone and follow the directions from there.
Setting up a Native Build Environment under Debian
If you follow the instructions to build Mono for ARM, you will fail miserably. Doing native builds on a G1 is not an option: the hardware is slow, runs out of memory, and just becomes unstable and unresponsive. Ultimately the watchdog timer kicks in and restarts the phone. However, the emulator does not have this issue. It runs faster than a G1, and you can tweak the qemu options to allocate extra RAM. So, the emulator makes a perfect "native" build environment!
As I mentioned, Mono will run under Debian. The Debian installation will be run off the SD card, contained in an ext2 image file. We will set up the ext2 image in the emulator, build Mono, and then transfer the image to a real phone.
Go to a directory somewhere and create a workspace for the installation:
mkdir monoimage
cd monoimage
Set up the SD card image to be used by the emulator by using the mksdcard command that comes with the Android SDK:
# use whatever size you wish
mksdcard 800MB sdcard.img
Build the Debian image as per the instructions in Saurik's post:
sudo apt-get install debootstrap
# use a size smaller than your SD card, must be less than 4GB as that is the max file size for FAT
dd if=/dev/zero of=debian.img seek=749999999 bs=1 count=1
mkefs -F debian.img
sudo mount -o loop debian.img debian
sudo debootstrap --verbose --arch armel --foreign lenny debian http://ftp.de.debian.org/debian
sudo umount debian
Now create the Debian kit on the SD card:
mkdir sdcard
sudo mount -o loop sdcard.img sdcard
sudo mkdir sdcard/kit
sudo cp debian.img sdcard/kit
rm debian.img # don't need this anymore!
# this script sets up the debian mount and gets you a bash prompt
wget http://koushikdutta.blurryfox.com/MonoWalkthrough/bash.sh
sudo cp bash.sh sdcard/kit
# this unmounts the debian image
wget http://koushikdutta.blurryfox.com/MonoWalkthrough/umount.sh
sudo cp umount.sh sdcard/kit
# busybox. handy tools...
wget http://koushikdutta.blurryfox.com/MonoWalkthrough/busybox
sudo cp busybox sdcard/kit
sudo umount sdcard
Time to start the emulator with SD card image we created. However, before we can can do that, we need to make a quick change. The emulator does not support ext2 out of the box, but that can be fixed by recompiling the kernel. I have a prebuilt emulator kernel available for download. (For further do it yourself instructions on how to compile the Android kernel manually, check out Motz's instructions. After following his instructions, you can use "make menuconfig" to tweak the kernel build options and then use "make" to build it.)
# get the custom emulator kernel
wget http://koushikdutta.blurryfox.com/MonoWalkthrough/zImageExt2
Ok, we're set now! Start her up!
# the last 2 arguments tell qemu to provide 512MB of virtual RAM to the emulator
emulator -sdcard sdcard.img -kernel zImageExt2 -qemu -m 512
Now, let's get into a shell on the emulator to finalize the Debian installation:
# This will get you to a "sh" instance on the emulator. Let's get a real bash prompt next.
adb shell
# Ignore any errors you may have with this command!
#You should see "I have no name!@localhost:/#" as your bash prompt after executing this.
. /sdcard/kit/bash.sh
/debootstrap/debootstrap --second-stage
echo 'deb http://ftp.de.debian.org/debian lenny main' > /etc/apt/sources.list
echo 'nameserver 4.2.2.2' > /etc/resolv.conf
# Let's restart the prompt to get rid of that "I have no name!" problem.
exit
# Ignore any errors again. You should get a "localhost:/#" bash prompt now.
. /sdcard/kit/bash.sh
Building Mono
Whew! You now have a full Debian distribution on the emulator. Now that we're set up, let's get the ARM portion of Mono built. But first, let's grab all the updates, dependencies, and tools we will need to build it.
apt-get update
apt-get install bison libglib2.0-0 libglib2.0-dev bzip2 gcc make
cd ~
# The following URL is the trunk snapshot at the time of writing this entry.
# Go to http://mono.ximian.com/monobuild/snapshot/sources-trunk/ to get the latest snapshot.
wget http://mono.ximian.com/monobuild/snapshot/snapshot_sources/mono/mono-122990.tar.bz2
tar xvf mono-12290.tar.bz2
cd ~/mono-122990
wget http://anonsvn.mono-project.com/viewvc/trunk/mono/eglib.tar.gz?view=tar
# wget mangles the file name...
tar xvf eglib.tar.gz?view=tar
And now we can finally begin the native build process. First let's set up some environment variables.
# CPPFLAGS=-DARM_FPU_VFP is used because the G1 supports VFP.
# Also, tell the linker where the runtime linker is found, and the path to the libraries.
export 'CFLAGS=-DARM_FPU_NONE -DPLATFORM_ANDROID -Wl,-rpath,.,-rpath,/data/data/com.koushikdutta.mono/assets/lib,--dynamic-linker,/data/data/com.koushikdutta.mono/assets/lib/ld-linux.so.3'
export CPPFLAGS=$CFLAGS
# We need to set the linker flags specifically as well...
export 'LDFLAGS=-Wl,-rpath,.,-rpath,/data/data/com.koushikdutta.mono/assets/lib,--dynamic-linker,/data/data/com.koushikdutta.mono/assets/lib/ld-linux.so.3'
# --disable-mcs-build configures it not to build the managed assemblies.
# Managed assemblies will be built on the host.
# --with-tls-=pthread changes how thread local storage is implemented.
# The default __thread does not work on Android.
# --disable-shared-handles is used because the G1 kernel does not have SYSVIPC enabled.
# --with-static-mono=no forces mono to dynamically link to libmono to save space.
# --with-glib=embedded embeds a trimmer version of glib into Mono.
# --prefix and --exec-prefix inform Mono of where it's binaries, libraries, and assemblies are found.
./configure --disable-mcs-build --with-tls=pthread --disable-shared-handles --with-static_mono=no --with-glib=embedded --prefix=/data/data/com.koushikdutta.mono/assets --exec-prefix=/data/data/com.koushikdutta.mono/assets
# This will take a couple hours... make a pot of coffee or 6.
make
Note that we have only built the Mono runtime at this point. We need to build the managed code portion now. First let's exit the emulator cleanly and unmount the ext2 image cleanly:
# Exit bash
exit
# Unmount the ext2 image
. /sdcard/kit/umount.sh
# Exit adb shell
exit
You can now close the emulator and remount the SD card on your host PC. You should be in the monoimage directory you created earlier.
sudo mount -o loop sdcard.img sdcard
sudo mount -o loop sdcard/kit/debian.img debian
Get the Mono sources again and build them on the host:
wget http://ftp.novell.com/pub/mono/sources/mono/mono-2.0.1.tar.bz2
tar xvf mono-2.0.1.tar.bz2
cd mono-2.0.1
./configure --with-glib=embedded --prefix=/data/data/com.koushikdutta.mono/assets --exec-prefix=/data/data/com.koushikdutta.mono/assets
make
So, both portions of Mono have been built. Let's install the managed portion directly to the ext2 image.
sudo make install DESTDIR=/full/path/to/monoimage/data/data/com.koushikdutta.mono/assets
Unmount the images:
sudo umount debian
sudo umount sdcard
Restart the emulator to finalize the Mono installation (this will overwrite the PC runtime that was installed):
emulator -sdcard sdcard.img -kernel zImageExt2 -qemu -m 512
. /sdcard/kit/bash.sh
cd ~/mono-2.0.1
make install
Mono should now be installed and functional! Let's test it:
cd ~
wget http://koushikdutta.blurryfox.com/MonoWalkthrough/hello.cs
# gmcs IS a managed executable; and obviously quite a complex one at that.
# If it succeeds. Then Mono is working... quite well.
gmcs hello.cs
# Hello World!
mono hello.exe
Before we transfer the Debian image to a real phone, let's shut down gracefully again:
# Exit bash
exit
# Unmount the ext2 image
. /sdcard/kit/umount.sh
# Exit adb shell
exit
Putting Android onto a Real Phone
If you just want a a premade SD card image that contains a working Debian install and Mono runtime, click here. Unzip the sdcard.zip and follow the instructions below to transfer the debian.img (which is contained in the sdcard.img) to your phone.
Your phone must be rooted and have the latest version of modified RC30 (1.3+) . Connect your phone and copy the kit directory from the SD card image to your real SD card. You should preferably do this by mounting your phone's SD card.
# Mount the SD card image first so we can access the kit.
sudo mount -o loop sdcard.img sdcard
# It is actually much faster to mount your phone's SD card as a removable disk on your
# computer and copy the kit directory over from the sdcard.img to the real SD card.
# It is also less likely to be corrupted as well. Don't use adb push unless you really want to.
adb push sdcard/kit /sdcard/kit
Now, go to a bash prompt on your phone and you will be able to run Mono!
adb shell
. /sdcard/kit/bash.sh
cd ~
mono hello.exe
What Now?
Well, now Mono is running on Android, side by side with Debian. However, attempting to invoke it from outside of a chroot /bin/bash will result in a "not found" error. This is because Mono is not configured to use Android's linker (/system/bin/linker), and looks for it at "/lib/linux-ld.so.3". You can fix that by doing the unionfs trick that Saurik posted about in his blog.
However, this process still requires the phone to have root, and have a Debian installation. For this to really be viable, Mono would need to be compiled statically, with all its dependencies. And I have no idea how to do that, yet. If someone does, please let me know!