Kodi for Android: Building and Using Binary Python Extensions - Part 2

 Nov. 17, 2016     0 comments

This is the 2nd article in my series about building and using binary Python modules in Kodi on Android. In the previous article I described prerequisites for building such modules. In this article I will cover building a simple Python/C extension module for Python in Kodi for Android.

A Simple Python/C Module

This is a very simple "Hello World" type module written in C using a vanilla Python/C API. I took the code from some online tutorial and modified it a bit. It does not have any dependencies except for Python itself and is well suited for my demonstration purposes. Here is the code (hello.c):

#include <Python.h>

static PyObject* get_hello(PyObject* self, PyObject* args)
{
    return Py_BuildValue("s", "Hello World!");
}

static PyMethodDef HelloMethods[] =
{
     {"get_hello", get_hello, METH_VARARGS, "Greet somebody."},
     {NULL, NULL, 0, NULL}
};

PyMODINIT_FUNC inithello(void)
{
     (void) Py_InitModule("hello", HelloMethods);
}

This module contains and exposes a single get_hello function that returns 'Hello World!' Python string. As you can see, the code is quite verbose and rather cryptic for those who are not familiar with Python/C API internals. Later I will demonstrate you how to write the same function in more clear way using convenience C++ libraries: Boost.Python and Pybind11.

Android NDK Project

Our NDK project has the following structure:

\hello_project\
  \jni\
  \src\
  \include\
  \lib\

The folder \jni contain NDK build configuration. This is the only mandatory folder. Other folders may be organized as you like.

The \src folder contains our source code. In our case it's one hello.c file.

The \include folder contains additional C header files. In this case we need only Python 2.6 headers modified as described in the previous article.

The \lib folder contains additional libraries to link against. In this case we need only libkodi.so library from Kodi for Android that contains Python symbols.

NDK build configuration consist of 2 files that you need to put inside \jni subfolder: Application.mk and Android.mk.

The Application.mk contains general build options and can be re-used between your projects. Here's the Application.mk file for our example project:

NDK_TOOLCHAIN_VERSION := 5 # GCC version
APP_OPTIM := release  
APP_ABI := armeabi-v7a # Define the target architecture to be ARM.
APP_CFLAGS := -std=c99
APP_CPPFLAGS := -frtti -fexceptions -std=c++11
APP_PLATFORM := android-19 # Define the target Android version of the native application.
APP_STL := gnustl_static
APP_LIBCRYSTAX := static
APP_CPPFLAGS += -DANDROID

It includes some C++ options that are not used in our simple C-based example but, as I've said, it's a general configuration. And here's our Android.mk file:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE    := hello  # name your module here.
LOCAL_SRC_FILES := ../src/hello.c
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include/Python2.6
LOCAL_LDLIBS := -L$(LOCAL_PATH)/../lib -lkodi
include $(BUILD_SHARED_LIBRARY)

This file contains build options specific to our project. Brief explanation for the options:

  • LOCAL_MODULE: the name of our library file ('lib' prefix will be pre-pended).
  • LOCAL_SRC_FILES: a space-separated list of source C files
  • LOCAL_C_INCLUDES: a space-separated list of directories containing header files used in our project.
  • LOCAL_LDLIBS: link flags. Strictly speaking, Android NDK requires you to defile each library dependency as a separate build module, but using link flags is less verbose. ndk-build will issue a warning about this that you can safely ignore.

Now go to our project directory and run ndk-build command there (it must be added to your system PATH variable). I there are no issues, ndk-build will build our extension module libhello.so and place it into \libs subfolder created in our project folder.

However, this is not the end of the story. The problem is that import mechanism for binary Python modules in Kodi on Android is seriously broken and you cannot simply use import hello or even __import__('hello'). Fortunately, guys from the unofficial Russian Kodi forum have found a magic trick that allows to import binary modules in Python addons for Kodi on Android. I will tell you about this trick in my 3rd article.

  AndroidCC++KodiPluginPython