Android Build cookbook| Build Apps or Libraries with Make file

Android make recipe feature img

Android provides different build systems to compile, deploy and convert your source code into object code. The one which is used in Android Studio is Gradle build system and another one which is used in AOSP to describe the source and libraries is Make file(Android.mk).

The Android.mk file is written to describe your sources and shared libraries to the build system. You can define several modules in a single Android.mk or you can write several Android.mk files, each defining single module.

A module can be one of the following types:

  1. An android program that is compiled and packaged to generate an APK file.
  2. A JAVA library program, compiled and packaged to generate a JAR file.
  3. C/C++ program, compiled to generate an executable C/C++ application.
  4. C/C++ static library, compile and generate C/C++ static library, and package it into .a file
  5. C/C++ shared library, compile and generate C/C++ shared library and packaged it into .so file.

All Android.mk files are parsed by the build system before any build occurs. These files are tiny GNU make file fragments that will be parsed more than once by the build system.

Let’s see the code snippet, that will help you quickly build the common tasks like building an APK, building a static or shared library or signing the APK with platform key. 

Building a simple APK using Android make file (Android.mk)

  LOCAL_PATH := $(call my-dir)
  include $(CLEAR_VARS)
   
  # Build all java files in the java subdirectory
  LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res \   # Name of the APK to build LOCAL_PACKAGE_NAME := LocalPackage   # Tell it to build an APK include $(BUILD_PACKAGE)

Now, let’s explain these lines:

LOCAL_PATH := $(call my-dir)

An Android.mk must begin with the definition of the LOCAL_PATH variable. It is used to locate source files in the development tree. In this example, the macro function ‘my-dir‘, provided by the build system, is used to return the path of the current directory (i.e. the directory containing the Android.mk file itself).

include $(CLEAR_VARS)

The CLEAR_VARS variable is a script and points to a special GNU Makefile that will clear all LOCAL_XXX variables for you such as LOCAL_MODULE, LOCAL_SRC_FILES, LOCAL_STATIC_LIBRARIES, etc… It does not clear LOCAL_PATH. This is because all build control files are parsed in a single GNU make execution context where all variables are global.

LOCAL_SRC_FILES := $(call all-subdir-java-files)

This is to include all the java source files present in the subdirectories that the build system uses to generate the module.

LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res \
This is to include the resource files associated with the application

LOCAL_PACKAGE_NAME := LocalPackage

This variable will tell the build system, what would be the name of the module you are building.

include $(BUILD_PACKAGE)

Just like CLEAR_VAR, BUILD_PACKAGE will also points to a script that will collect all the information you have provided in the LOCAL_XXX and tell the build system to build and APK out of it.

Building an APK that depends on static JAR using Android make file (Android.mk)

  LOCAL_PATH := $(call my-dir)
  include $(CLEAR_VARS)
   
  # List of static libraries to include in the package
  LOCAL_STATIC_JAVA_LIBRARIES := static-library
   
  # Build all java files in the java subdirectory
  LOCAL_SRC_FILES := $(call all-subdir-java-files)
   
  # Name of the APK to build
  LOCAL_PACKAGE_NAME := LocalPackage
   
  # Tell it to build an APK
  include $(BUILD_PACKAGE)

LOCAL_STATIC_JAVA_LIBRARIES := static-library

This variable is to include any static JAVA libraries in your module. For example, Suppose you want to use support-v7- appcompact library, then use it like this:
LOCAL_STATIC_JAVA_LIBRARIES += android-support-v7-appcompat
Or, if you want to use any local library that you have built, then use it like this:
LOCAL_STATIC_JAVA_LIBRARIES += myLib

Building an APK signed with platform keys

  LOCAL_PATH := $(call my-dir)
  include $(CLEAR_VARS)
   
  # Build all java files in the java subdirectory
  LOCAL_SRC_FILES := $(call all-subdir-java-files)
   
  # Name of the APK to build
  LOCAL_PACKAGE_NAME := LocalPackage
   
  LOCAL_CERTIFICATE := platform
   
  # Tell it to build an APK
  include $(BUILD_PACKAGE)

LOCAL_CERTIFICATE := platform

This variable will tell the build system, sign the module or APK with platform keys. 

Building apk Signed with vendor specific key

LOCAL_CERTIFICATE := vendor/example/certs/app

Here everything will be same as above, only the LOCAL_CERTIFICATE will point to the location where certificates are present.

Now, you have a prebuilt APK and want to include it in the build tree. Let’s see how to do it.

Including a prebuilt apk in build tree using Android.mk file

  LOCAL_PATH := $(call my-dir)
  include $(CLEAR_VARS)
   
  # Module name should match apk name to be installed.
  LOCAL_MODULE := LocalModuleName
  LOCAL_SRC_FILES := $(LOCAL_MODULE).apk
  LOCAL_MODULE_CLASS := APPS
  LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
   
  include $(BUILD_PREBUILT)

Here the LOCAL_SRC_FILE will now point to your APK. The LOCAL_MODULE_CLASS will help the system to determine the intermediate file storage location such as the final compiled output, for APPS, the intermediate location will be obj/APPS. We have to specify the file type such as APPS for apk files, SHARED_LIBRARIES for dynamic libraries, EXECUTABLES for bin files, ETC for other files.
The LOCAL_MODULE_CLASS options are:
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_CLASS := STATIC_LIBRARIES
LOCAL_MODULE_CLASS := EXECUTABLES
LOCAL_MODULE_CLASS := FAKE
LOCAL_MODULE_CLASS := JAVA_LIBRARIES
LOCAL_MODULE_CLASS := SHARED_LIBRARIES
LOCAL_MODULE_CLASS := APPS

LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
This variable indicates the suffix of the target package after compiling the link. For .so files it will be .so and for APK it will be COMMON_ANDROID_PACKAGE_SUFFIX

Include $(BUILD_PREBUILT)
It is a script which tells the build system, that module is pre-compiled and pre processing.

Building static JAVA libraries using Android.mk file

  LOCAL_PATH := $(call my-dir)
  include $(CLEAR_VARS)
   
  # Build all java files in the java subdirectory
  LOCAL_SRC_FILES := $(call all-subdir-java-files)
   
  # Any libraries that this library depends on
  LOCAL_JAVA_LIBRARIES := android.test.runner
   
  # The name of the jar file to create
  LOCAL_MODULE := sample
   
  # Build a static jar file.
  include $(BUILD_STATIC_JAVA_LIBRARY)

LOCAL_JAVA_LIBRARIES := android.test.runner
This variable will define any library, that the current building library is dependent on. In this example, our Sample library is depending on android.test.runner JAVA library.

include $(BUILD_STATIC_JAVA_LIBRARY)
This is the script which will build the static JAVA library.

Building C/C++ Executable file

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE := SampleModule
LOCAL_SRC_FILES := helloworld.cpp CalculateVolume.cpp
LOCAL_MODULE_TAGS := optional
LOCAL_STATIC_LIBRARIES := libutils libcutils liblog libc

include $(BUILD_EXECUTABLE)

LOCAL_STATIC_LIBRARIES := libutils libcutils liblog libc
This variable contains the list of static libraries, required by the module in order to compile.

include $(BUILD_EXECUTABLE)
This will tell the build system to build an executable object.

Let see Android.mk variables

First see the System variables

Variable Description
include $(CLEAR_VARS)
Points to a build script, clear all LOCAL_XXX variables except LOCAL_PATH
BUILD_SHARED_LIBRARY
Points to the build script to compile listed source into shared library based on the LOCAL_XXX variables described in the make file.
BUILD_STATIC_LIBRARY
Points to the build script to compile listed source into static library based on the LOCAL_XXX variables described in the make file. include $(BUILD_STATIC_LIBRARY) will generate a file called lib$(LOCAL_MODULE).a
BUILD_PREBUILT
Points to the build script to include pre-compiled and pre processed modules in the build system

Now, let's see the Local variables

Variable Description
LOCAL_CPP_EXTENSION

Optional variable, that points to C++ code file. The default is .cpp but it can be changed, for e.g:
LOCAL_CPP_EXTENSION := .cxx

LOCAL_ASSET_FILES

In Android.mk files that include $(BUILD_PACKAGE) set this to the set of files you want built into your app. Usually:

LOCAL_ASSET_FILES += $(call find-subdir-assets)

LOCAL_C_INCLUDES

Additional directories to instruct C/C++ compilers to include the header files of it. The default header file search path is the LOCAL_PATH directory.

For e.g: LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/.. \
$(LOCAL_PATH)/include \
$(LOCAL_PATH)/../btcore/include \
$(LOCAL_PATH)/../hci/include \
$(LOCAL_PATH)/../include \
$(LOCAL_PATH)/../stack/include \
$(bluetooth_C_INCLUDES)

LOCAL_C_INCLUDES needs to be set before any flags containing LOCAL_CFLAGS/LOCAL_CPPFLAGS.

LOCAL_CFLAGS

This is optional compiler option to use when compiling C code files. This may be useful when you are specifying macro defination, additional include path or compilation options.

For e.g: LOCAL_CFLAGS += -DANDROID \
      -Wall \
      -DLIBUTILS_NATIVE=1\

LOCAL_CPPFLAGS

Same as LOCAL_CFLAGS, but applicable for both C/C++ files

LOCAL_SHARED_LIBRARIES

Same as LOCAL_STATIC_LIBRARIES, but here it only includes shared libraries that module depends on during runtime.

For e.g:
LOCAL_SHARED_LIBRARIES := \
    libutils \
    libui \
    libaudio \
    libexpat \
    libsgl

LOCAL_CC

This will be used when you want to set different C compiler, rather than default.

LOCAL_FORCE_STATIC_EXECUTABLE

Set this to true, if you want to link your executable statically. This is only be used for executables running in /sbin, on the root of your file system.

For e.g: LOCAL_FORCE_STATIC_EXECUTABLE := true

LOCAL_MODULE_TAG

This flag is used to specify under what circumstances to compile your module. It could be optional/eng/test

For e.g:
LOCAL_MODULE_TAGS := optional
Means, To compile this module in all versions.
LOCAL_MODULE_TAGS := eng
Means, to compile this module only in development/engineering environment.
Same goes for
LOCAL_MODULE_TAGS := debug
Means. to compile only in debug environment
LOCAL_MODULE_TAGS := user
If the library or mudule is required only in final software product.

LOCAL_PROGUARD_ENABLED

This is to set the type of proguard you want to enable in your module(obfuscation,, optimazation or both).

For e.g:
LOCAL_PROGUARD_ENABLED := obfuscation
or
LOCAL_PROGUARD_ENABLED := obfuscation optimization

LOCAL_PROGUARD_FLAG_FILES

This is to include the proguard file.

For e.g:
LOCAL_PROGUARD_FLAG_FILES := proguard.flags

LOCAL_PRIVILEGED_MODULE

This is to place the apk in the priv-app folder of the build. It could be either /system/priv-app/ or /vendor/priv-app/

For e.g:
LOCAL_PRIVILEGED_MODULE := true

LOCAL_VENDOR_MODULE

This is to place the apk in the /vendor/ directory

For e.g:
LOCAL_VENDOR_MODULE := true

LOCAL_MULTILIB

This is to tell the build system, that the module is limited to 32 bit or 64bit or it is compatible to both.

For e.g:
LOCAL_MULTILIB := 32/64/BOTH

LOCAL_REQUIRED_MODULES

This is to tell the build system, that your module needs the mentioned libraries to be build first.

For e.g:
LOCAL_REQUIRED_MODULES := SoundRecorder, AndroidLib

LOCAL_OVERRIDES_PACKAGES

This variable will override the package provided, with your current package/module. You need to provide the name of the module that you want to override to. For example you are creating your own app and want to override launcher3 app that is already present in the system.
Adding this flag to your Android.mk file will ensure that those packages are not added to any build where this package is added.

Use it like this:
LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3

Fowllowing this call: make installclean while building your android source. This will clean out your out directory of any old modules/images.

You can see it from this link how DeskClock is overriding the AlarmClock in AOSP.

GNU make 'function' macro in Android.mk

Now, let’s see some of the macro functions in action.

  1. all-subdir-makefiles: This will returns the list of all Android.mk files under the LOCAL_PATH or current directory.
    For e.g: include $(call all-subdir-makefiles)

  2. all-makefiles-under: This is to include all the make files under the given path in the macro.
    For e.g: include $(call all-makefiles-under,$(LOCAL_PATH))

  3. all-java-files-under: This is to include all the JAVA files present in the given location.
    For e.g: LOCAL_SRC_FILES := $(call all-java-files-under, src)
    Where src is the directory under which Java files are present.

  4. all-Iaidl-files-under: This is to include all AIDL files present in the given location.
    For e.g: LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-Iaidl-files-under, src)

Conclusion

This is all about the Android make file(Android.mk). For more information on the GNU make file, you can see the complete recipe here. This makefile based system is now on the verge of being replaced by Soong, a new build system written in ‘GO’ language. Many of the modules in Android R have already replaced the old Android.mk file with Android.bp. If you like this post, please do share it on social media like Facebook or WhatsApp. If you have any suggestions or questions please do let me know in the comment section. Or if you want to know anything about any app or software for your own purpose, write us to support@gizmomind.com, and please like our Facebook page.

Swetabh

Swetabh is an Android Developer and a Tech enthusiast. His enthusiasm drives him to know about various technologies. Now he wanted to share his knowledge to other enthusiast who are passionate to know-how and become a GIZMOMIND.

Leave a Reply