Android NDK — принудительная перестройка библиотеки при изменении конфигурации

Есть ли способ заставить Android NDK перестроить определенную библиотеку при изменении конфигурации сборки в Eclipse?

Я создаю проект Android, используя Android NDK для создания библиотек C++. Я использую Eclipse с плагином Sequoyah. Все настроено и работает хорошо.

Однако у меня возникла проблема с конфигурациями сборки. Вы можете управлять конфигурациями сборки, щелкнув правой кнопкой мыши проект-> свойства, а затем перейдите в раздел C/C++ Build. Это позволяет создавать традиционные сборки Debug и Release, от которых так или иначе зависит большинство библиотек C++.

Вот пример моей конфигурации «Отладка»:

V=1 NDK_DEBUG=1 NDK_APPLICATION_MK=config/debug/Application.mk

Они работают хорошо, за исключением того, что когда я переключаюсь между конфигурациями, это не вызывает перестроение библиотек, которые я создаю. Это было бы ожидаемо для чего-то вроде Visual Studio, где каждая конфигурация сборки сбрасывается в другой каталог, но в Eclipse все сбрасывается в один и тот же каталог. Я вынужден фактически изменить соответствующие исходные файлы, чтобы вызвать перестроение. Итак, что в итоге происходит, так это то, что я запускаю (например) в конфигурации отладки, но связываюсь с библиотеками, которые были встроены в релиз.

Итак, мой вопрос: есть ли способ заставить NDK перестроить библиотеку при изменении конфигурации? Я знаю о команде -B, которую я могу добавить, но она перестраивает все, каждый раз. Я был бы готов пересобирать каждый раз, если бы мог сделать это только для одной конкретной библиотеки (в данном случае libBootInfo).

Вот как выглядит мой корневой файл Android.mk:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := game$(MY_BUILD_CONFIG_EXTENSION)

# Include files are relative to the NDK root directly (fix by prepending with $(LOCAL_PATH))
# Source files are relative $(LOCAL_PATH)

#LOCAL_LDLIBS    := -landroid

# Add all source file names to be included in lib separated by a whitespace
LOCAL_SRC_FILES :=  ../../../../../../engine/code/main/mainandroid.cpp

# Module dependencies are expressed with LOCAL_STATIC_LIBRARIES and LOCAL_SHARED_LIBRARIES.
# we're building the "main" entry point, so it doesn't depend on much
LOCAL_STATIC_LIBRARIES := libDebug$(MY_BUILD_CONFIG_EXTENSION) libCore$(MY_BUILD_CONFIG_EXTENSION)

include $(BUILD_SHARED_LIBRARY)

$(call import-module,libBdCore)
$(call import-module,libDebug)

##################################################################
## In addition to the core game library, we also build another
## *.so file here: "libBootInfo". This very small library is used
## by Java to find out which version of game to load based on
## the current build configuration.
##

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := libBootInfo

# Add all source file names to be included in lib separated by a whitespace
# TODO: This path is relative to "android-ndk\build\core" which seems
#       different from the LOCAL_SRC_FILES in game above. It seems like
#       the build process leaves us in a different directory than we started.
#       We make need to look into a way to make sure that this path always 
#       works regardless of what came before it.
#
LOCAL_SRC_FILES := ../../../../engine/code/main/bootinfo.cpp

include $(BUILD_SHARED_LIBRARY)

person Goose    schedule 19.10.2012    source источник
comment
в вашем файле Android.mk часть, которая создает libBootInfo, не должна пересчитывать LOCAL_PATH := $(call my-dir). Это потому, что $(call my-dir) на самом деле приводит путь к последнему созданному файлу, включенному перед этим оператором. Если вы переместите всю часть libBootInfo в верхнюю часть вашего Android.mk, все будет работать гладко.   -  person Alex Cohn    schedule 23.10.2012
comment
Кроме того, другой комментарий также неверен: Включаемые файлы относятся непосредственно к корню NDK. На самом деле включаемые файлы LOCAL_C_INCLUDES относятся к текущему каталогу, который обычно является корнем проекта.   -  person Alex Cohn    schedule 23.10.2012


Ответы (2)


Сборка NDK всегда обновляет библиотеки .so в lib/armeabi. С другой стороны, каталог obj содержит отдельные деревья для отладочных и выпускных сборок для каждого модуля.

К сожалению, эту настройку довольно легко испортить, если ваш Android.mk делает что-то, что не поддерживается tge framework.

Например, в вашем случае длинные восходящие пути к файлам cpp (../../../..) могут быть плохой идеей. Я бы посоветовал установить LOCAL_PATH для каждого модуля и избегать ../ в LOCAL_SRC_FILES.

Вот мое предлагаемое изменение для Android.mk:

ANDROID_MK_PATH := $(call my-dir)
LOCAL_PATH := $(ANDROID_MK_PATH)/../../../engine/code/main

include $(CLEAR_VARS)

LOCAL_MODULE := game$(MY_BUILD_CONFIG_EXTENSION)
LOCAL_SRC_FILES :=  mainandroid.cpp
LOCAL_STATIC_LIBRARIES := libDebug$(MY_BUILD_CONFIG_EXTENSION) libCore$(MY_BUILD_CONFIG_EXTENSION)

include $(BUILD_SHARED_LIBRARY)

##################################################################
## In addition to the core game library, we also build another
## *.so file here: "libBootInfo". This very small library is used
## by Java to find out which version of game to load based on
## the current build configuration.
##

include $(CLEAR_VARS)

LOCAL_MODULE := libBootInfo
LOCAL_SRC_FILES := bootinfo.cpp

include $(BUILD_SHARED_LIBRARY)

$(call import-module,libBdCore)
$(call import-module,libDebug)

ОБНОВЛЕНИЕ: На самом деле, использование суффикса имени модуля для разделения конфигураций сборки, на мой взгляд, является лучшим решением. Такой подход позволяет создавать и развертывать несколько конфигураций одновременно. Например, я использую его, когда мне нужно оптимизировать библиотеку либо под Tegra (без Neon), либо под Snapdragon (с Neon): до недавнего времени разместить в Play Store два отдельных APK было непросто, поэтому я упаковывал оба libv-neon.so и libv-tegra.so в lib/armeabi-v7a.

Я не знаю, какую логику включает ваша библиотека BootInfo, но если вы развертываете только одну библиотеку, вы можете избежать всех хлопот с помощью следующего кода в своем статическом конструкторе класса Java:

static {
    boolean loaded = false;
    while (!loaded) {
        try {
            System.loadLibrary("game" + nextExtensionAttempt);
            loaded = true;
        } 
        catch (Exception ex) {
        }
    }
}

Альтернативным подходом может быть переопределение выходного каталога ./obj. Для этого вы можете добавить в файл Application.mk следующую строку:

NDK_APP_OUT := obj$(MY_BUILD_EXTENSION)

Таким образом, все файлы .obj, .a и .so (до их установки в libs/armeabi) будут помещены в отдельный каталог для каждой конфигурации. Еще проще, вы можете указать параметр NDK_OUT в командной строке ndk-build, например.

ndk-build V=1 NDK_OUT=obj${MY_BUILD_EXTENSION}

Это очень просто, если вы используете Eclipse для обслуживания и выбора конфигураций. Это упрощает для Java загрузку модуля, поскольку он всегда будет иметь одно и то же имя. Но вы можете развертывать только одну конфигурацию за раз.

person Alex Cohn    schedule 20.10.2012
comment
Мне кажется, это не так. NDK не создает файлы .so и *.a в отдельных каталогах. Только объектные файлы (.o и *.o.d) помещаются в obj/local/armeabi/objs-debug/ и .../armeabi/objs/. Все скомпилированные библиотеки находятся в obj/local/armeabi/ без учета конфигурации сборки. Не говоря уже о том, что папка objs-debug, по-видимому, основана на флаге APP_OPTIM, который поддерживает только выпуск и отладку, но наш движок имеет гораздо больше конфигураций. - person Goose; 22.10.2012
comment
Использование $(MY_BUILD_CONFIG_EXTENSION) на самом деле очень хорошее решение. на мой взгляд. Это более или менее то, что я предпочитаю в своих проектах. Это дает очень хороший контроль над тем, какие конфигурации фактически развернуты на устройстве (подсказка, их может быть больше, чем _one!). Альтернативное решение см. в разделе обновление ответа. - person Alex Cohn; 23.10.2012
comment
Решение try/catch кажется мне отличным, но я не совсем понимаю, как предотвратить развертывание других версий библиотек на устройстве во время сеансов отладки (например, нажатие F11 в Eclipse). Например, в ...\obj\local\armeabi у меня есть и radgamed.so, и radgamer.so. Если я запускаю в конфигурации отладки, я хочу, чтобы он находил только библиотеки, оканчивающиеся на d, но похоже, что библиотеки, оканчивающиеся на r, также развертываются, поэтому моя попытка/поймать просто преуспеет при первой попытке. Кстати: Спасибо за подробные ответы! Я очень ценю это! - person Goose; 23.10.2012
comment
Я не испытываю проблемы, которая у вас есть. Я имею в виду, если я создам одну конфигурацию. тогда только один файл .so превратится в libs/armeabi/ и, в конечном итоге, в файл APK. Правда, в objs/ есть все линкованные (но неразрезанные) библиотеки. - person Alex Cohn; 24.10.2012
comment
Я вижу то же самое. libs/armeabi/ содержит только текущие библиотеки конфигурации сборки. Сам APK-файл в каталоге bin также содержит только то, что было в libs/armebi/. Я считаю, что проблема сейчас в том, что все версии библиотеки остаются на телефоне. Если я вручную удалю приложение и начну заново, все работает, но если я потом переключу конфиги, старая библиотека останется на телефоне. В зависимости от порядка загрузки он может сначала найти эту старую библиотеку. Я создал новый поток по этой конкретной проблеме: stackoverflow.com/questions/13041095/ - person Goose; 24.10.2012

Я никогда не мог заставить это работать совершенно правильно. В конце концов я просто создал пакетный файл, который записал пустой исходный файл. Этот пакетный файл выполняется как часть этапа сборки в Eclipse. Затем я включил этот исходный файл как часть моей библиотеки. Поскольку отметка времени меняется каждый раз, когда я строю, это заставляет ndk каждый раз пересобирать эту библиотеку. Я слежу за тем, чтобы библиотека была небольшой, а большая часть кода находилась в других библиотеках, что делало время сборки довольно коротким.

person Goose    schedule 15.11.2012