Thursday, January 10, 2013

How to port Mozilla SpiderMonkey 1.7 to Android

Another third-party package I needed to cross-compile for Android was Mozilla's SpiderMonkey 1.7 Javascript engine. I found two issues here:

  1. When configuring SpiderMonkey the makefile tries to build two executables (jscpucfg and jskwgen) and then runs them to generate two  configuration header files (jsautocfg.h and jsautokw.h, respectively). The problem when using the Android NDK cross-compiler is that these two executables can only be run on the Android target (for example an ARM processor), while I'm cross-compiling my build from a Linux Ubuntu 12.04 machine with an x86_64 processor architecture. So you get an error that you cannot execute these files on the host machine.
    I solved this problem by copying the two executables to an Android device (Samsung Galaxy S III) using scp and the SSHDroid application, and generating the two header files there:

    $ jscpucfg > jsautocfg.h
    $ jskwgen > jsautokw.h

    then I copied the two files back to my node on my Ubuntu machine and saved in the source trunk under the config sub-directory. Then I changed the makefile to skip generating these two header files when cross-compiling for Android and get them instead from the config sub-directory.
  2. The SpiderMonkey jsnum.c file generates the following error when cross-compiled for Android:

    js-1.7/jsnum.c: In function 'js_InitRuntimeNumberState':
    js-1.7/jsnum.c:578: error: 'struct lconv' has no member named 'thousands_sep'
    js-1.7/jsnum.c:578: error: 'struct lconv' has no member named 'thousands_sep'
    js-1.7/jsnum.c:580: error: 'struct lconv' has no member named 'decimal_point'
    js-1.7/jsnum.c:580: error: 'struct lconv' has no member named 'decimal_point'
    js-1.7/jsnum.c:582: error: 'struct lconv' has no member named 'grouping'
    js-1.7/jsnum.c:582: error: 'struct lconv' has no member named 'grouping'
    gmake[2]: *** [js-1.7.dir/jsnum.c.o] Error 1

    This is caused by the fact that the lconv structure in locale.h shipped with the Android NDK is stubbed with the following comment:

    #if 1 /* MISSING FROM BIONIC - DEFINED TO MAKE libstdc++-v3 happy */
    struct lconv { };
    struct lconv *localeconv(void);
    #endif /* MISSING */

    To solve this problem I applied the following patch to jsnum.c and I was able to successfully cross-compile SpiderMonkey 1.7 for Android.

    --- a/jsnum.c   2013-01-10 10:37:54.413800695 -0500
    +++ b/jsnum.c   2013-01-10 10:06:49.432752061 -0500
    @@ -573,13 +573,28 @@ js_InitRuntimeNumberState(JSContext *cx)
         u.s.lo = 1;
         number_constants[NC_MIN_VALUE].dval = u.d;
    -    locale = localeconv();
    -    rt->thousandsSeparator =
    -        JS_strdup(cx, locale->thousands_sep ? locale->thousands_sep : "'");
    -    rt->decimalSeparator =
    -        JS_strdup(cx, locale->decimal_point ? locale->decimal_point : ".");
    -    rt->numGrouping =
    -        JS_strdup(cx, locale->grouping ? locale->grouping : "\3\0");
    +    /* Copy locale-specific separators into the runtime strings. */
    +    const char *thousandsSeparator, *decimalPoint, *grouping;
    +    locale = localeconv();
    +    thousandsSeparator = locale->thousands_sep;
    +    decimalPoint = locale->decimal_point;
    +    grouping = locale->grouping;
    +    thousandsSeparator = getenv("LOCALE_THOUSANDS_SEP");
    +    decimalPoint = getenv("LOCALE_DECIMAL_POINT");
    +    grouping = getenv("LOCALE_GROUPING");
    +    if (!thousandsSeparator)
    +        thousandsSeparator = "'";
    +    if (!decimalPoint)
    +        decimalPoint = ".";
    +    if (!grouping)
    +        grouping = "\3\0";
    +    rt->thousandsSeparator = JS_strdup(cx, thousandsSeparator);
    +    rt->decimalSeparator = JS_strdup(cx, decimalPoint);
    +    rt->numGrouping = JS_strdup(cx, grouping);
         return rt->thousandsSeparator && rt->decimalSeparator && rt->numGrouping;

    Of course you would define HAVE_LOCALECONV only for regular builds but not for Android cross-compilations, so that you could either pass your own definitions for the locale thousands separator, decimal point or locale grouping via environment variables, or use the above defaults.

No comments:

Post a Comment