Examples of embedding CHICKEN

Minimal example of embedding CHICKEN in a regular C program

From the CHICKEN-users mailing list, a post by Felix.

This shows a minimal example of embedding (you don't need CHICKEN_initialize, unless you want to set specific buffer sizes):

/* gcc x.c -lchicken -o x */
#include <chicken.h>

int main()
{
 C_word x;

 CHICKEN_run(CHICKEN_default_toplevel);
 CHICKEN_eval_string("(print (+ 3 4))", &x);
 return 0;
}

Embedding CHICKEN in an Android application

A quick way to make sense of all the moving parts is to start by copying samples/hello-jni from your NDK directory. Then you can alter hello-jni.c so it looks like this:

#include <string.h>
#include <jni.h>
#include <chicken.h>

jstring
Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,
                                                  jobject thiz )
{
  C_word res;
  char foo[100];

  CHICKEN_run(CHICKEN_default_toplevel);
  CHICKEN_eval_string("(+ 2 5)", &res);
  snprintf(foo, 100, "Evaluation result: %d", C_unfix(res));
  return (*env)->NewStringUTF(env, foo);
}

Then, build CHICKEN using PLATFORM=android for your target architecture. You'll have to build a libchicken.so for each architecture you want to support. For simplicity, let's say for now that we only want to build for armeabi (which has no apply-hack at the time of writing, so we disable autodetection via "ARCH="):

 $ make PLATFORM=android ARCH= C_COMPILER='/path/to/ndk/toolchains/arm-linux-androideabi-4.6/prebuilt/linux-x86/bin/arm-linux-androideabi-gcc --sysroot=/path/to/ndk/platforms/android-16/arch-arm'

The above is according to "Invoking the compiler (the hard way)" from the docs/STANDALONE-TOOLCHAIN.html in the NDK's root. The easy way involves more steps and is harder to explain here. See the aforementioned document for more information.

In your copy of the sample program, alter jni/Application.mk to read APP_ABI := armeabi. Copy libchicken.so to jni/chicken/armeabi and copy chicken.h to jni/chicken/include.

Then, create a file jni/chicken/Android.mk containing the following:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := chicken
LOCAL_SRC_FILES := $(TARGET_ARCH_ABI)/libchicken.so
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include

include $(PREBUILT_SHARED_LIBRARY)

Changejni/Android.mk so that the application will depend on libchicken, and pull in the new Makefile so it knows how to build it:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := hello-jni
LOCAL_SRC_FILES := hello-jni.c
LOCAL_SHARED_LIBRARIES := chicken

include $(BUILD_SHARED_LIBRARY)

include $(LOCAL_PATH)/chicken/Android.mk

Finally, modify com/example/hellojni/HelloJni.java to load the chicken module before hello-jni is loaded:

    static {
        System.loadLibrary("chicken");
        System.loadLibrary("hello-jni");
    }

You're all set!

To build, run /path/to/ndk/ndk-build and then ant debug to package up the program.

Extending this build to other ABI versions is simple: just add it to Application.mk and build different libchicken.so versions and install them under the correspondingly named subdirectory under jni/chicken/<ABI-NAME>/libchicken.so.