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

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

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

我没有找到明确的指南。

我关注了this文档:

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

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

另外,

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

【问题讨论】:

    标签: c++ android-studio gradle android-ndk googletest


    【解决方案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.3classpath '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.1classpath '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 和模拟器上:
        1. @987654328首先@将您的资源、库和 googletest 可执行文件放入 /sdcard/&lt;PROJECT_NAME&gt; 文件夹
        2. adb shell mv /sdcard/&lt;PROJECT_NAME&gt; /data/local/tmp/.
        3. 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"
                }
            }
        
        }
        

        【讨论】:

          猜你喜欢
          • 2018-12-12
          • 2017-03-02
          • 2013-08-10
          • 2011-05-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多