使用Google测试的Android NDK

时间:2017-09-26 15:03:16

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

我试图在Android Studio上使用GoogleTest。

根据我的理解,最新版本的NDK包括gtest。

我没有找到明确指南如何做到。

我关注this文件:

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

enter image description here

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

此外,

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

6 个答案:

答案 0 :(得分:13)

如果您选择 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.mk Application.mk 文件。
  • 对于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下轻松找到可执行文件。

在共享库中测试 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)

答案 1 :(得分:4)

为了添加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的路径。

答案 2 :(得分:1)

To带每个人的答案...并非此处的所有解决方案都能100%起作用,但我确实将此处的所有答案结合在一起,得出了对我有用的东西。我正在CMake中构建我们的库,其构建由Android Studio插件生成。我已经通过bashadb直接运行GoogleTests。

注意事项:

  • googletest official documentation本质上为我编译的所有平台提供了一个有效的版本。很琐碎!我必须添加Android Gradle插件使用Android交叉编译的args。我使用这种方法是因为我们的测试需要gmock。 NDK没有它(哇),所以我最终使用了官方说明。
  • 您的单元测试是可执行文件,因此,在您的CMakeLists.txt中,您必须使用add_executable(UnitTest "")创建它并在其中链接您的内容。
  • 就像每个人都说过的那样,${AS_STUDIO_LIBRARY_ROOT}/build/intermediates/cmake/${release|debug}/obj/${ARCH}包含您的编译源代码。这应该包括共享库和其他库以及单元测试可执行文件。此可执行文件不会进入最终的APK,因此您不必担心。
  • 通过执行以下操作防止文件权限问题。由于某种原因,直接将所有内容直接复制到/data/local/tmp/<PROJECT_NAME>然后进行chmod 777都将无法正常工作,尤其是在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>

我还通过Visual Studio Code通过gdbservergdb进行了远程调试。我宁愿使用lldb来代替,但我还没有弄清楚。要进行完整的调试,该主题将需要多个段落,因此,如果您lldb使用Visual Studio Code,或者对我如何解决此问题感到好奇,请随时告诉我。

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

答案 3 :(得分:0)

您可以使用CMake ndk-build(Android.mk),而不是两者。对于CMake来说,NDK的gtest部分并没有通过。 https://github.com/android-ndk/ndk/issues/500

答案 4 :(得分:0)

我基本上使用alex cohns的答案。 Ubuntu 18-04。必须在android studio中进行构建。注意:如果在stuidio外部运行cmake,则只能为当前平台构建。 我不得不限制构建armeabi。我的目标系统就是这个,而我所建立的静态库仅以这种形式存在。 由于我是静态链接的,所以只需要放下脚步并最快速地运行构建目标。 这是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)

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

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

}

答案 5 :(得分:0)

在Alex Cohn和donturner回答之后,这是构建测试并将其作为本地构建事件运行的完整解决方案。在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\")