Сборка исходников Android. Инструмент Androgenizer.

14 ноября 2013 г.

В данной статье рассматривается только адаптация сборки ПО с исходным кодом для Unix/Linux на Android.

На своей работе часто приходится принимать решения по переносу некоторых библиотек/приложений из Linux в Android. В интернете много англоязычных статей, описывающих основные принципы переноса и адаптации. Поэтому я решил собрать весь материал, касающийся сборки ПО, воедино и поделиться своим опытом. Излагаемый материал подразумевает, что у читателей есть опыт сборки AOSP и знание синтаксиса файла Android.mk.

Зачем нужен Androigenizer?

У Android есть своя собственная система сборки наподобие GNU make. Тем не менее, большинство разработчиков не очень-то радуются, когда им приходится добавлять поддержку новой системы сборки в open source проекты (в большенстве случаев используется autotools), за которыми, к тому же, необходимо постоянно следить (а вдруг что-то поломается в другой версии). Androgenizer был создан, чтобы избежать подобных ситуаций.

Вместо того, чтобы добавлять в каждый модуль проекта файл Android.mk, Вы можете добавить только один файл Android.mk на самом верхнем уровне для автоматической конфигурации для пред-сборочной стадии. Выполнение всех шагов в autoconf/automake/configure, а затем вызов make в соответствующих каталогах, для создания в них файлов Android.mk.

Каждый Makefile.am модуля, который Вы хотите скомпилировать под Android, должен содержать небольшой фрагмент для генерации Android.mk, с использованием androgenizer.

Иногда содержимое Android.mk файла зависит от того, как будет собираться проект: с помощью NDK или как часть AOSP. Androgenizer это тоже учитывает.

Androgenizer обнаруживает систему сборки на основе переменной окружения ANDROID_BUILD_TOP, если она не установлена ​​или пуста, то предполагается сборка из-под NDK.

Рассмотрим пример сборки плагина mpeg2enc от gst-plugins-bad для gstreamer’а.

Введение

Как мобильные технологии влияют
Вся сборка будет осуществляться с использованием AOSP:

  • Проект Gstreamer и все плагины располагаются в папке «external/gstreamer-aggregate»
  • Файл Android.mk для подготовки переменных окружения располагается в папке «external/gstreamer-aggregate/jni»
  • Плагин gst-plugins-bad располагается в папке «external/gstreamer-aggregate/gst-plugins-bad»
  • Файл Android.mk, для конфигурирования плагина gst-plugins-bad, располагается в папке «external/gstreamer-aggregate/gst-plugins-bad/Android.mk»

Шаг 1. Подготовка переменных окружения

Рассмотрим создание файла Android.mk, который будет располагаться по пути «external/gstreamer-aggregate/jni/Android.mk». Возьмем за основу файл Android.mk и рассмотрим подробнее:

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

Задание локального пути и очистка значений некоторых переменных.

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

Задание вершины директории, где располагаются исходники Gstremer. В нашей случае переменная GSTREAMER_AGGREGATE_TOP будет указывать на “/path/to/AOSP/external/gstreamer-aggregate”.

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

Если переменная SYSROOT задана, то сборка осуществляется с помощью ndk-build, иначе в исходниках AOSP.

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

Задание глобальных переменных, где расположены библиотеки, приложения, плагины и т.п. Эти переменные нам понадобятся в дальнейшем для сборки и конфигурации.

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)

Различные переменные окружения, необходимые для компиляции, линковки и т.д…

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

Исполняемый файл для запуска конфигурирования.

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

Переменные окружения для указания директорий, где расположены файлы конфигурация пакетов (pc-файлы). Не всегда работает, зачастую приходится вручную править или вообще убирать зависимости в файлах, таких как configure.ac, чтобы завершить успешно конфигурирование.

На основной системе должен быть установлен pkg-config

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

Специфичные глобальные опции для Gstreamer.

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

Добавление специфичных глобальных опций для Gstreamer из пакета конфигураций. Для это требуется чтобы в основной системе был установлен пакет «gstreamer». Опция не критическая, но зачастую бывает полезной.

Шаг 2. Подготовка к конфигурации

Рассмотрим создание файла Android.mk, который будет располагаться по пути «external/gstreamer-aggregate/gst-plugins-bad/Android.mk», для конфигурирования и создания конечных файлов Android.mk для сборки бинарных библиотек/плагинов. Возьмем за основу файл Android.mk и рассмотрим подробнее:

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

Задание локального пути и очистка значений некоторых переменных.

GST_PLUGINS_BAD_TOP := $(LOCAL_PATH)

Задание вершины директории, где располагаются исходники gst-plugins-bad. В нашей случае переменная GST_PLUGINS_BAD_TOP будет указывать на “/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 \
	...

Эта переменная содержит пути и цели для make. К примеру, нам нужно зайти в папку «gst/h264parse» и выполнить «make Android.mk», для этого выполним команду «cd gst/h264parse; make Android.mk», что эквивалентно команде «make -C ‘gst/h264parse’ Android.mk»

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

Меняем относительные пути на абсолютные.

.PHONY: gst-plugins-bad-configure

Добавляем «ложную» цель на всякий случай.

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

Создаем цель gst-plugins-bad-configure.

При конфигурировании всегда указываем опцию “–prefix=/system”, так как по умолчанию в Android все системные библиотеки, приложения, настройки и т.п. располагаются в папке /system

Для ARM-систем обязательно указываем опцию “–host=arm-linux-androideabi”.

Остальные опции конфигурирования уже зависят от Ваших требований, желаний и возможностей.

По необходимости можно воспользоваться дополнительными командами для чистки исходников «make distclean» и/или «autoreconf -fiv» перед конфигурированием.

-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
...

И в самом конце располагаются пути к файлам Android.mk, которые будут собирать библиотеки, приложения, плагины и т.д.

Шаг 3. Добавление цели в Makefile

Рассмотрим добавление цели «Android.mk» в файл Makefile.am, который будет располагаться по пути «external/gstreamer-aggregate/gst-plugins-bad/ext/mpeg2enc/Makefile.am», для построения файла Android.mk для сборки бинарного плагина. Возьмем за основу файл Makefile.am и рассмотрим подробнее:

plugin_LTLIBRARIES = libgstmpeg2enc.la

Название плагина

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

Исходные файлы, необходимые для сборки

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

Флаги для компиляции и линковки

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

Файлы заголовков.

У нас есть все необходимое для созднание цели «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' \
	> $@

Следует обратить внимание на LOCAL_CPP_EXTENSION, я задал принудительно “.сс”, так как по умолчанию Android использует расширение «cpp». Всегда возникают проблемы, если в проекте храняться сразу несколько файлов с различными расширениями «cxx», «cpp» и «cc».

Перевод опций androgenizer, о которых можно прочитать в README.txt.

  • -:PROJECT Название проекта. Параметр должен вызываться первым и только один раз.
  • -:SUBDIR добавляет -include, кроме объявленной переменной ‹project›_TOP
  • Путь замены для -I включений
    • -:ABS_TOP задает абсолютный путь к папке исходных кодов
    • -:REL_TOP задает относительный путь к папке исходных кодов

    Всегда должно быть: -:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir)

  • Типы создаваемых модулей:
    • -:STATIC создает статическую библиотеку для целевой системы (BUILD_STATIC_LIBRARY)
    • -:SHARED создает динамическую библиотеку для целевой системы (BUILD_SHARED_LIBRARY)
    • -:EXECUTABLE создает исполняемый файл для целевой системы (BUILD_EXECUTABLE)
    • -:HOST_STATIC создает статическую библиотеку для хоста (BUILD_HOST_STATIC_LIBRARY)
    • -:HOST_SHARED создает динамическую библиотеку для хоста (BUILD_HOST_SHARED_LIBRARY)
    • -:HOST_EXECUTABLE создает исполняемый файл для хоста (BUILD_HOST_EXECUTABLE)

    Несколько модулей могут быть созданы с помощью одной командной строки.

  • Дополнительные ресурсы для модулей (вначале необходимо объявить модуль!):
    • -:SOURCES список исходный файлов
      • -:CFLAGS флаги для компилятора C
      • -:CXXFLAGS флаги для компилятора C++
      • -:CPPFLAGS флаги для препроцессора C

      CPPFLAGS используется компиляторами C и C++

      CFLAGS используется компиляторами C и C++

      CXXFLAGS используется только компилятором C++

      Android использует различные конвенции, поэтому не удивляйтесь, если CXXFLAGS окажется в конце LOCAL_CPPFLAGS в Android.mk

      Не существует способ передать только флаги компилятора C в систему сборки Android.

      Все -I флаги в любом из CFLAGS, CPPFLAGS или CXXFLAGS будут добавлены в LOCAL_C_INCLUDES без “-I”.

      некоторые флаги удаляются без предупреждения: -Werror -pthread

    • -:LDFLAGS список опций линковщика:
      • -l‹foo› добавит как lib‹foo› в LOCAL_SHARED_LIBRARIES
      • -L и -R будут удалены без предупреждения
      • -pthread и -lpthread будут удалены без предупреждения
      • -lrt будет удалена без предупреждения (rt встроенная библиотека в bionic)
      • -no-undefined будет удалена без предупреждения
      • -dlopen, -version-info, и слова следующие после них (опциональные агрументы) будут удалены без предупреждения
      • Только файлы *.a и *.la сохраняться, остальные будут удалены без предупреждения.
    • -:LIBFILTER_STATIC список библиотек (без префикса «lib» и расширения)Эти библиотеки будут добавлены в LOCAL_STATIC_LIBRARIES и исключены из LOCAL_SHARED_LIBRARIES.
    • -:LIBFILTER_WHOLE список библиотек (без префикса «lib» и расширения)Эти библиотеки будут добавлены в LOCAL_WHOLE_STATIC_LIBRARIES.
    • -:TAGS должен включать в себя любой из параметров: optional user eng tests
    • -:HEADERS список файлов заголовков для LOCAL_COPY_HEADERS
    • -:HEADER_TARGET устанавливает LOCAL_COPY_HEADERS_TO, может включать в себя несколько строк, но сохраниться только последняя
    • -:PASSTHROUGH список строк, встраиваемых в текущую конфигурацию модуля. К примеру LOCAL_ARM_MODE:=arm
    • -:END опционально… может быть удалено в будущем. Заканчивает описание текущего модуля, и начинает описание следующего.

Шаг 4. Сборка

Скачиваем androgenizer, собираем и кладем программу, где ее будет видно из переменной окружения PATH.

Теперь у нас все готово для сборки модуля. Для этого переходим в корень AOSP и выполняем команду:

make gst-plugins-bad-configure

Спустя какое-то время gst-plugins-bad сконфигурируется и на выходе получится готовый файл Android.mk:

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)

Итоги

Достоинства:

  • Автоматическая генерация файлов Android.mk

Недостатки:

  • Требует индивидуального составления и добавления цели Android.mk в каждый Makefile
  • Требует работы напильником с каждым файлом Android.mk: очень часто не включены некоторые пути к заголовочным файлам, то опции лишние добавлены и т.п.
  • Встраивание абсолютных путей в файл Android.mk, а хотелось бы на основе $(LOCAL_PATH)

Используемые статьи:

Максим aka bmx666

Теги: рубрика Android
  • Похожие статьи
  • Предыдущие из рубрики