Issue
i have a Android studio project which uses Native C++ code with JNI, however when i try to Build my project, it throws error: undefined reference to 'std::basic_ostream<char...
errors.
I am using ubuntu and i believe the problem is caused by IDE using gcc compiler and tries to compile it as C code. When i try to run simple hello world program by running gcc -o foo foo.cpp
in terminal, it gives same error. When i change gcc to g++, everything works fine.
But how do i change the compiler in IDE? I'm kinda new to using CMake and i think i need to use cmake's enable_language function but even i add enable_language(CPP) line to my CMakeLists.txt, it gives same errors.
Here is my CMakeLists.txt :
cmake_minimum_required(VERSION 3.4.1)
include_directories("/usr/include/x86_64-linux-gnu/c++/9")
include_directories("/usr/include/c++/9")
include_directories("/usr/include/x86_64-linux-gnu")
include_directories("/usr/include")
file(GLOB srcs *.cpp *.c)
file(GLOB hdrs *.hpp *.h)
include_directories(${OpenCV_DIR}/jni/include)
include_directories(${TESSERACT_INCLUDE_DIRS})
include_directories(${LEPTONICA_INCLUDE_DIRS})
add_library( lib_opencv SHARED IMPORTED )
set_target_properties(lib_opencv PROPERTIES IMPORTED_LOCATION ${OpenCV_DIR}/libs/${ANDROID_ABI}/libopencv_java4.so)
add_library(
native-lib
SHARED
#
native-lib.cpp )
find_library(
log-lib
log )
target_link_libraries(
native-lib
lib_opencv
${log-lib}
${TESSERACT_LIBRARIES}
${LEPTONICA_LIBRARIES})
Thanks!
ps: My G++ and Gcc versions are 9.3.0, build-essentials are already installed.
Solution
Your CMakeLists.txt is a whole lot of mess. Lets go through some potential problems, one by one, and see if it starts working.
The solution is probably in point 7, but I already wrote it all so I am leaving it here. Let me know if my answer helped you.
1. Does Android Studio really use this CMakeLists.txt file for building?
There are two usual build systems for NDK - older ndk-build and newer CMake. Check your Gradle build file to see if this CMakeLists.txt is really used. Here is documentation about how it should work: https://developer.android.com/studio/projects/gradle-external-native-builds.
And of course, you can make a deliberate syntax error in your CMakeLists.txt file to see if the build systems starts complaining about it.
And note that CMake should not by invoked separately when building for Android. It should be invoked from inside the Gradle script that is used by Android Studio for building the whole project.
2. Do all source files that use C++ libraries have .cpp extension?
CMake determines what compiler to use (C or C++) based on the extension of the file. If it is a .c file, then C compiler is used. If it is a .cpp file, then C++ compiler is used.
Also note that Android NDK now uses Clang (from LLVM project) instead of GCC, so GCC version or custom GCC invocations might not be that relevant.
You can also make CMake verbose - to print all the build commands, to see which commands are exactly executed. Add set(CMAKE_VERBOSE_MAKEFILE ON CACHE BOOL "ON")
to your CMakeLists.txt file to make it verbose.
3. Is correct C++ library used?
Symbols you are missing (something from std::basic_ostream) are defined in C++ library (I think) that needs to be linked to your library. By default, libc++ should be linked and that is the right choice. But there is an option that can mess with that. So check your gradle build file for a ANDROID_STL
variable and remove any assignments to that variable. The issue of C++ library in NDK is explained here: https://developer.android.com/ndk/guides/cpp-support#cmake
4. Missing source files.
When you call add_library, you are only adding one source file - native-lib.cpp. So only this one file will be compiled in the library (apart from lib_opencv that is linked to it). The line file(GLOB srcs *.cpp *.c)
assigns all cpp and c files to the ${srcs} variable but that variable is then left unused. You could change add_library to use that variable: add_library( native-lib SHARED ${srcs} )
.
If you want to also include files from subdirectories, use GLOB_RECURSE instead of GLOB in your file(GLOB srcs *.cpp *.c)
command.
The line file(GLOB hdrs *.hpp *.h)
is completely useless as far as I can tell. I don't see how a list of all header files could be useful in a CMake script.
5. Use new target-based CMake configuration.
Older CMake used file-based configuration, now target-based configuration is encouraged. File-based commands are for example link_libraries and include_directories, their target-based alternatives are target_link_libraries and target_include_directories. You are mixing those two styles. Basically, when a command has its target_ variant, you always want to use it.
You need to first create your target using add_library( native-lib SHARED ${srcs} )
, the targets name is native-lib. Then add all include directories and link all libraries to it using target-based commands.
Also the way you add lib_opencv is missing a find_package command. Use find_package(OpenCV REQUIRED)
and then target_link_libraries( native-lib PUBLIC ${OpenCV_LIBS} )
to link it.
The way you are adding logging library is OK, because the log-lib library is part of native API (https://developer.android.com/ndk/guides/stable_apis#logging) so it is more straightforward.
With CMake, you need to search internet or documentation to find out, which commands are needed to add each library to your target (unless you are compiling the library from sources). Tthough it is almost always some combination of find_package and target_link_libraries.
6. Missing project information. I am not sure how important that is, but usually you start your main CMakeLists.txt with something like this:
cmake_minimum_required( VERSION 3.4.1 )
project( my_special_project )
set( CMAKE_CXX_STANDARD 14 )
set( CMAKE_CXX_STANDARD_REQUIRED True )
7. Don't manually include system directories.
Thinking about it, this is probably the problem. Toss out all of these lines:
include_directories("/usr/include/x86_64-linux-gnu/c++/9")
include_directories("/usr/include/c++/9")
include_directories("/usr/include/x86_64-linux-gnu")
include_directories("/usr/include")
Ndk uses its own toolchain, so never manualy add paths from your system toolchain to your NDK project.
I would discourage doing this also on any other project. It is not portable and things like that should be taken care of indirectly, by CMake itself.
Answered By - Ivorne
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.