Best Practices for authors of libraries

This page describes a suggested standard way of packaging Android libraries.
If you wish to use such a packed library, please refer to Best Practices for users of libraries.

Why this document ?

Before we end up with the autoconf nightmare all over again, I would like to propose a unified way of packaging libraries so that FOSS softwares are easier to build for the Android platform.

Basically, these simple conventions use the existing mecanisms in the Android NDK to provide fonctionalities similar to those of pkg-config on regular Linux systems.

Suggested way of distributing Android Libraries

Versioning

Please choose an "API version number" such that you guarantee that if the API version does not change, softwares that used to build with previous releases will still build. If you do not wish to make such a guarantee, you can use the library version number as the API version number.

Consider an imaginary library libfoo version 2.3.4 . If my API version is 2.3, then when the 2.3.5 version is released, people can update and know their code will still build. It is OK to use 2.3.4 as the API version number, thus providing no guarantee.

Choosing a build system

You basically have two possibilities : you can either keep your existing build system (be it cmake, scons or whatever) or you can rely on the Android NDK build system (which is itself based on GNU Make).
  • I highly recommend that you use the Android NDK build system.
    It allows the user to rebuild all bundled libraries with a single command. It takes cares of a lot of things, like handling multiple architectures or debug release in a nice and consistent way for the user.
  • If you wish to keep your existing build system, you also can.
    <fud>Be prepared to face useless bug reports from angry users that will have failed to understand how to properly cross-build. All attempts of using ordinary build systems I've seen on Internet were wrong. And I am not even sure that I know what the correct way of using them would be. And even if you figure out how to do it properly, it will still require a significant work for you to be confident that most of your users won't get it wrong . Especially if you are doing C++ and using exceptions or RTTI. Please read the example using the NDK build system before deciding not to use it.</fud>
A middle way would be to use a traditional build system to generate the makefiles used by the Android NDK build system. As of today, I have not investigated the ways of doing that, but believe it to be possible. If you do that, I would like to suggest that you distribute those generated Makefiles with the released source code.

Building with the Android NDK build system

If possible, I recommend using the Android build system is used to build the libraries, for reasons mentioned above.

The theory

Please feel free to skip this section and to refer to the example which follows.

Released source code archive that are to be built by the NDK build system should contain an Android.mk file as per the ANDROID-MK.html of the NDK. This Android.mk file should be placed at the root directory. It should define a module called "library-API_version_number" where the API version number is as described above. It should also export its headers by setting the LOCAL_EXPORT_C_INCLUDES variable.

The file should not refer to files outside of the library archive, and should instead use the NDK module system. It should not make assumptions on the architecture. In particular, it must not optimise for certain CPUs without checking if the build targets this CPU. It should not enable generic-purpose optimizations or debug options as those are to be enabled project-wide by the Application.mk file.

Example

Considering an imaginary library libfoo version 2.3.4 whose "API version number" (as described above) is 2.3 . libfoo depends on libbar and libwiz. The source code archive should contain an Android.mk along those lines:

# Source code root.
# All paths should be contain to this variable, with the exception
# of LOCAL_SRC_FILES .
LOCAL_PATH := $(call my-dir)

# Needed by the NDK build system
include $(CLEAR_VARS)

# Name your library with its API version number (described above).
LOCAL_MODULE := libfoo-2.3

# This indicates the path to the headers that users
# of your library wants (like libfoo.h)
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/headers-are-here \
                           $(LOCAL_PATH)/headers-are-there

# Those are the libraries we are using.
# Android.mk will setup linking and includes for you !
LOCAL_SHARED_LIBRARIES := wiz-1.0 bar-2.1
LOCAL_STATIC_LIBRARIES := wuz-2.3

# This is a path to headers you do not wish to expose.
# Please do not put relative paths to the headers of your dependencies here.
# Instead, please add the dependencies, to LOCAL_SHARED_LIBRARIES and rely
# on the dependencies to set their LOCAL_EXPORT_C_INCLUDES .
LOCAL_C_INCLUDES := $(LOCAL_PATH)/private-headers/

# Please do not add GCC optimisation or "-g" flags here, as they should
# be set project-wise by the user
# Please do not set include paths here (see LOCAL_C_INCLUDES).
# Please do not add ARM-specific defines here (but see below).
LOCAL_CFLAGS := -DENABLE_MAGIC

# If you want to add ARM-specific defines, please do it this way
ifeq ($(TARGET_ARCH),arm)
LOCAL_CFLAGS += -DENABLE_ARM_SPECIFIC_MAGIC
endif

# You can either use this or explicitely list the source code files
LOCAL_SRC_FILES := $(notdir $(wildcard $(LOCAL_PATH)/*.c))
# If you explicitely list source files, you do not need to repeat $(LOCAL_PATH)
#LOCAL_SRC_FILES := file_without_local_path.c \
#                other_file_without_local_path.c   

# Use this only when linking with system libraries that are part
# of the Android ABI.
# For the other libraries, please use LOCAL_SHARED_LIBRARIES
# and LOCAL_STATIC_LIBRARIES as demonstrated above.
LOCAL_LDLIBS := -lz

# This enables the build of your library as a shared library.
# Please do so, if possible, as you would on a GNU/Linux system.
include $(BUILD_SHARED_LIBRARY)
#include $(BUILD_STATIC_LIBRARY)

Building with your custom build system

After adapting your custom build system so that it produces binaries for the android platform, you need to create a short file that will expose the generated files to the NDK.

Considering an imaginary library libfoo version 2.3.4 whose "API version number" (as described above) is 2.3 . libfoo depends on libbar and libwiz. The source code archive should contain an Android.mk along those lines:

# Source code root.
# All paths should be contain to this variable, with the exception
# of LOCAL_SRC_FILES .
LOCAL_PATH := $(call my-dir)

# Needed by the NDK build system
include $(CLEAR_VARS)

# Name your library with its API version number (described above).
LOCAL_MODULE := libfoo-2.3

# This indicates the path to the headers that users
# of your library wants (like libfoo.h)
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/headers-are-here \
                           $(LOCAL_PATH)/headers-are-there

# Those are the libraries we are using.
# Android.mk will setup linking and includes for you !
LOCAL_SHARED_LIBRARIES := wiz-1.0 bar-2.1
LOCAL_STATIC_LIBRARIES := wuz-2.3

# Please list here the library files that your build system produces
# You do not need to repeat $(LOCAL_PATH)
LOCAL_SRC_FILES := objs/my_lib.so

# This exports your library as a shared library.
# Please do so, if possible, as you would on a GNU/Linux system.
include $(PREBUILT_SHARED_LIBRARY)
#include $(PREBUILT_STATIC_LIBRARY)


Comments