Android NDK - 强制在配置更改时重新构建库

3
有没有一种方法可以强制Android NDK在Eclipse中更改构建配置时重新构建特定库?
我正在使用Android NDK构建C ++库来构建Android项目。 我使用带有Sequoyah插件的Eclipse。 一切都设置好了,也工作正常。
但是,我遇到了构建配置的问题。 您可以通过右键单击项目->属性,然后进入C / C ++ Build部分来管理构建配置。 这使您可以创建大多数C ++库某种方式依赖的传统Debug和Release构建。
以下是我的“调试”配置示例:
V=1 NDK_DEBUG=1 NDK_APPLICATION_MK=config/debug/Application.mk

这些工具很好用,但是当我在配置之间切换时,它不会触发我正在构建的库的重建。对于像Visual Studio这样的工具,每个构建配置都会转储到不同的目录中,这是可以预期的,但是在Eclipse中,所有东西都会被倾倒到同一个目录中。我被迫实际更改相关源文件才能触发重新构建。因此,最终的结果是,我在使用调试配置运行程序,但是链接到了在Release中构建的库。

因此我的问题是:是否有一种方法可以强制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)

1
在你的 Android.mk 文件中,构建 libBootInfo 的部分不应重新计算 LOCAL_PATH := $(call my-dir)。这是因为 $(call my-dir) 实际上会带来在此语句之前包含的最后一个 make file 的路径。如果你将所有 libBootInfo 部分移到 Android.mk 的顶部,它将可以平稳地工作。 - Alex Cohn
1
另外,另一个评论也不正确:“包含文件相对于NDK根目录”。实际上,包含文件LOCAL_C_INCLUDES是相对于_当前目录_的,通常是项目根目录。 - Alex Cohn
2个回答

2

NDK构建始终会刷新lib/armeabi中的.so库。另一方面,obj目录包含每个模块的调试和发布版本的单独树。

不幸的是,如果您的Android.mk执行了框架不支持的操作,则很容易搞砸这个设置。

例如,在您的情况下,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商店上放置两个单独的APK不容易,因此我将libv-neon.solibv-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-build 命令行上提供 NDK_OUT 参数,例如:

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

如果您使用Eclipse来维护和选择配置,这将非常容易。这使得Java加载模块变得容易,因为它将始终具有相同的名称。但是,您一次只能部署单个配置。


这对我来说似乎不是这种情况。NDK不会将*.so和*.a文件构建到单独的目录中。只有对象文件(.o和.o.d)被放置在"obj/local/armeabi/objs-debug/"和".../armeabi/objs/"中。实际编译的库都可以在"obj/local/armeabi/"中找到,而不考虑构建配置。更不用说,objs-debug文件夹似乎基于APP_OPTIM标志,该标志仅支持"release"和"debug",但我们的引擎有比这更多的配置。 - Goose
1
在我看来,使用 $(MY_BUILD_CONFIG_EXTENSION) 实际上是一个非常好的解决方案。这更或多或少是我在项目中喜欢的。这可以很好地控制实际部署到设备的配置(提示:可能不止一个!)。有关备选解决方案,请参见答案的“更新”部分。 - Alex Cohn
try/catch的解决方案对我来说似乎很好,但有一件事我不清楚,就是如何在调试会话期间阻止其他版本的库被部署到设备上(例如,在Eclipse中按F11)。例如,在“...\obj\local\armeabi”中,我有“radgamed.so”和“radgamer.so”两个库。如果我在Debug配置下运行,我希望它只能找到以“d”结尾的库,但似乎以“r”结尾的库也被部署了,所以我的try/catch只会在尝试第一个库时成功。顺便说一句:感谢您详细的回答!我非常感激! - Goose
我没有遇到你所说的问题。我的意思是,如果我构建一个配置文件,那么只有一个“.so”文件会进入“libs/armeabi/”,最终进入APK文件。当然,在“objs/”中有所有链接(但未剥离)的库。 - Alex Cohn
我看到了同样的事情。"libs/armeabi/"仅包含当前构建配置库。位于bin目录中的APK文件本身也仅包含"libs/armebi/"中的内容。我认为问题现在是所有版本的库都留在手机上。如果我手动删除应用并重新启动,一切都正常,但如果我切换配置,旧库将留在手机上。根据加载顺序,它可能首先找到旧库。我已经在这个特定问题上开始了一个新线程:http://stackoverflow.com/questions/13041095/android-ndk-shared-libraries-staying-on-device - Goose

0

我从未能够完全正确地使其工作。最后,我只是创建了一个批处理文件,该文件写出一个空的源文件。该批处理文件作为Eclipse构建步骤的一部分执行。然后我将该源文件包含在我的库中。由于时间戳每次构建时都会更改,因此它会欺骗ndk每次重新构建该库。我确保该库保持较小,并且大多数代码驻留在其他库中,使得构建时间非常短。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接