Compiling native apps for Android explained
In my daily work (the one that pays the bills) I've been burdened with the
responsability to port two projects to the Android platform:
hop
, a programming language for the web
2.0/application server writen in Scheme
. For that I would need to also port
bigloo
, a Scheme
compiler. The
goal is to be able to run hop
apps in the phone itself.
I really didn't know much about the platform then, only that probably it would be a fun platform to have in your phone, being GNU/Linux based, open source and all. There was also the promise to play with these phones; being somewhat a technophile, that was definetely a plus. With a smile in my face, I cracked may hands, sat down and started... reading.
The first thing one learns when reading about developing for the platform is
that the language is Java
. There's a complete
SDK for developing them. If you
want to port some C/C++
library, you can do so with the
NDK, and that's pretty
much it. Now, from my point of view, this is very restricting: you cannot choose
your own programming language. Even if I like the language a bit (but not its
std library or the way that the language is driven), I prefer Python
or
whatever suits better the problem. But then, I understand Android/Google/OHA
needs to be control freaks with the apps running on the phone, because there are
limited resources and you cannot leave an application open forever. So, they
devised their component
lifecycle,
which reminds me of OSGi, made it in
Java
, made all the tools and that was it.
But in my case I needed a native port, and that's for two reasons. First, even
if bigloo
can produce Java
bytecode, and even when I've been told that
Dalvik
, their JVM
implementation, can run Java
bytecode1, the result is not fast enough, being
a both bigloo
and the JVM in turn problem. Certainly bigloo
's Java
output
might not be the best, but also the JVMs we tried were too heavy2. The second
and most important reason is that if we did a Java
port, we would be tied to
the component lifecycle I mentioned above. We need a hop
daemon running in
order to be able to run its apps. If this deamon goes off by the platform's
request3, there's no way to awake it again when a (web) client tries to make a
request4, so the request would fail and would make hop
useless. Hence, a
native port5.
I have to say in everybody's defense that this is my first contact with toolchains and cross compiling.
Given that native porting is not supported, I went off searching. The
first
posts talked about static
linking. This guy even pointed to a first
wrapper for the precompiled toolchain and gave some details on some CFLAGS
to
set. But static linking was not an option for us. hop
uses bigloo
's runtime
libs as dynlibs6.
Then I
hit Rob Savoye's
efforts to port gnash
. There
I started to fully understand the Android platform: «while the C compiler is ok,
the C++ compiler is crippled. No iostream support in libstdc++, no locales,
etc... Then to make it more fun they use their own libc called Bionic, which is
trimmed down for embedded devices». He has more details on C++
compilation
because he needed it (we don't... yet).
So I turned to what I knew, if it only was its name:
emdebian
. I was till a litle hazy about what cross
compiling meant, so I started to make questions. Thing is, emdebian
is coupled
with Debian
(it only makes sense), so the first step would have been creating
a Bionic
package. Of course, this was way beyond my requirements and
abilities. In that channel I was also pointed to a post by Harald
Welte,
which in turn links to a talk called "Android
Mythbusters",
where we learn also that the linking is not standard [s.6], some hacks in
the device support [s.7], hardcoded hotplug [s.8], no
tslib
support [s.12], hardcoded limits [s.13], and
a lot more of calamities, including bad community support [s.18], and its peak,
the
CyanogenMod-gate.
All that analisys is nice, but I was still stuck with no porting system. My boss
suggested scratchbox
, but just like emdebian
it only has support for (e)glibc
and uClibc
. It is possible to add support
for other platforms, but their script didn't work and my feeble attempts led me
no closer to a solution.
Then with a strike of luck I found Joel Reymont's Android
Notes, who not only
explains a little more the platform, but also has a example Makefile
for
compiling executables for Android. And then, just a few minutes later, Takuya
Murakami's wrapper, which, together
with the prebuilt toolchain in Android's source
code, made most of it. I finally got a
simple Hello World!
dynamic executable running in the emulator.
From here all was mostly downhill. The last stone in the path was a linking
problem. bigloo
uses Boehm's
gc
, a renowed garbage
collector used by several proyects, including mono
and ocalm
. gc
needs a
symbol called __stack_base__
defined, which, from what I could find looking,
used to be defined in crt0.o
, one of the parts that the compiler normally
attaches to executables to make them so. The problem is that this file is not in
the SDK or Android's source code/prebuilt toolchain. To be fair, that file is
not to be found in a fairly new distro. What I found was a reverse engineered
crt0.S
7, but it doesn't define the symbol I needed, and in any case I already got
running executables in the emulator. In the other hand,
naoya_t
seems to
have hit the same problem, but he writes in japanese and I couldn't get hold of
anyone to translate it for me8.
Long story short, I made a
hack
to make the linking happy. bigloo
finished compiling, I pushed it to the
emulator and... it worked!
Some notes about this port: with a little bit of luck it will be officially
announced next week, even if it works with a hack. Ivan Maidanski assures
me
that it should not be nessesary, so probably there's an error on our side
when configuring gc
; but then again, I spent more that 2 weeks with that
problem alone and I followed all the suggestions they gave us9, so I'm pretty
sure is something in the middle. Also, mono
has been ported to
Android, and even if I read references to
the NDK, I've been assured that it's a native port. If they could port it
without hacks, so should we10.
Lastly, some notes about the crippled GNU/Linux running behing the Android
platform: besides all the low level stuff mentioned by others cited above, there
are no cp
or tee
; ls
does not support the -R
option, mkdir
does not
support the -p
option, and even when one of these tools don't recognize an
option, instead of barking, it happily accepts it as another file to treat. so,
mkdir -p bongs/bonga
creates a -p
dir and barfs on bongs/bonga
. What it is
there, yes, are dd
and strace
! Not very surprisingly, there's no
/etc/passwd
, but then the system somehow knows several others users besides
root
and shell
, so I guess they're hardcoded somewhere. It's really
impressive the effort they put to cripple this thing. Most probably they're
using a tool like BusyBox
11, and if so, they are only saving some directory
entries in the case of cp
or tee
and wasting time in the other cases.
All in all, I'm dissapointed by the platform. I understand that what we're trying to do is not supported, and that alone tells me that I don't want an Android device until this has changed, if it ever does. Probably the fact that Android is (now) open source and that the Cyanogengate led to the formation of «the Open Android Alliance (not to be confused with the Open Handset Alliance) an organization whose stated goal is to distribute "a Flavor of Android that is fully customizable and does not rely on Google or other copyrights» gives the platform some hope.
Meanwile I will prefer Maemo
(who at least looks more
open), specially now that Qt
will be the default toolkit. I'll also keep an
eye on Bada
, Samsung's new platform. There are not
many details about this last one, except that they have their own toolkit in
C++, and from the API's
documentation, it looks like it's a very thorough
one.
-
but then the WikiPedia page says you have to convert the
.class
file in a.dex
file using a tool calleddx
, and that «It uses its own bytecode, not Java bytecode». ↩ -
but then
Dalvik
is supposed to be fast enough for the platform. ↩ -
«If an activity is paused [not having the focus] or stopped [run at some time but obscured completely], the system can drop it from memory either by asking it to finish (calling its finish() method), or simply killing its process». ↩
-
because instead of communicating via Android's component system, the communication is via a simple TCP socket. ↩
-
but probably we'll have to make at least a
Java
wrapper to start it. I'm even pondering making a "hop apps" app which would be aWebKit
widget and ahop
server runnning in the background. ↩ -
or in any case I would need to compile
bigloo
first dynamically to get the libs to compilehop
staticly. ↩ -
that blog has some more details about porting to Android. ↩
-
a chineese friend could read some, but his translation didn't make more sense than google's. babelfish just timed out on that page. ↩
-
the ones that made sense, anyways. ↩
-
when I was looking for anyone involved in
mono
's irc channel, I was told to look in the code. A simple “noone involved is here, try reaching them by mail” would have sufficed. Way to go, mono community! ↩ -
and if they're not, then I don't know what's going on in their heads. ↩