Android NDK With Google Test
Answer :
If you choose cmake to drive your externalNativeBuild (and this is the preferred option, according to Android Developers NDK guide), then you can simply add the following lines to your 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)
If your build succeeds, you will find app/.externalNativeBuild/cmake/debug/x86/footest
. From here, you can follow the instructions in README.NDK to run it on emulator or device.
Notes:
- make sure that the ABI matches the target you use (the guide is not very clear about this).
- the list of ABI's that are built is controlled by abiFilters in build.gradle. In Android Studio, even ndk-build ignores APP_ABI set in Application.mk.
- the files Android.mk and Application.mk are ignored when you use cmake.
for gradle-3.3, and
classpath 'com.android.tools.build:gradle:2.3.3'
, as in the current Android Studio release 2.3.3, you may need to explicitly specify the unittest target in build.gradle:android { defaultConfig { externalNativeBuild { cmake { targets "foo_unittest" }}}}
with Android Studio 3.0, gradle-4.1, and
classpath 'com.android.tools.build:gradle:3.0.0-beta6'
the executable is easier to find underapp/build/intermediates/cmake/debug/obj
.
To test the foo(int x, int y) function from foo.cpp in a shared library (to make is as close as possible to the NDK instructions), you need some more lines in your CMakeLists.txt script:
# build libfoo.so add_library(foo SHARED src/main/jni/foo.cpp) target_link_libraries(footest foo)
You will find libfoo.so to copy manually to your device under app/build/intermediates/cmake/debug/obj
.
To reduce the hassle, you can use STATIC
instead of SHARED
, or simply add foo.cpp to footest executable:
add_executable(footest src/main/jni/foo_unittest.cc src/main/jni/foo.cpp)
Just to add to Alex's excellent answer, you can also deploy and run the resulting test binary using adb
by adding the following to your 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\")
Note that in the above example footest
is dependent on the shared library native-lib
which is why we push that. The path to native-lib
is specified by setting the LD_LIBRARY_PATH
environment variable.
To piggyback everyone's answers... Not all the solutions here worked 100%, but I did combine all the answers here to get something that worked for me. I'm building our libraries in CMake, whose build is generated by the Android Studio plugin. I've gotten our GoogleTests running directly via bash
and adb
.
Caveats:
- The googletest official documentation essentially gave me a working version for all the platforms we compile. Very trivial! I had to add the args that the Android Gradle plugin uses cross-compile for Android. I used this method since our tests require gmock. The NDK doesn't have it (much wow), so I ended up using the official instructions.
- Your unit tests are executables, so in your CMakeLists.txt, you must create it using
add_executable(UnitTest "")
and link your stuff there. - Like everyone has said,
${AS_STUDIO_LIBRARY_ROOT}/build/intermediates/cmake/${release|debug}/obj/${ARCH}
houses your compiled source. This should include shared libraries and other libs as well as the unit test executable. This executable won't make it to your final APK, so no worries there. - Prevent file permission issues by doing the following below. Copying everything to
/data/local/tmp/<PROJECT_NAME>
directly thenchmod 777
ing everything will not work for some reason, especially on the Pixel 2 and the emulator:adb push
ing your resources, libraries, and googletest executable to the/sdcard/<PROJECT_NAME>
folder firstadb shell mv /sdcard/<PROJECT_NAME> /data/local/tmp/.
chmod 777 -R /data/local/tmp/<PROJECT_NAME>
After this is all done, you should be able to run your googletest like this:
adb shell LD_LIBRARY_PATH=/data/local/tmp/<PROJECT_NAME>; cd /data/local/tmp/<PROJECT_NAME>; ./<GOOGLE_TEST_EXECUTABLE>
I also got remote debugging working via gdbserver
and gdb
through Visual Studio Code. I'd prefer to use lldb
instead but I haven't figured it out yet. This topic to get full debugging to work will require multiple paragraphs, so feel free to PM me if you got lldb
working with Visual Studio Code or are curious how I solved this issue.
Don't forget to remove the files after running the unit tests since they'll stay on your device otherwise.
Comments
Post a Comment