LiteCoding

Заметки о программировании

Как собрать нативное приложение под Android

without comments

Все разработчики, которые хотя бы слышали об Android, делятся на две группы. В первую группу входят те, кто считают, что Android — это прежде всего Linux, а не Dalvik VM, библиотеки и SDK. А во вторую — их идеологические противники. И если вторые, как правило, обходятся стандартным SDK и уходят в сторону NDK с большой неохотой, то первым гораздо интереснее игры с нативным кодом. Так вот, для тех, кто только начал причислять себя к первой группе (или хочет к ней примкнуть), и предназначена эта статья.

Я предполагаю, что вы уже знакомы с тем, что из себя представляет NDK, для чего нужен cygwin при разработке под Windows, и уже собрали хотя бы одну нативную библиотеку.

В сети есть ряд интересных заметок и комментариев, как собрать обычную Linux-программу для Android. В NDK уже есть все необходимое, чтобы это сделать, но сам процесс выглядит, честно говоря, не очень удобным. Требуется, как минимум, воссоздать структуру каталогов, к которым адаптированы сценарии сборки, и указать ряд обязательных параметров.

Я хочу предложить вам более простой (на мой взгляд, конечно) способ. Скрипт ndk-build является лишь средством запуска утилиты make с нужными параметрами. Поэтому, чтобы ничего не поломать, создадим альтернативный путь сборки, и начнем с точки входа.

1. Копируем скрипт ndk-build и называем копию как вам больше нравится (у меня — anbuild)

2. В скопированном скрипте меняем последнюю строчку на
$GNUMAKE -f $PROGDIR/build/core/build-native.mk $@
Видите, мы поменяли точку входа, указав другой сценарий сборки. На самом деле, он мало чем отличается от стандартных

3. Наш build-native.mk является чуть отредактированной копией build-local.mk. Основная идея — избавиться от структуры каталогов, которые хороши для использования jni, и не очень подходят для сборки программ для Linux. Вариант этого файла, который я использую в настоящий момент, можно скачать отсюда. Конечно, можно внести изменения так, что пользоваться сценарием станет еще удобнее, но даже в таком виде он выглядит гораздо привлекательнее неотредактированной версии

Чтобы не быть голословным, покажу, как собираются 3 утилиты из пакета sysstat. Для этого распакуем исходники в какой-нибудь каталог для экспериментов. Запускаем скрипт configure и получаем версию исходного кода, готовую к сборке. Стоит заметить, что в более сложных программах придется вносить дополнительные правки в исходный код, связанные как с особенностью платформы, так и с ограничениями, налагаемыми самим NDK.

В нашем случае все гораздо проще — достаточно создать заголовочный файл android-porting.h и подключить его в common.h исходников sysstat.

#ifndef __android_porting_h__
#define __android_porting_h__

#include <termios.h>

#ifndef PATH_MAX
#define PATH_MAX 4096
#endif

#endif

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

Нам понадобятся еще 2 файла: Application.mk и Android.mk.
4. Создаем Application.mk со следующим содержимым:

APP_PLATFORM := android-4
NDK_APP_OUT := ./objs
APP_BUILD_SCRIPT := ./Android.mk
APP_ABI := armeabi

Здесь все предельно ясно. Все объектные файлы, библиотеки и прочие продукты работы будут складываться именно по адресу, указанному в NDK_APP_OUT. А вот результат сборки окажется в ./libs, что является стандартным поведением сценария сборки NDK.

А вот с Android.mk придется немного повозиться. Для начала нужно понять, что и как там собирается, потому смотрим сначала Makefile.

%.o: %.c
$(CC) -o $@ -c $(CFLAGS) $(DFLAGS) $< % : %.o $(CC) -o $@ $(CFLAGS) $^ $(LFLAGS) common.o: common.c version.h common.h ioconf.h sysconfig.h ... ioconf.o: ioconf.c ioconf.h common.h sysconfig.h rd_stats.o: rd_stats.c common.h rd_stats.h ioconf.h sysconfig.h ... libsyscom.a: common.o ioconf.o $(AR) rvs $@ $? librdstats.a: librdstats.a(rd_stats.o) ... iostat.o: iostat.c iostat.h version.h common.h ioconf.h sysconfig.h rd_stats.h iostat: iostat.o librdstats.a libsyscom.a

Вот теперь сразу ясно, что для сборки iostat нам нужен iostat.c и 2 статические библиотеки (которые нужно собрать заранее).

5. Теперь садимся писать Android.mk. Это можно сделать, например, вот так:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := librdstats
LOCAL_SRC_FILES := rd_stats.c
LOCAL_CFLAGS := -g -O2 -Wall -Wstrict-prototypes -pipe -O2

include $(BUILD_STATIC_LIBRARY)

include $(CLEAR_VARS)

LOCAL_MODULE := libsyscom
LOCAL_SRC_FILES := common.c ioconf.c
LOCAL_CFLAGS := -g -O2 -Wall -Wstrict-prototypes -pipe -O2

include $(BUILD_STATIC_LIBRARY)

#Build iostat

include $(CLEAR_VARS)

LOCAL_MODULE := iostat
LOCAL_SRC_FILES := iostat.c
LOCAL_STATIC_LIBRARIES := librdstats libsyscom
LOCAL_CFLAGS := -g -O2 -Wall -Wstrict-prototypes -pipe -O2

include $(BUILD_EXECUTABLE)

#Build pidstat

include $(CLEAR_VARS)

LOCAL_MODULE := pidstat
LOCAL_SRC_FILES := pidstat.c
LOCAL_STATIC_LIBRARIES := librdstats libsyscom
LOCAL_CFLAGS := -g -O2 -Wall -Wstrict-prototypes -pipe -O2

include $(BUILD_EXECUTABLE)

#Build mpstat

include $(CLEAR_VARS)

LOCAL_MODULE := mpstat
LOCAL_SRC_FILES := mpstat.c
LOCAL_STATIC_LIBRARIES := librdstats libsyscom
LOCAL_CFLAGS := -g -O2 -Wall -Wstrict-prototypes -pipe -O2

include $(BUILD_EXECUTABLE)

6. И осталось всего лишь как-то упростить процесс сборки, потому что набирать следующую команду из командной строки весьма непросто.

anbuild V=1 NDK_LOG=1 NDK_PROJECT_PATH=. NDK_APPLICATION_MK=./Application.mk >make.log 2>make_error.log

Можно немного модифицировать сам anbuild или написать еще один маленький скрипт. Как именно поступить - решать вам.

В итоге у нас получился вполне универсальный сценарий для сборки небольших утилит силами cygwin и Android NDK. Не скажу, что это самый лучший способ, однако он уже есть, и он работает.

Share and Enjoy:
  • Print
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • LinkedIn
  • Tumblr

Written by Дмитрий Воробьев

Пятница, Декабрь 24th, 2010 at 13:35

Posted in Статьи

Tagged with ,

Leave a Reply

You must be logged in to post a comment.