Linking GSL using CMake, undefined symbols - c++

I'm attempting to link GSL to my C++ code using cmake, and all goes well until I attempt to build. CMakeLists.txt:
cmake_minimum_required(VERSION 3.9)
include_directories(${project_SOURCE_DIR}/src)
link_directories(${project_BINARY_DIR}/src)
FIND_PACKAGE(Boost COMPONENTS system filesystem unit_test_framework REQUIRED )
INCLUDE_DIRECTORIES( ${Boost_INCLUDE_DIR} )
find_package(GSL REQUIRED)
set(CMAKE_CXX_STANDARD 14)
set (lib_SOURCES
../src/file.h
../src/file.cpp
# etc
)
target_include_directories(project PRIVATE ${PROJECT_SOURCE_DIR} ${GSL_INCLUDE_DIR})
target_link_libraries(sigma OpenMP::OpenMP_CXX GSL::gsl GSL:gslcblas)
When I add #include <gsl/gsl_integration.h> and call something from the library in the C++, e.g. a gsl_integration_workspace * w = gsl_integration_workspace_alloc (1000);, at the make I get an Undefined symbols for architecture x86_64:..., so the library isn't linking, but I don't know why. CMake clearly recognises GSL, but isn't liking to it.
I'm on Big Sur 11.3.1, GSL 2.6 installed via homebrew.

Fixed this by removing my CMakeCache.txt files and running cmake again.
I realized this as I was preparing minimal working example in response to Tsyvarev's comment. I hope this can help somebody.

Related

How to properly link OpenCV in a C++ cross-platform library for Android and iOS?

I am developing a C++ library, including OpenCV, which will be used in a cross-platform Xamarin solution through a wrapper and the NuGet packaging system (see this guide). I configured a CMakeLists.txt file but I simply cannot get OpenCV to be correctly linked for both static (iOS) and dynamic (Android) libraries.
I tried to change the OpenCV_DIR variable, install and build OpenCV from sources and manually include the content of the OpenCV_INCLUDE_DIRS variable but nothing worked. I also noticed that the linking works when only using cv::Point. But the linking does not work when using cv::Mat, which I do not understand the reason.
The following is the CMakeLists.txt that I am using :
cmake_minimum_required (VERSION 3.2)
project (MyLib C CXX)
enable_testing()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
MESSAGE(STATUS "CMAKE_CXX_FLAGS: " ${CMAKE_CXX_FLAGS})
# Source and headers files
set(SOURCES File1.cpp File2.cpp)
set(HEADERS File1.h File2.h)
# Library
if(BUILD_SHARED_LIBS)
add_library (MyLib SHARED ${SOURCES} ${HEADERS})
target_compile_definitions(MyLib PUBLIC IS_BUILDING_SHARED)
else()
add_library (MyLib STATIC ${SOURCES} ${HEADERS})
endif()
# Dependencies
set(OpenCV_DIR /usr/local/Cellar/opencv/4.5.0_1/lib/cmake/opencv4)
find_package(OpenCV REQUIRED)
message(STATUS "OpenCV_INCLUDE_DIRS = ${OpenCV_INCLUDE_DIRS}")
message(STATUS "OpenCV_LIBS = ${OpenCV_LIBS}")
message(STATUS "OpenCV_DIR = ${OpenCV_DIR}")
include_directories(${OpenCV_INCLUDE_DIRS})
target_link_libraries(MyLib ${OpenCV_LIBS})
The following shows the location of OpenCV's files that are used during the build process. Everything seems alright.
-- OpenCV_INCLUDE_DIRS = /usr/local/Cellar/opencv/4.5.0_1/include/opencv4
-- OpenCV_LIBS = opencv_calib3d;opencv_core;opencv_dnn;opencv_features2d;opencv_flann;opencv_gapi;opencv_highgui;opencv_imgcodecs;opencv_imgproc;opencv_ml;opencv_objdetect;opencv_photo;opencv_stitching;opencv_video;opencv_videoio;opencv_alphamat;opencv_aruco;opencv_bgsegm;opencv_bioinspired;opencv_ccalib;opencv_datasets;opencv_dnn_objdetect;opencv_dnn_superres;opencv_dpm;opencv_face;opencv_freetype;opencv_fuzzy;opencv_hfs;opencv_img_hash;opencv_intensity_transform;opencv_line_descriptor;opencv_mcc;opencv_optflow;opencv_phase_unwrapping;opencv_plot;opencv_quality;opencv_rapid;opencv_reg;opencv_rgbd;opencv_saliency;opencv_sfm;opencv_shape;opencv_stereo;opencv_structured_light;opencv_superres;opencv_surface_matching;opencv_text;opencv_tracking;opencv_videostab;opencv_viz;opencv_xfeatures2d;opencv_ximgproc;opencv_xobjdetect;opencv_xphoto
-- OpenCV_DIR = /usr/local/Cellar/opencv/4.5.0_1/lib/cmake/opencv4
Android
The following is the commands that I am using to build the Android dynamic library (.so). I have installed the NDK and am building for each ABI (x86, x86_64, armeabi-v7a, arm64-v8a).
cmake ../.. -DCMAKE_TOOLCHAIN_FILE=/Users/$USER/Library/Android/sdk/ndk-bundle/build/cmake/android.toolchain.cmake -DANDROID_NATIVE_API_LEVEL=21 -DANDROID_ABI=$abi_name -DBUILD_SHARED_LIBS=ON
cmake --build . --config Release
I directly get an error when building the library which is the following.
ld: error: /usr/local/Cellar/opencv/4.5.0_1/lib/libopencv_gapi.4.5.0.dylib: unknown file type
ld: error: /usr/local/Cellar/opencv/4.5.0_1/lib/libopencv_stitching.4.5.0.dylib: unknown file type
[...]
ld: error: /usr/local/Cellar/opencv/4.5.0_1/lib/libopencv_rapid.4.5.0.dylib: unknown file type
ld: error: too many errors emitted, stopping now (use -error-limit=0 to see all errors)
clang++: error: linker command failed with exit code 1 (use -v to see invocation)
iOS
The following is the commands that I am using to build the iOS static library (.a). I am using leetal's ​cmake toolchain file from this repository.
cmake ../.. -G Xcode -DCMAKE_TOOLCHAIN_FILE=../../ios.toolchain.cmake -DPLATFORM=OS64COMBINED -DBUILD_SHARED_LIBS=OFF
cmake --build . --config Release
The compilation of the static library seems to work because no error message is printed. However, when the library is used in the final Xamarin solution, the linked library cannot be found and the following error is shown.
Native linking failed, undefined symbol: cv::Mat::deallocate(). Please verify that all the necessary frameworks have been referenced and native libraries are properly linked in. (MT5210)
Question
What am I missing in order to properly compile and link OpenCV into my C++ library ?
I am working on macOS Big Sur and uses the following tools versions:
cmake : 3.20.0-rc5
ndk : 23.0.7196353
apple clang : 12.0.0
I hope that the description of my problem is clear enough and I thank you in advance for any help.
We had same problems, including Xamarin's DllNotFoundException with message from last comment, which led me to this topic. What fixed the exception for us in the end was linking statically to OpenCV *.a libs instead of linking to the shared libopencv_java4.so file. So we now have a huge 30MB nativelib.so file for each android ABI in build output, instead of a pair of small nativelib.so and libopencv_java4.so per ABI. CMakeLists looks like this:
set( OpenCV_DIR "~/opencv/build/OpenCV-android-sdk/sdk/native/jni" )
find_package( OpenCV REQUIRED )
target_link_libraries( # Specifies the target library.
nativelib
${OpenCV_LIBS})
Another thing in our project is we use OpenCV optional modules and had to create a custom OpenCV build, which I guess ensures our native library and OpenCV are compiled against same NDK version. I suppose using the prebuilt OpenCV distribution and compiling against a different NDK version could lead to problems too otherwise.

Undefined reference errors in simple boost serialization

I have a minimal example of Boost serialization where I try to save an integer in a binary archive file
Here is main.cpp:
#include <iostream>
#include <fstream>
#include <boost/archive/binary_oarchive.hpp>
int main() {
int t = 0;
std::ofstream file("Test.bin");
boost::archive::binary_oarchive archive(file);
archive << t;
file.close();
return 0;
}
and here is the CMake file:
cmake_minimum_required(VERSION 3.15)
project(Test)
set(CMAKE_CXX_STANDARD 17)
find_package(Boost REQUIRED serialization)
add_executable(Test main.cpp)
if(Boost_FOUND)
include_directories(${Boost_INCLUDE_DIRS})
target_link_libraries(Test ${Boost_LIBRARIES})
endif()
When I try to run this program in CLion, I get a large list of undefined reference errors as shown here:
https://pastebin.com/8uX9MZFf
I have setup Boost using vcpkg package manager. I'm compiling using Mingw-w64. The CMake file loads without errors (only a warning that says "New Boost version may have incorrect or missing dependencies and imported targets," though I've heard this warning isn't of concern, as it just means the current version of CMake isn't aware of the newest version of Boost).
I've tried to look for solutions to this everywhere, but I can't seem to find anything that works here. Any help would be appreciated.
I'm using cmake 3.15.3, boost 1.73.0 and mingw-w64 6.0.
EDIT
I uninstalled and reinstalled Boost without using the package manager, and tried getting the serialization library again. In this context, CMake runs into errors saying it can't find Boost with serialization (Though it can find Boost alone). I set Boost_DEBUG to ON and looked at the output, and noticed the following things:
_boost_COMPILER = "-mgw81" (guessed)
CMake seems to guess that the compiler I used to compile boost was mgw81. I'm guessing it got the 8.1 from my gcc version, which is correct.
Searching for SERIALIZATION_LIBRARY_RELEASE: boost_serialization-mgw81-mt-x64-1_73;boost_serialization-mgw81-mt-x64;...
As a result of that compiler selection, it searches for a file with "-mgw81" in the name. The problem is that the library files generated when I built boost are named like so:
libboost_serialization-mgw8-mt-x64-1_73.a
This says "-mgw8" instead of "-mgw81". I don't know how to correct CMake or build boost in such a way that this conflict doesn't happen. I've tried rebuilding boost with toolset=gcc-8.1 instead of toolset=gcc, but I still get "-mgw8" in the library file names.
EDIT 2
I found the solution to the above issue. I've posted it below.
After realizing that the issue was what I mentioned in EDIT, I looked further into how that issue could be resolved, and I found out you can manually set the compiler that is used to search through the variable Boost_COMPILER.
I changed my CMake file to the following:
cmake_minimum_required(VERSION 3.15)
project(Test)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_INCLUDE_PATH ${CMAKE_INCLUDE_PATH} "C:/boost_1_73_0")
set(CMAKE_LIBRARY_PATH ${CMAKE_LIBRARY_PATH} "C:/boost_1_73_0/libs")
set(Boost_DEBUG ON)
set(Boost_COMPILER -mgw8)
set(Boost_ARCHITECTURE -x64)
set(BOOST_ROOT C:/boost)
set(BOOST_INCLUDEDIR C:/boost/include/boost-1_73/boost)
set(BOOST_LIBRARYDIR C:/boost/lib)
set(BOOST_NO_SYSTEM_PATHS ON)
find_package(Boost REQUIRED serialization)
add_executable(Test main.cpp)
if(Boost_FOUND)
include_directories(${Boost_INCLUDE_DIRS})
target_link_libraries(Test ${Boost_LIBRARIES})
endif()
I believe the critical changes here were setting Boost_COMPILER and Boost_ARCHITECTURE. I realized Boost_ARCHITECTURE needed to be set from this question: Linking boost in CLion project.
With this CMake file, my main.cpp file from above ran properly.

How to build static library with bundled dependencies - CMake

I am currently using CMake to create a static library which utilizes a few of the static libraries from OpenCV 4 ( core imgcodecs video highgui imgproc ). My intention is to be able to bundle all of the required OpenCV static libraries into my own library so that I can distribute it as one library. Additionally, I want for the user of my library to not have to install OpenCV 4 on their system (but do not mind if the user has to do simple installs using apt-get install). I know there are tools for bundling static libraries (such as using ar for linux).
However, where I really am having the issue is with all the dependencies of OpenCV (such as libjpeg, libpng, etc). I don't necessarily mind if these libraries are bundled with mine or linked dynamically as they are relatively easy to install (can be installed with sudo apt-get install, whereas opencv4 needs to be built from source).
What is the best way to go about doing this?
This is my current CMakeLists.txt
It is currently working, but that is because I am using find_package(OpenCV REQUIRED) (which defeats the purpose of what I am trying to do). When I remove that line, the linker complains about not being able to find the OpenCV dependencies.
cmake_minimum_required(VERSION 2.8)
project(myproject)
set(CMAKE_CXX_STANDARD 14)
include_directories(${CMAKE_CURRENT_LIST_DIR}/include)
link_directories(${CMAKE_CURRENT_LIST_DIR}/lib)
find_package(OpenMP REQUIRED)
find_package(OpenCV REQUIRED)
set(JSON_BuildTests OFF CACHE INTERNAL "")
add_subdirectory(nlohmann_json)
list(APPEND LINKER_LIBS opencv_core opencv_highgui opencv_video opencv_imgcodecs libmxnet.so libncnn.a nlohmann_json::nlohmann_json)
file(GLOB SRC${CMAKE_CURRENT_LIST_DIR}/src/*.cpp${CMAKE_CURRENT_LIST_DIR}/main.cpp)
add_library(myproject ${SRC})
target_link_libraries(myproject ${LINKER_LIBS} ${OpenMP_CXX_FLAGS})
To elaborate on my question. I build my project which generates libmyproject.a. I then take this library and will eventually extract the symbols from the OpenCV libs (libopencv_core.a libopencv_highgui.a libopencv_imgcodecs.a libopencv_video.a) and add them to my lib (for the time being, I have not yet done this step, which is why in the below example I am linking libopencv_*). I then use my library in a new project, for which the CMakeLists.txt is shown below:
cmake_minimum_required(VERSION 2.8)
project(myproject-driver)
set(CMAKE_CXX_STANDARD 14)
include_directories(${CMAKE_CURRENT_LIST_DIR}/include)
link_directories(${CMAKE_CURRENT_LIST_DIR}/lib)
find_package(OpenMP REQUIRED)
add_executable(myproject-driver main.cpp)
target_link_libraries(myproject-driver myproject libncnn.a ${OpenMP_CXX_FLAGS} libmxnet.so libopencv_core.a libopencv_highgui.a libopencv_imgcodecs.a libopencv_video.a)
Building this generates the following errors:
Linking CXX executable myproject-driver
/usr/bin/ld: /home/nchafni/Cyrus/myproject/lib/libopencv_imgcodecs.a(grfmt_jpeg.cpp.o): undefined reference to symbol 'jpeg_default_qtables##LIBJPEG_8.0'
//usr/lib/x86_64-linux-gnu/libjpeg.so.8: error adding symbols: DSO missing from command line
collect2: error: ld returned 1 exit status
How can I fix this. Is there some CMake command which will link all these dependencies for me? Do I need to manually track down each dependency of those libopencv_* libs and link those manually? Once again, this is assuming that the person using libmyproject.a can't use find_package(OpenCV REQUIRED) as it won't be defined as they have not installed OpenCV on their machine.
First of all, don't use the super old and outdated version 2.8 of CMake. CMake 3.x is so much more powerful and pretty straightforward to use.
Some tips for modern CMake.
Don't use file(GLOB), see here why that is.
Don't use directory wide instructions, rather use target instructions, e.g. target_include_directories vs. include_directories.
Don't use string variables like ${<PACKAGE_NAME>_LIBRARIES}, rather use targets, e.g. <Package_NAME>::lib
When using targets instead of string variables, all the properties (including LINK_INTERFACE) of that target will be populated to the library/executable when calling target_link_libraries, so no more include_directories,link_directories, etc.
myproject
cmake_minimum_required(VERSION 3.14)
project(myproject)
set(CMAKE_CXX_STANDARD 14)
find_package(OpenMP REQUIRED)
find_package(OpenCV REQUIRED)
set(JSON_BuildTests OFF CACHE INTERNAL "")
add_subdirectory(nlohmann_json)
set(SOURCES ...) # list all the source files here
add_library(myproject ${SOURCES})
target_include_directories(myproject PUBLIC # give it a scope
${CMAKE_CURRENT_LIST_DIR}/include
)
target_link_libraries(myproject PUBLIC # give it a scope
opencv_core # using the target, you will get all LINK_LIBRARIES
opencv_highgui
opencv_video
opencv_imgcodecs
libmxnet.so # where is this coming from?
libncnn.a # where is this coming from?
nlohmann_json::nlohmann_json
OpenMP::OpenMP_CXX ## linking against a target, CXX_FLAGS will be populated automatically
)
myprojec-driver
cmake_minimum_required(VERSION 3.14)
project(myproject-driver)
set(CMAKE_CXX_STANDARD 14)
add_executable(myproject-driver main.cpp)
target_link_libraries(myproject-driver PUBLIC # give it a scope
myproject # gets all dependencies through the LINK_INTERFACE
)

Compiling with CMake and Boost unit tests

I'm relatively new to C++, and I'm trying to compile a project using CMake with the following #include:
#include <boost/test/unit_test.hpp>
I get the following error
undefined symbols for architecture x86_64:
"boost::unit_test::unit_test_log_t::instance()", referenced from:
___cxx_global_var_init in functions1.cpp.o
___cxx_global_var_init in functions2.cpp.o
___cxx_global_var_init in main.cpp.o
ld: symbol(s) not found for architecture x86_64
I'm pretty sure this is something to do with my CMakeLists.txt, so here it is:
cmake_minimum_required(VERSION 3.13)
project(MyProject)
set(CMAKE_CXX_STANDARD 14)
include_directories(.)
include_directories(/usr/local/include/)
add_executable(MyProject
functions1.cpp
functions1.h
functions2.cpp
functions2.h
main.cpp
main.h
utillity.cpp
utillity.h)
set(BOOST_ROOT "/usr/local/Cellar/boost/1.69.0_2")
find_package(Boost COMPONENTS filesystem system unit_test_framework REQUIRED)
#find_package(Boost 1.69.0)
if(NOT Boost_FOUND)
message(FATAL_ERROR "Could not find boost!")
endif()
include_directories (${Boost_INCLUDE_DIRS})
I'm on OSX Mojave, and installing with brew install boost. I'm aware there are several posts out there that report very similar issues, but none of the solutions seem to work for me.
Edit:
Have adjusted my CMakeLists to the following based on Guillaume's suggestion below.
make_minimum_required(VERSION 3.13)
project(MyProject)
set(CMAKE_CXX_STANDARD 14)
add_executable(MyProject
functions1.cpp
functions1.h
functions2.cpp
functions2.h
main.cpp
main.h
utillity.cpp
utillity.h)
set(BOOST_ROOT "/usr/local/Cellar/boost/1.69.0_2")
find_package(Boost COMPONENTS filesystem system test REQUIRED)
target_include_directories(MyProject PUBLIC ".")
target_link_libraries(MyProject PUBLIC
Boost::filesystem Boost::system Boost::test)
I understand that this is, in principle, better, but it's giving me:
Unable to find the requested Boost libraries.
Boost version: 1.69.0
Boost include path: /usr/local/Cellar/boost/1.69.0_2/include
Could not find the following Boost libraries:
boost_test
Some (but not all) of the required Boost libraries were found. You may need to install these additional Boost libraries. Alternatively, set BOOST_LIBRARYDIR to the directory containing Boost libraries or BOOST_ROOT to the location of Boost.
Edit 2:
Have tried edits and upgrading to boost 1.7, but still have:
Could not find a package configuration file provided by "boost_test"
(requested version 1.70.0) with any of the following names:
boost_testConfig.cmake
boost_test-config.cmake
Add the installation prefix of "boost_test" to CMAKE_PREFIX_PATH or set
"boost_test_DIR" to a directory containing one of the above files. If
"boost_test" provides a separate development package or SDK, be sure it
has been installed.
You have to link properly to boost instead of adding include directory flags.
Linking libraries in cmake will apply all required property to use boost onto your targets.
First off, don't do that:
include_directories(.)
include_directories(/usr/local/include/)
This is asking for link error. It will enable you to use headers of libraries you don't link properly to. This will cause linking errors, even within your own project.
cmake_minimum_required(VERSION 3.13)
project(MyProject)
set(CMAKE_CXX_STANDARD 14)
add_executable(MyProject
functions1.cpp
functions1.h
functions2.cpp
functions2.h
main.cpp
main.h
utillity.cpp
utillity.h)
list(APPEND CMAKE_PREFIX_PATH "/usr/local/Cellar/boost/1.69.0_2")
set(Boost_ADDITIONAL_VERSIONS "1.69.0" "1.69")
find_package(Boost COMPONENTS filesystem system test REQUIRED)
# Not needed
#if(NOT Boost_FOUND)
# message(FATAL_ERROR "Could not find boost!")
#endif()
# include_directories (${Boost_INCLUDE_DIRS})
target_include_directories(MyProject PUBLIC ".")
# adds include directories, definitions and link libraries
target_link_libraries(MyProject PUBLIC
Boost::filesystem Boost::system Boost::test
)

Static linking DCMTK library

I use DCMTK in my application and for compilation use cmake file. cmake finds all libraries (at least headers, because in compiles source files to .o files) the only problem is that during linking it tries to find dynamic libraries for DCMTK. I compiled one as static, so I do not have .so files. As a result it gives me error :No rule to make target /usr/lib/libdcmdata.so, needed by dcm_seg. Stop.
I use Ubuntu 14.04 x64.
It confuses me pretty much. So, what's the problems?
cmake file:
cmake_minimum_required(VERSION 2.6)
project(dcm_segm)
set(CMAKE_CXX_FLAGS "-std=c++0x ${CMAKE_CXX_FLAGS} -g -ftest-coverage -fprofile-arcs")
set(Boost_USE_STATIC_LIBS ON)
set(OpenCV_USE_STATIC_LIBS ON)
set(DCMTK_USE_STATIC_LIBS ON)
set(OpenCV_STATIC ON)
find_package( VTK REQUIRED )
find_package( OpenCV REQUIRED )
find_package( Boost COMPONENTS system filesystem REQUIRED )
find_package( DCMTK REQUIRED )
include(${VTK_USE_FILE} )
link_directories(${OpenCV_LIB_DIR})
add_executable(dcm_seg main.cpp DICOMin.cpp Ensemble.cpp Ensemble3dExtension.cpp point_3d.cpp RegionGrow.cpp)
target_link_libraries(dcm_seg ${VTK_LIBRARIES} ${OpenCV_LIBS} ${DCMTK_LIBRARIES} ${Boost_LIBRARIES})
Can you check the content of ${DCMTK_LIBRARIES} (it should be a list of paths to DCMTK static libraries) ?
you can also check the following CMake entries during the CMake configuration:
DCMTK_DIR /path/to/DCMTK/install
DCMTK_config_INCLUDE_DIR /path/to/DCMTK/install/include/dcmtk/config
DCMTK_dcmdata_INCLUDE_DIR /path/to/DCMTK/install/dcmdata/include/dcmtk/dcmdata
DCMTK_dcmdata_LIBRARY_DEBUG /path/to/DCMTK/install/dcmdata/libsrc/libdcmdata.a
DCMTK_dcmdata_LIBRARY_RELEASE /path/to/DCMTK/install/dcmdata/libsrc/libdcmdata.a
[...]
Another hint: I noted in the past that find DCMTK from a build instead of an install not always works properly.
If you have trouble finding DCMTK with the script provided with CMake
(${DCMTK_LIBRARIES} doesn not content the path to you static DCMTK libs for example) you can try to use this alternative script