Android 带有 Google 测试的 NDK

Android NDK with Google Test

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

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

我没有找到明确的指南。

我关注了 this 文档:

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

但是不识别 #include gtest/gtest.h

此外,

您可以使用 CMake ndk-build (Android.mk),但不能同时使用。 NDK 的 gtest 部分未针对 CMake 进行检测。 https://github.com/android-ndk/ndk/issues/500

如果您选择 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.[=73= 中设置的 APP_ABI ]
  • 文件 Android.mkApplication.mk 在您使用 cmake.
  • 对于 gradle-3.3classpath 'com.android.tools.build:gradle:2.3.3',与当前 Android Studio 版本 2.3.3 一样,您可能需要在 build.gradle:

    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.

    [ 下找到=73=]

在共享库中测试来自 foo.cppfoo(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)

为了补充 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,这就是我们推送它的原因。 native-lib 的路径是通过设置 LD_LIBRARY_PATH 环境变量指定的。

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

注意事项:

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

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

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

我还通过 gdbservergdb 通过 Visual Studio 代码进行了远程调试。我宁愿使用 lldb 而不是,但我还没有弄清楚。要使完整调试正常工作,这个主题需要多个段落,所以如果您 lldb 使用 Visual Studio 代码或者想知道我是如何解决这个问题的,请随时私信我。

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

我基本上使用了 alex cohns 的答案。 ubuntu 18-04。必须在 android 工作室中构建。注意如果 运行 cmake outside stuidio 只为您当前的平台获取构建。 我不得不限制 armeabi 的构建。我的目标系统是这个,我正在构建的静态库只以这种形式存在。 由于我是静态 linking,所以我只需要下载和 运行 构建目标 footest。 这是 cmake 文件,它基本上是 Alex Cohn 的文件,添加了 link 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)

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

defaultConfig {
    externalNativeBuild {
        cmake {
            abiFilters "armeabi-v7a"
        }
    }

}

根据 Alex Cohn 和 donturner 的回答,这里是构建测试的完整解决方案,运行 它们作为 post-native-build 事件。在 CMakeLists.txt.
的末尾添加这个(对 filenames/variables 进行一些更改) 我创建了一个示例项目,其中包含更详细的解释: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\")

灵感来自 Alex Answer

我从 https://github.com/google/googletest 下载了 google 测试并在我的 CMakeLists.txt

中使用了以下代码
set(GOOGLETEST_ROOT C:/path/to/folder/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)
target_link_libraries(myExecutable gtest)