带有 Google 测试的 Android NDK

Posted

技术标签:

【中文标题】带有 Google 测试的 Android NDK【英文标题】:Android NDK with Google Test 【发布时间】:2018-03-07 21:13:14 【问题描述】:

我正在尝试在 android Studio 上使用 GoogleTest。

据我了解,最新版的NDK已经包含了gtest。

我没有找到明确的指南。

我关注了this文档:

于是,我打开了一个新项目,创建了jni文件夹和以下文件(里面的文件我写的完全是什么文件):

但它无法识别#include gtest/gtest.h

另外,

最后如何运行adb? 我创建了一个 android.mk 文件,但我应该在哪里调用它?

【问题讨论】:

【参考方案1】:

如果您选择 cmake 来驱动您的 externalNativeBuild(这是首选选项,根据 Android 开发人员NDK guide),那么您只需添加以下几行到您的 CMakeLists.txt

set(GOOGLETEST_ROOT $ANDROID_NDK/sources/third_party/googletest/googletest)
add_library(gtest STATIC $GOOGLETEST_ROOT/src/gtest_main.cc $GOOGLETEST_ROOT/src/gtest-all.cc)
target_include_directories(gtest PRIVATE $GOOGLETEST_ROOT)
target_include_directories(gtest PUBLIC $GOOGLETEST_ROOT/include)

add_executable(footest src/main/jni/foo_unittest.cc)
target_link_libraries(footest gtest)

如果您的构建成功,您将找到app/.externalNativeBuild/cmake/debug/x86/footest。从这里,您可以按照README.NDK 中的说明在模拟器或设备上运行它。


注意事项

确保 ABI 与您使用的目标匹配(指南对此不是很清楚)。 已构建的 ABI 列表由 build.gradle 中的 abiFilters 控制。在 Android Studio 中,即使是 ndk-build 也会忽略 Application.mk 中设置的 APP_ABI。 使用 cmake 时会忽略文件 Android.mkApplication.mk

对于gradle-3.3 和classpath 'com.android.tools.build:gradle:2.3.3',与当前Android Studio 2.3.3 版一样,您可能需要在build.gradle 中明确指定unittest 目标:

android  defaultConfig  externalNativeBuild  cmake  targets "foo_unittest" 

使用 Android Studio 3.0、gradle-4.1 和 classpath 'com.android.tools.build:gradle:3.0.0-beta6',在 app/build/intermediates/cmake/debug/obj 下更容易找到可执行文件。


在共享库中测试 foo.cpp 中的 foo(int x, int y) 函数(尽可能接近 NDK instructions),您需要在 CMakeLists.txt 脚本中添加更多行:

# build libfoo.so
add_library(foo SHARED src/main/jni/foo.cpp)
target_link_libraries(footest foo) 

您会在app/build/intermediates/cmake/debug/obj下找到libfoo.so手动复制到您的设备。

为减少麻烦,您可以使用STATIC 代替SHARED,或者简单地将foo.cpp 添加到footest 可执行文件中:

add_executable(footest src/main/jni/foo_unittest.cc src/main/jni/foo.cpp)

【讨论】:

谢谢!你帮了我很多!构建确实成功,但我找到的路径是:\app\.externalNativeBuild\cmake\debug\x86\CMakeFiles\footest.dir。而且我没有 libfoo.so 文件。我真的很抱歉,但从这里我不知道该怎么办..我可以从你那里得到更多帮助吗? 很公平,如果您不构建 libfoo.so,它就不会神奇地出现。要测试 foo.cpp 中的 foo(int x, int y) 函数,您需要在 CMakeLists.txt 脚本中添加更多行。我将这些添加到上面的答案中。 非常感谢!好吧,libfoo.so build 。但是在自述文件中写道,两个文件“libfoo.so”和“foo_unittest”都应该出现,我找不到测试所在的“foo_unittest”文件。我尝试在添加 libfoo.so 时添加文件,但没有成功。 在我的脚本中,文件名为footest;可以在app/.externalNativeBuild/cmake/debug/x86/中找到 @rozina 你可能会觉得这个讨论很有趣:github.com/android-ndk/ndk/issues/500【参考方案2】:

为了添加 Alex 的出色答案,您还可以使用 adb 部署和运行生成的测试二进制文件,方法是将以下内容添加到您的 CMakeLists.txt

find_program(ADB adb)
add_custom_command(TARGET footest POST_BUILD
    COMMAND $ADB shell mkdir -p /data/local/tmp/$ANDROID_ABI
    COMMAND $ADB push $<TARGET_FILE:native-lib> /data/local/tmp/$ANDROID_ABI/
    COMMAND $ADB push $<TARGET_FILE:footest> /data/local/tmp/$ANDROID_ABI/
    COMMAND $ADB shell \"export LD_LIBRARY_PATH=/data/local/tmp/$ANDROID_ABI\; /data/local/tmp/$ANDROID_ABI/footest\")

请注意,在上面的示例中,footest 依赖于共享库 native-lib,这就是我们推送它的原因。通过设置LD_LIBRARY_PATH 环境变量来指定native-lib 的路径。

【讨论】:

【参考方案3】:

捎带每个人的答案...这里并非所有的解决方案都 100% 有效,但我确实结合了这里的所有答案以获得对我有用的东西。我在 CMake 中构建我们的库,其构建是由 Android Studio 插件生成的。我已经通过bashadb 直接运行了我们的GoogleTests。

注意事项:

googletest official documentation 基本上给了我一个适用于我们编译的所有平台的工作版本。很琐碎!我必须添加 Android Gradle 插件用于 Android 交叉编译的参数。我使用这种方法是因为我们的测试需要 gmock。 NDK 没有它(哇),所以我最终使用了官方说明。 您的单元测试是可执行文件,因此在您的 CMakeLists.txt 中,您必须使用 add_executable(UnitTest "") 创建它并将您的内容链接到那里。 正如大家所说,$AS_STUDIO_LIBRARY_ROOT/build/intermediates/cmake/$release|debug/obj/$ARCH 包含您编译的源代码。这应该包括共享库和其他库以及单元测试可执行文件。这个可执行文件不会进入您的最终 APK,所以不用担心。 通过执行以下操作来防止文件权限问题。将所有内容直接复制到/data/local/tmp/&lt;PROJECT_NAME&gt; 然后chmod 777ing 由于某种原因,所有内容都无法正常工作,尤其是在 Pixel 2 和模拟器上:
    @987654328首先@将您的资源、库和 googletest 可执行文件放入 /sdcard/&lt;PROJECT_NAME&gt; 文件夹 adb shell mv /sdcard/&lt;PROJECT_NAME&gt; /data/local/tmp/. chmod 777 -R /data/local/tmp/&lt;PROJECT_NAME&gt;

这一切都完成后,你应该可以像这样运行你的 googletest:

adb shell LD_LIBRARY_PATH=/data/local/tmp/<PROJECT_NAME>; cd /data/local/tmp/<PROJECT_NAME>; ./<GOOGLE_TEST_EXECUTABLE>

我还通过 Visual Studio Code 通过gdbservergdb 进行远程调试。我更喜欢使用lldb,但我还没有弄清楚。要使完整调试工作的这个主题需要多个段落,所以如果你有 lldb 使用 Visual Studio Code 或好奇我是如何解决这个问题的,请随时 PM 我。

不要忘记在运行单元测试后删除文件,否则它们会保留在您的设备上。

【讨论】:

【参考方案4】:

根据 Alex Cohn 和 donturner 的回答,这里是构建测试并将其作为 post-native-build 事件运行的完整解决方案。在CMakeLists.txt 的末尾添加这个(对文件名/变量进行一些更改)。 我创建了一个带有更详细说明的示例项目:https://github.com/Mr-Goldberg/android-studio-googletest

# Build and link tests

set(GTEST_DIR $ANDROID_NDK/sources/third_party/googletest) # GTest included into NDK package. You may change to another distribution.

add_library(gtest STATIC $GTEST_DIR/src/gtest_main.cc $GTEST_DIR/src/gtest-all.cc)
target_include_directories(gtest PRIVATE $GTEST_DIR)
target_include_directories(gtest PUBLIC $GTEST_DIR/include)

add_executable(native-tests-lib ./test/native-libTests.cpp)
target_link_libraries(native-tests-lib native-lib gtest)

# Push and execute tests as post-build event.

set(TARGET_TEST_DIR /data/local/tmp/native-tests-lib/$ANDROID_ABI) # Directory on device to push tests.

message("ANDROID_SDK_ROOT: $ANDROID_SDK_ROOT") # ANDROID_SDK_ROOT should be passed as variable to this script.
find_program(ADB NAMES adb PATHS $ANDROID_SDK_ROOT/platform-tools)

add_custom_command(TARGET native-tests-lib POST_BUILD
        COMMAND $ADB shell mkdir -p $TARGET_TEST_DIR

        # Push libraries

        COMMAND $ADB push $<TARGET_FILE:native-tests-lib> $TARGET_TEST_DIR/
        COMMAND $ADB push $<TARGET_FILE:native-lib> $TARGET_TEST_DIR/

        # Execute tests

        COMMAND $ADB shell \"export LD_LIBRARY_PATH=$TARGET_TEST_DIR\; $TARGET_TEST_DIR/native-tests-lib\")

【讨论】:

adb shell /data/local/tmp/native-tests-lib/x86_64/native-tests-lib /system/bin/sh: /data/local/tmp/native-tests-lib/ x86_64/native-tests-lib:无法执行:权限被拒绝 native-tests-lib 权限:-rw-rw-rw- 。 X 在哪里?【参考方案5】:

您可以使用 CMake ndk-build (Android.mk),不能同时使用。 NDK 的 gtest 部分没有用于 CMake。 https://github.com/android-ndk/ndk/issues/500

【讨论】:

谢谢!那么如何将我在 Android.mk 上写的内容添加到 CMakeLists.txt 中呢?【参考方案6】:

我基本上使用了 alex cohns 的答案。 Ubuntu 18-04。必须从 android studio 中构建。注意如果在 stuidio 之外运行 cmake,则只能为您当前的平台构建。 我不得不限制 armeabi 的构建。我的目标系统就是这个,而我正在构建的静态库只以这种形式存在。 由于我是静态链接,我只需要下载并运行构建目标footest。 这是 cmake 文件,基本上是 Alex Cohn 的文件,添加了链接 2 个静态库:

# gtest setup
set(ANDROID_NDK /home/labhras/Android/Sdk/ndk/20.0.5594570)
set(GOOGLETEST_ROOT $ANDROID_NDK/sources/third_party/googletest)
add_library(gtest STATIC $GOOGLETEST_ROOT/src/gtest_main.cc $GOOGLETEST_ROOT/src/gtest-all.cc)
target_include_directories(gtest PRIVATE $GOOGLETEST_ROOT)
target_include_directories(gtest PUBLIC $GOOGLETEST_ROOT/include)

# link_directories(~/AndroidStudioProjects/Nativecgtest/app/src/main/cpp)

add_library(libtsl.a STATIC IMPORTED)
set_target_properties(libtsl.a
        PROPERTIES IMPORTED_LOCATION /home/labhras/AndroidStudioProjects/Nativecgtest/app/src/main/cpp/libtsl.a
        INTERFACE_INCLUDE_DIRECTORIES /home/labhras/AndroidStudioProjects/Nativecgtest/app/src/main/cpp/libtsl.a
        )


add_library(libtcl.a STATIC IMPORTED)
set_target_properties(libtcl.a
        PROPERTIES IMPORTED_LOCATION /home/labhras/AndroidStudioProjects/Nativecgtest/app/src/main/cpp/libtcl.a
        INTERFACE_INCLUDE_DIRECTORIES /home/labhras/AndroidStudioProjects/Nativecgtest/app/src/main/cpp/libtcl.a
        )

add_executable(footest  test2.cpp)
target_link_libraries(footest gtest libtsl.a libtcl.a)

我使用 app build.gradle 中的 abiFilters 行将构建限制为 armeabi:

defaultConfig 
    externalNativeBuild 
        cmake 
            abiFilters "armeabi-v7a"
        
    


【讨论】:

以上是关于带有 Google 测试的 Android NDK的主要内容,如果未能解决你的问题,请参考以下文章

sh 为Android开发构建Google Protobuf库。用android ndk独立工具链。

NDK

android studio怎么更新android-ndk

Android开发 SDK NDK下载

Android NDK Tools 下载链接大全

Android NDK Tools 下载链接大全