Androgenizer - a tool for building source codes for Android

This article only discusses the adaptation of the software assembly with source code for Unix / Linux on Android.

At my work, I mainly work on finalizing the functionality of the standard AOSP , so I often have to make decisions on moving some libraries / applications from Linux to Android. There are many English-language articles on the Internet that describe the basic principles of transfer and adaptation. Therefore, I decided to collect all the material related to software assembly, put it together and share my experience. The material presented implies that readers have experience building AOSP and knowledge of the syntax of the Android.mk file.


Why do you need an Androigenizer?


In Android has its own build system like the GNU the make . However, most developers are not very happy when they have to add support for the new build system in open source projects (in most cases autotools is used ), which, moreover, must be constantly monitored (and suddenly something breaks down in another version). Androgenizer was created to avoid such situations.

Instead of adding an Android.mk file to each project module , you can add only one Android.mk file at the highest level for automatic configuration for the pre-assembly stage. Complete all steps inautoconf / automake / configure , and then calling make in the appropriate directories to create Android.mk files in them .

Each Makefile.am module that you want to compile for Android should contain a small fragment for generating Android.mk , using androgenizer .

Sometimes the contents of an Android.mk file depend on how the project will be built: using NDK or as part of AOSP. Androgenizer also takes this into account.

Androgenizer detects build system based on ANDROID_BUILD_TOP environment variableif it is not installed or empty, then it is supposed to build from under the NDK.


Let's look at an example of building the mpeg2enc plugin from gst-plugins-bad for gstreamer.

Introduction


All build will be done using AOSP :
  • The Gstreamer project and all plugins are located in the “external / gstreamer-aggregate” folder
  • The Android.mk file for preparing environment variables is located in the “external / gstreamer-aggregate / jni” folder
  • The gst-plugins-bad plugin is located in the “external / gstreamer-aggregate / gst-plugins-bad” folder
  • The Android.mk file for configuring the gst-plugins-bad plugin is located in the “external / gstreamer-aggregate / gst-plugins-bad / Android.mk” folder

Step 1. Preparing environment variables


Consider creating an Android.mk file that will be located on the path “external / gstreamer-aggregate / jni / Android.mk”. Take the Android.mk file as a basis and consider in more detail:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

Setting the local path and clearing the values ​​of some variables.

GSTREAMER_AGGREGATE_TOP := $(abspath $(LOCAL_PATH))/..

Defining the top of the directory where the Gstremer sources are located . In our case, the variable GSTREAMER_AGGREGATE_TOP will point to "/ path / to / AOSP / external / gstreamer-aggregate".

ifneq ($(SYSROOT),)
NDK_BUILD := true
else
NDK_BUILD := false
endif

If the SYSROOT variable is set, then the assembly is performed using ndk-build, otherwise in the AOSP sources.

ifeq ($(gstreamer_TOP),)
gstreamer_TOP := $(GSTREAMER_AGGREGATE_TOP)/gstreamer
endif
ifeq ($(GST_PLUGINS_GOOD_TOP),)
GST_PLUGINS_GOOD_TOP := $(GSTREAMER_AGGREGATE_TOP)/gst-plugins-bad
endif
ifeq ($(GST_PLUGINS_BAD_TOP),)
GST_PLUGINS_BAD_TOP := $(GSTREAMER_AGGREGATE_TOP)/gst-plugins-bad
endif

Setting global variables where libraries, applications, plugins, etc. are located We will need these variables in the future for assembly and configuration.

CONFIGURE_CC := $(TARGET_CC)
CONFIGURE_CXX := $(TARGET_CXX)
CONFIGURE_INCLUDES :=
CONFIGURE_LDFLAGS := -lc -ldl
# as ndk-build
ifeq ($(NDK_BUILD),true)
CONFIGURE_CFLAGS := \
    -nostdlib -Bdynamic \
    -Wl,-dynamic-linker,/system/bin/linker \
    -Wl,--gc-sections \
    -Wl,-z,nocopyreloc \
    $(call host-path,\
        $(TARGET_CRTBEGIN_DYNAMIC_O) \
        $(PRIVATE_OBJECTS)) \
    $(call link-whole-archives,$(PRIVATE_WHOLE_STATIC_LIBRARIES))\
    $(call host-path,\
        $(PRIVATE_STATIC_LIBRARIES) \
        $(TARGET_LIBGCC) \
        $(PRIVATE_SHARED_LIBRARIES)) \
    $(PRIVATE_LDFLAGS) \
    $(PRIVATE_LDLIBS) \
    $(call host-path,\
        $(TARGET_CRTEND_O)) \
	$(CONFIGURE_INCLUDES)
CONFIGURE_LDFLAGS += -L$(SYSROOT)/usr/lib -L$(TARGET_OUT)
CONFIGURE_INCLUDES += -I$(SYSROOT)/usr/include \
		-I$(GSTREAMER_AGGREGATE_TOP)/libid3tag \
		-I$(GSTREAMER_AGGREGATE_TOP)/libmad \
		-I$(GSTREAMER_AGGREGATE_TOP)/faad/include
CONFIGURE_CPP := $(TOOLCHAIN_PREFIX)cpp
CONFIGURE_CXX := $(TOOLCHAIN_PREFIX)c++
LIB := $(SYSROOT)/usr/lib
# as AOSP build
else
LIB := $(TARGET_OUT_SHARED_LIBRARIES)
CONFIGURE_CC := $(patsubst %,$(PWD)/%,$(TARGET_CC))
CONFIGURE_CXX := $(patsubst %,$(PWD)/%,$(TARGET_CXX))
CONFIGURE_LDFLAGS += -L$(PWD)/$(TARGET_OUT_INTERMEDIATE_LIBRARIES)
CONFIGURE_CFLAGS := \
    -nostdlib -Bdynamic \
    -Wl,-dynamic-linker,/system/bin/linker \
    -Wl,--gc-sections \
    -Wl,-z,nocopyreloc
CONFIGURE_LDFLAGS += \
    $(PWD)/$(TARGET_CRTBEGIN_DYNAMIC_O) \
    $(call link-whole-archives,$(PRIVATE_WHOLE_STATIC_LIBRARIES))\
    $(PRIVATE_STATIC_LIBRARIES) \
    $(PWD)/$(TARGET_LIBGCC) \
    $(PRIVATE_SHARED_LIBRARIES) \
    $(PWD)/$(TARGET_CRTEND_O)
CONFIGURE_CPP := $(PWD)/$(TARGET_TOOLS_PREFIX)cpp
CONFIGURE_INCLUDES += \
	$(foreach incdir, $(realpath $(C_INCLUDES) $(TARGET_C_INCLUDES)), -I$(incdir)) \
	-I$(abspath $(TOP)/external/zlib) \
	-I$(GSTREAMER_AGGREGATE_TOP)/libid3tag \
	-I$(GSTREAMER_AGGREGATE_TOP)/libmad \
	-I$(GSTREAMER_AGGREGATE_TOP)/faad/include
endif
CONFIGURE_CPPFLAGS := \
	$(CONFIGURE_INCLUDES)
CONFIGURE_CXXFLAGS := \
	$(CONFIGURE_INCLUDES)

Various environment variables needed for compilation, linking, etc ...

# configure as ./autogen.sh
CONFIGURE := autogen.sh
# or configure as ./configure
CONFIGURE := configure

The executable file to start the configuration.

CONFIGURE_PKG_CONFIG_LIBDIR := $(GLIB_TOP):$(gstreamer_TOP)/pkgconfig:$(GST_PLUGINS_BASE_TOP)/pkgconfig:$(GST_PLUGINS_GOOD_TOP)/pkgconfig:$(GST_PLUGINS_BAD_TOP)/pkgconfig:$(GSTREAMER_AGGREGATE_TOP)/x264
PKG_CONFIG := PKG_CONFIG_LIBDIR=$(CONFIGURE_PKG_CONFIG_LIBDIR) PKG_CONFIG_TOP_BUILD_DIR="/" pkg-config

Environment variables to indicate the directories where the package configuration files (pc files) are located. It doesn’t always work, often you have to manually edit or completely remove dependencies in files such as configure.ac in order to complete the configuration successfully. Pkg-config
must be installed on the main system

GST_CFLAGS := \
	-DD_GNU_SOURCE \
	-DGST_DISABLE_DEPRECATED \
	-DHAVE_CONFIG_H \
	-I$(gstreamer_TOP)

Specific global options for Gstreamer .

GST_CFLAGS += \
	$(shell $(PKG_CONFIG) gstreamer --cflags)

Adding specific global options for Gstreamer from the configuration package. This requires the gstreamer package to be installed on the main system. The option is not critical, but is often useful.

Step 2. Preparing for the configuration


Consider creating an Android.mk file, which will be located on the path “external / gstreamer-aggregate / gst-plugins-bad / Android.mk”, for configuring and creating the final Android.mk files for building binary libraries / plugins. Take the Android.mk file as a basis and consider in more detail:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

Setting the local path and clearing the values ​​of some variables.

GST_PLUGINS_BAD_TOP := $(LOCAL_PATH)

Specifies the top of the directory where gst-plugins-bad sources are located . In our case, the variable GST_PLUGINS_BAD_TOP will point to "/ path / to / AOSP / external / gstreamer-aggregate / gst-plugins-bad".

GST_PLUGINS_BAD_BUILT_SOURCES := \
	pkgconfig/gstreamer-plugins-bad-1.0-uninstalled.pc \
	pkgconfig/gstreamer-plugins-bad-1.0.pc \
	gst-libs/gst/baseparse/Android.mk \
	gst-libs/gst/basecamerabinsrc/Android.mk \
	gst-libs/gst/codecparsers/Android.mk \
	gst-libs/gst/interfaces/Android.mk \
	gst/h264parse/Android.mk \
	...

This variable contains the paths and goals for make. For example, we need to go into the folder “gst / h264parse” and execute “make Android.mk”, for this we execute the command “cd gst / h264parse; make Android.mk ”, which is equivalent to the command“ make -C 'gst / h264parse' Android.mk "

GST_PLUGINS_BAD_BUILT_SOURCES := $(patsubst %, $(abspath $(GST_PLUGINS_BAD_TOP))/%, $(GST_PLUGINS_BAD_BUILT_SOURCES))

We change relative paths to absolute ones.

.PHONY: gst-plugins-bad-configure

Add a “false” target just in case.

gst-plugins-bad-configure:
	cd $(GST_PLUGINS_BAD_TOP) ; \
	CC="$(CONFIGURE_CC)" \
	CFLAGS="$(CONFIGURE_CFLAGS)" \
	CXX="$(CONFIGURE_CXX)" \
	CXXFLAGS="$(CONFIGURE_CXXFLAGS)" \
	LD=$(TARGET_LD) \
	LDFLAGS="$(CONFIGURE_LDFLAGS)" \
	CPP=$(CONFIGURE_CPP) \
	CPPFLAGS="$(CONFIGURE_CPPFLAGS)" \
	PKG_CONFIG_LIBDIR="$(CONFIGURE_PKG_CONFIG_LIBDIR)" \
	PKG_CONFIG_TOP_BUILD_DIR=/ \
	$(abspath $(GST_PLUGINS_BAD_TOP))/$(CONFIGURE) \
		--prefix=/system --host=arm-linux-androideabi \
		--disable-gtk-doc \
		--disable-valgrind && \
	for file in $(GST_PLUGINS_BAD_BUILT_SOURCES); do \
		rm -f $$file && \
		make -C $$(dirname $$file) $$(basename $$file) ; \
	done

Create the gst-plugins-bad-configure target .
When configuring, we always specify the option "--prefix = / system", since by default in Android all system libraries, applications, settings, etc. are located in the / system folder.
For ARM systems, we must specify the option "--host = arm-linux-androideabi".
Other configuration options already depend on your requirements, desires and capabilities.
If necessary, you can use additional commands to clean the sources of “make distclean” and / or “autoreconf -fiv” before configuration.

-include $(GST_PLUGINS_BAD_TOP)/gst-libs/gst/baseparse/Android.mk
-include $(GST_PLUGINS_BAD_TOP)/gst-libs/gst/basecamerabinsrc/Android.mk
-include $(GST_PLUGINS_BAD_TOP)/gst-libs/gst/codecparsers/Android.mk
-include $(GST_PLUGINS_BAD_TOP)/gst-libs/gst/interfaces/Android.mk
-include $(GST_PLUGINS_BAD_TOP)/gst/h264parse/Android.mk
...

And at the very end are the paths to the Android.mk files that will collect libraries, applications, plugins, etc.

Step 3. Adding a target to the Makefile


Consider adding the target “Android.mk” to the Makefile.am file, which will be located along the path “external / gstreamer-aggregate / gst-plugins-bad / ext / mpeg2enc / Makefile.am”, to build the Android.mk file for building the binary plugin. Take the Makefile.am file as a basis and consider in more detail:

plugin_LTLIBRARIES = libgstmpeg2enc.la

Plugin name

libgstmpeg2enc_la_SOURCES = \
	gstmpeg2enc.cc \
	gstmpeg2encoptions.cc \
	gstmpeg2encoder.cc \
	gstmpeg2encstreamwriter.cc \
	gstmpeg2encpicturereader.cc

Source files required for assembly

libgstmpeg2enc_la_CXXFLAGS = \
	$(GST_PLUGINS_BAD_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) $(GST_CXXFLAGS) $(MPEG2ENC_CFLAGS)
libgstmpeg2enc_la_LIBADD = \
	$(GST_PLUGINS_BASE_LIBS) -lgstvideo-@GST_API_VERSION@ \
	$(GST_LIBS) $(MPEG2ENC_LIBS)
libgstmpeg2enc_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
libgstmpeg2enc_la_LIBTOOLFLAGS = --tag=disable-static

Flags for compilation and linking

noinst_HEADERS = \
	gstmpeg2enc.hh \
	gstmpeg2encoder.hh \
	gstmpeg2encoptions.hh \
	gstmpeg2encstreamwriter.hh \
	gstmpeg2encpicturereader.hh

Header files.

We have everything necessary to create the goal of “Android.mk”
Android.mk: Makefile.am $(BUILT_SOURCES)
	androgenizer \
	-:PROJECT libgstmpeg2enc -:SHARED libgstmpeg2enc \
	 -:TAGS eng debug \
	 -:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \
	 -:SOURCES $(libgstmpeg2enc_la_SOURCES) \
	 -:CFLAGS $(DEFS) $(DEFAULT_INCLUDES) $(libgstmpeg2enc_la_CXXFLAGS) \
	 -:LDFLAGS $(libgstmpeg2enc_la_LDFLAGS) \
	           $(libgstmpeg2enc_la_LIBADD) \
	           -ldl \
	 -:PASSTHROUGH LOCAL_ARM_MODE:=arm \
				   LOCAL_MODULE_PATH:='$$(TARGET_OUT)/lib/gstreamer-0.10' \
				   LOCAL_CPP_EXTENSION:='.cc' \
	> $@

You should pay attention to LOCAL_CPP_EXTENSION , I set the ".ss" force, since by default Android uses the extension "cpp". Problems always arise if several files with different extensions “cxx”, “cpp” and “cc” are stored in a project at once.

Translation of androgenizer options that can be read in README.txt .
more details
  • -: PROJECT The name of the project. The parameter must be called first and only once.

  • -: SUBDIR adds -include , except for the declared variable ‹project› _TOP

  • Replacement path for -I inclusions
    • -: ABS_TOP sets the absolute path to the source folder
    • -: REL_TOP sets the relative path to the source folder

    It should always be: -: REL_TOP $ (top_srcdir) -: ABS_TOP $ (abs_top_srcdir)

  • Types of modules created:
    • -: STATIC creates a static library for the target system (BUILD_STATIC_LIBRARY)
    • -: SHARED creates a dynamic library for the target system (BUILD_SHARED_LIBRARY)
    • -: EXECUTABLE creates an executable for the target system (BUILD_EXECUTABLE)
    • -: HOST_STATIC creates a static library for the host (BUILD_HOST_STATIC_LIBRARY)
    • -: HOST_SHARED creates a dynamic library for the host (BUILD_HOST_SHARED_LIBRARY)
    • -: HOST_EXECUTABLE creates the executable for the host (BUILD_HOST_EXECUTABLE)

    Several modules can be created using a single command line.

  • Additional resources for modules (you must first declare a module!):
    • -: SOURCES list of source files

      • -: CFLAGS flags for C compiler
      • -: CXXFLAGS flags for the C ++ compiler
      • -: CPPFLAGS flags for C preprocessor

      CPPFLAGS is used by C and C ++ compilers
      CFLAGS is used by C and C ++ compilers
      CXXFLAGS is used only by the C ++

      Android compiler uses various conventions, so do not be surprised if CXXFLAGS is at the end of LOCAL_CPPFLAGS in Android.mk
      There is no way to transfer only the C compiler flags to the Android build system.

      All -I flags in any of CFLAGS, CPPFLAGS or CXXFLAGS will be added to LOCAL_C_INCLUDES without the "-I".

      some flags are deleted without warning: -Werror -pthread

    • -: LDFLAGS linker options list:
      • -l ‹foo› will add as lib ‹foo› to LOCAL_SHARED_LIBRARIES
      • -L and -R will be deleted without warning
      • -pthread and -lpthread will be deleted without warning
      • -lrt will be deleted without warning (rt built-in library in bionic)
      • -no-undefined will be deleted without warning
      • -dlopen , -version-info , and the words following them (optional arguments) will be deleted without warning
      • Only * .a and * .la files are saved, the rest will be deleted without warning.

    • -: LIBFILTER_STATIC list of libraries (without the “lib” prefix and extension)
      These libraries will be added to LOCAL_STATIC_LIBRARIES and excluded from LOCAL_SHARED_LIBRARIES.

    • -: LIBFILTER_WHOLE list of libraries (without the “lib” prefix and extension)
      These libraries will be added to LOCAL_WHOLE_STATIC_LIBRARIES.

    • -: TAGS must include any of the parameters: optional user eng tests

    • -: HEADERS list of header files for LOCAL_COPY_HEADERS

    • -: HEADER_TARGET sets LOCAL_COPY_HEADERS_TO, may include several lines, but only the last

    • -: PASSTHROUGH is a list of lines embedded in the current module configuration. For example, LOCAL_ARM_MODE: = arm

    • -: END optional ... may be removed in the future. Ends the description of the current module, and begins the description of the next.




Step 4. Assembly


Download the androgenizer , collect or put the program, where it will be seen from an environment variable the PATH .
Now we are ready to build the module. To do this, go to the root of AOSP and run the command:
make gst-plugins-bad-configure

After some time, gst-plugins-bad will be configured and the resulting Android.mk file will be output:
external / gstreamer-aggregate / gst-plugins-bad / ext / mpeg2enc / Android.mk
LOCAL_PATH:=$(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE:=libgstmpeg2enc
LOCAL_MODULE_TAGS:=eng debug
LOCAL_SRC_FILES := \
    gstmpeg2enc.cc \
    gstmpeg2encoptions.cc \
    gstmpeg2encoder.cc \
    gstmpeg2encstreamwriter.cc \
    gstmpeg2encpicturereader.cc
LOCAL_SHARED_LIBRARIES:=\
    libglib-2.0 \
    libgobject-2.0 \
    libgstreamer-0.10 \
    libgstbase-0.10 \
    libgstriff-0.10 \
    libgsttag-0.10 \
    libgstvideo-0.10 \
    libdl
LOCAL_LDFLAGS:=\
    -module\
    -avoid-version\
    -export-symbols-regex\
    -no-undefined\
    -Wl,-Bsymbolic-functions
LOCAL_CFLAGS := \
    -DHAVE_CONFIG_H \
    -I. \
    -I/path/to/AOSP/external/gstreamer-aggregate/gst-plugins-bad \
    -I/path/to/AOSP/external/gstreamer-aggregate/gst-plugins-bad/gst-libs \
    -I/path/to/AOSP/external/gstreamer-aggregate/gst-plugins-base \
    -I/path/to/AOSP/external/gstreamer-aggregate/gst-plugins-base/gst-libs \
    -I/path/to/AOSP/external/gstreamer-aggregate/gstreamer \
    -I/path/to/AOSP/external/gstreamer-aggregate/glib/glib \
    -I/path/to/AOSP/external/gstreamer-aggregate/glib \
    -I/path/to/AOSP/external/gstreamer-aggregate/glib/gmodule \
    -I/path/to/AOSP/external/gstreamer-aggregate/gstreamer/libs \
    -DG_THREADS_MANDATORY \
    -DG_DISABLE_DEPRECATED \
    -Wall \
    -Wdeclaration-after-statement \
    -Wvla \
    -Wpointer-arith \
    -Wmissing-declarations \
    -Wmissing-prototypes \
    -Wredundant-decls \
    -Wundef \
    -Wwrite-strings \
    -Wformat-nonliteral \
    -Wformat-security \
    -Winit-self \
    -Wmissing-include-dirs \
    -Waddress \
    -Waggregate-return \
    -Wno-multichar \
    -Wnested-externs \
    -Wno-unused \
    -g \
    -DGST_DISABLE_DEPRECATED
LOCAL_PRELINK_MODULE := false
LOCAL_ARM_MODE:=arm
LOCAL_MODULE_PATH:=$(TARGET_OUT)/lib/gstreamer-0.10
LOCAL_CPP_EXTENSION:=.cc
include $(BUILD_SHARED_LIBRARY)



Summary


Advantages:
  • Automatic generation of Android.mk files

Disadvantages:
  • Requires individual compilation and addition of an Android.mk target to each Makefile
  • It requires a file to work with every Android.mk file: very often some paths to header files are not included, then extra options are added, etc.
  • Embedding absolute paths in the Android.mk file, but I would like based on $ (LOCAL_PATH)


Used articles:

Also popular now: