CMake include shared library and it's dependencies - c++

I am trying to include this C++ library with CMake. As I understood, the file that I need to include is located at lib/libbinacpp/lib/libbinacpp.so (relative to the library root folder).
So, I created a new folder, with two subfolders
src, which only contains one file: src/main.cpp (a simple Hello, World)
lib, which contains a folder binacpp, the result of cloning the library
On the top level, I have my CMakeLists.txt. First, I tried to write the following:
cmake_minimum_required(VERSION 3.20)
project(RebalancerBot)
set(CMAKE_CXX_STANDARD 20)
#Include binacpp
add_library(binacpp SHARED IMPORTED)
set_target_properties(binacpp PROPERTIES IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/lib/binacpp/lib/libbinacpp/lib/libbinacpp.so)
add_executable(RebalancerBot src/main.cpp)
#Link everything after adding the executable
target_link_libraries(RebalancerBot PRIVATE binacpp)
Which resulted in the error
make[3]: *** No rule to make target 'lib/binacpp/lib/libbinacpp/lib/libbinacpp.so', needed by 'RebalancerBot'. Stop.
To my surprise, I got a different error after replacing ${CMAKE_BINARY_DIR} with the actual root path of my folder (i.e /home/actual_path/). I found this surprising since I thought that ${CMAKE_BINARY_DIR}$ should exactly be the path to the root CMakeLists.txt file. Anyway, after the replacement, I got the following new errors:
/usr/bin/ld: warning: libcrypto.so.1.0.0, needed by ../lib/binacpp/lib/libbinacpp/lib/libbinacpp.so, not found (try using -rpath or -rpath-link)
/usr/bin/ld: warning: libwebsockets.so.11, needed by ../lib/binacpp/lib/libbinacpp/lib/libbinacpp.so, not found (try using -rpath or -rpath-link)
/usr/bin/ld: ../lib/binacpp/lib/libbinacpp/lib/libbinacpp.so: undefined reference to `lws_client_connect_via_info'
/usr/bin/ld: ../lib/binacpp/lib/libbinacpp/lib/libbinacpp.so: undefined reference to `lws_callback_on_writable'
... (the list goes on)
If I understand correctly, these errors are due to the dependencies of libbinacpp.so. How can fix them?
PS: It should be noted that, if I cd into lib/binacpp/src and make inside of this directory, everything runs without errors.
EDIT: Thanks to #AlanBirtles comment, I found that the error with CMAKE_BINARY_DIR was due to the fact that it unfolds to the directory I run cmake in. What I needed was CMAKE_SOURCE_DIR: the directory containing my cmake file.

CMake offers the possibility to have a build directory which differs from the source directory. A offer we should gladly accept, because it keeps our build system from messing with our source and we can have different build directories, depending on build type, compiler, target system, etc.
${CMAKE_BINARY_DIR} is a shortcut to your top-level build directory. CMake will generate all files here during the build.
${CMAKE_SOURCE_DIR} is where CMake is looking for your sources. CMake is not intended to make changes in your source directory.
If you copied the library to your source directory, you need to reference it with the right shortcut.
By the way, are you sure, you want to copy the library to the source directory? Because you don't need to. You can use find_file (https://cmake.org/cmake/help/latest/command/find_file.html), find_library (https://cmake.org/cmake/help/latest/command/find_library.html), find_path (https://cmake.org/cmake/help/latest/command/find_path.html) or find_program (https://cmake.org/cmake/help/latest/command/find_program.html) to checkout the location of the item you need and use this path to define your imported library.

Related

Undefined reference errors when trying to compile and simple ImageMagick program

I've been searching for a solution to this problem for a long time to no avail.
I am trying to compile this simple program:
#include <iostream>
#include <Magick++.h>
#include <Magick++/Image.h>
using namespace std;
using namespace Magick;
int main(int argc,char **argv) {
InitializeMagick("D:\\Programming\\CPPProjects\\NoteScripts\\Dependencies\\magick\\include");
Image image;
// image.read("arch");
// image.write("test.png");
}
Upon building, I get the following error:
CMakeFiles\main.dir/objects.a(main.cpp.obj):main.cpp:(.text+0x1c): undefined reference to `Magick::InitializeMagick(char const*)'
CMakeFiles\main.dir/objects.a(main.cpp.obj):main.cpp:(.text+0x28): undefined reference to `Magick::Image::Image()'
CMakeFiles\main.dir/objects.a(main.cpp.obj):main.cpp:(.text+0x34): undefined reference to `Magick::Image::~Image()'
collect2.exe: error: ld returned 1 exit status
From what I can tell, this is a linker error but I have no idea where I am going wrong with linking the libs needed.
I installed ImageMagick on Windows 10 from the ImageMagick downloads page with this installer: ImageMagick-7.1.0-50-Q16-HDRI-x64-dll.exe
I then copied the lib files from the lib folder under the installation directory into my project and then copied the include folder under the installtion directory into my project.
Here is what the project hierarchy looks like (Source Directory is NoteScripts):
My CMakeLists.txt consists of:
cmake_minimum_required(VERSION 3.10)
set( CMAKE_CXX_COMPILER "C:/Program Files/mingw-w64/x86_64-8.1.0-posix-seh-rt_v6-rev0/mingw64/bin/g++.exe")
set( CMAKE_C_COMPILER "C:/Program Files/mingw-w64/x86_64-8.1.0-posix-seh-rt_v6-rev0/mingw64/bin/gcc.exe" )
# set the project name
project("Notes")
include_directories(D:/Programming/CPPProjects/NoteScripts/Dependencies/magick/include)
# add the executable
add_executable(main main.cpp)
target_link_libraries(main D:/Programming/CPPProjects/NoteScripts/Dependencies/magick/lib/CORE_RL_Magick++_.lib)
target_link_libraries(main D:/Programming/CPPProjects/NoteScripts/Dependencies/magick/lib/CORE_RL_MagickCore_.lib)
target_link_libraries(main D:/Programming/CPPProjects/NoteScripts/Dependencies/magick/lib/CORE_RL_MagickWand_.lib)
If I comment out lines 9 and 10 where InitializeMagick() is called and where Image image is declared, the program compiles without error. I'm also aware that the order of the static libs listed out matters but trying out multiple combinations has resulted in the same error. I have also verfied the dependency order by sifting through the original source code and the reference path is Magick++ -> MagickCore -> MagickWand.
I am relatively new to the process of adding external dependencies to my C++ projects so this is unfamiliar territory (coming from languages with clean package managers). Any help as to how to fix this issue is greatly appreciated!
The typical (and easiest) way of handling dependencies in CMake is using its find_package command:
find_package(ImageMagick REQUIRED COMPONENTS MagickCore MagickWand Magick++)
// ...
target_link_libraries(main ${ImageMagick_LIBRARIES})
This method is available for ImageMagick with your CMake version. I'm not familiar with CMake on Windows, but find_package by default searches a number of standard (system) locations for the package's files. Since you have a custom setup, it should also be possible to specify a nonstandard search prefix to the command. Additionally, you could download external dependencies in a portable way with the FetchContent commands.
First of all, it is a pain to setup this thing if you are a newbie like me.
Now to the steps to dynamically link imagemagick libs with your C app:
go to https://github.com/ImageMagick/ImageMagick-Windows and follow the instructions there (Install Visual Studio dependencies - Clone the dependencies - Build configure.exe- Build ImageMagick)
in the step Build configure.exe, when running configure.exe, keep the default option selected when asked about the output library type (keep it set to dynamic)
in the Build ImageMagick step, when you open VisualDynamicMT.sln in visual studio, before you start the build, select all the solutions in the project, and right-click -> properties -> General -> C Language Standard -> choose Default (Legacy MSVC). After that, click on the top-most solution that contains all the other 196 solutions, and build it. watch the console for errors, I didn't get any errors with the configuration above.
After the latter step, go the VisualMagick folder (created from steps before), and you will see lib folder and bin folder. You're done, your dlls are in bin and your .lib file are in bin folder. Note that these files will be corresponding to build or release environments depending on what you selected in visual studio at the build step.
How do you use imagemagick now in your project regardless if you have imagemagick app installed on your pc or not? Lets create a new project in vscode, call it demo.
Create this project structure:
inside src you will put your C code.
inside deps create ImageMagick/lib and ImageMagick/bin and ImageMagick/include
inside ImageMagick/include place the same include files you said you got in your question.
inside ImageMagick/lib place the .lib files you got from above
inside ImageMagick/bin place the .dll files you got from above
now add this to your CMakeLists.txt:
cmake_minimum_required(VERSION 3.23)
project(demo-app C)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
file(GLOB SOURCE_FILES src/*.c)
add_executable(demo ${SOURCE_FILES})
include_directories(src)
# ImageMagick
if(WIN32)
add_definitions( -DMAGICKCORE_QUANTUM_DEPTH=16 )
add_definitions( -DMAGICKCORE_HDRI_ENABLE=0 )
include_directories(deps/ImageMagick/include)
target_link_directories(demo PRIVATE deps/ImageMagick/lib)
file(GLOB IMAGEMAGICK_LIBS deps/ImageMagick/lib/*.lib)
target_link_libraries(demo
"${IMAGEMAGICK_LIBS}"
)
add_custom_command(TARGET demo POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${PROJECT_SOURCE_DIR}/deps/ImageMagick/bin"
$<TARGET_FILE_DIR:demo>)
endif()
(The add_custom_command will copy the dlls to your executables path after every build)
Now write some magick code in you src directory.
ctrl + shift + A -> CMake select kit -> choose the Visual Studio community 2022 release -amd64 or change to what fits you if it doesn't work
ctrl + shift + A -> CMake Clean Rebuild
ctrl + shift + A -> CMake run without debugging

Imported target "Boost::system" includes non-existent path "/include"

I am a newbie with CMake please bear with me. I have a library (libvpop) which I created in c++ using some Boost components (system and date_time). I can link to it without a problem in windows but on Ubuntu, I am getting an error that implies the path to the boost include files cannot be found. Here is the simple CMakeLists.txt file.
cmake_minimum_required(VERSION 3.0.0)
set (Boost_DEBUG 1)
project(vpoplibuser)
find_package(fmt CONFIG REQUIRED)
find_package(Boost CONFIG REQUIRED system )
find_package(Boost CONFIG REQUIRED date_time)
add_executable(vpoplibuser vpoplibuser.cpp vpoplib.h)
find_library(VPLIB libvpop HINTS ~/projects/vpoplibuser/ )
message(STATUS "VPLib include dir: ${VPLIB}")
target_include_directories(vpoplibuser PUBLIC ${PROJECT_SOURCE_DIR} )
target_link_libraries(vpoplibuser PUBLIC ${VPLIB})
target_link_libraries(vpoplibuser PRIVATE fmt::fmt)
target_link_libraries(vpoplibuser PRIVATE Boost::system Boost::date_time)
When I run CMake, I get message:
CMake Error in CMakeLists.txt
Imported target "Boost::system" includes non-existent path "/include"
in its INTERFACE_INCLUDE_DIRECTORIES. Possible reasons include:
The path was deleted, renamed, or moved to another location.
An install or uninstall procedure did not complete successfully.
The installation package was faulty and references files it does not provide.
I have removed and reinstalled Boost. My Boost libraries are at /lib/x86_64-linux-gnu. I cannot figure out exactly where CMake is searching for the boost include file. When I inspect the _BOOST_INCLUDEDIR variable in boost_header-1.71.0/boost_headers-config.cmake it tells me _BOOST_INCLUDEDIR is "/include". I have read something about the PATH variable being an issue so I added /usr to the beginning of my PATH (there is a folder /usr/include/boost which has the boost .hpp files so I was making an assumption that is what CMake is looking for). I have been stuck on this for a couple of days so I would appreciate any advice from the expert community.
I have found a work around thanks to this article: https://github.com/VowpalWabbit/vowpal_wabbit/issues/3003
Something in the Boost cmake process is causing boost to look for the include files at /include when they are really at /usr/include. I created a symbolic link for /include to point to /usr/include and this allowed cmake to find everything. I have not solved the root cause but can move forward with this approach.

CMake not building a library when added as a subdirectory

I added the xgboost library as a git submodule of my project and I'm trying to add it to cmake as a subdirectory. Unfortunately it's not working. A simple hello world project with the following CMakeLists.txt replicates the error that I'm getting.
cmake_minimum_required(VERSION 3.2)
project(foo)
add_subdirectory(xgboost)
add_executable(${PROJECT_NAME} foo.cpp)
target_link_libraries(${PROJECT_NAME} xgboost)
After building the library there is nothing in the xgboost/lib directory so I get the following error.
clang: error: no such file or directory:
'/Users/.../myproject/xgboost/lib/libxgboost.dylib'
I think that the problem is generated in their CMakeLists file since they have two different targets. Maybe cmake is choosing the wrong target but I'm not familiar enough with cmake to figure it out. The following code is from xgboost's CMakeLists.
# Executable
add_executable(runxgboost $<TARGET_OBJECTS:objxgboost> src/cli_main.cc)
set_target_properties(runxgboost PROPERTIES
OUTPUT_NAME xgboost
)
set_output_directory(runxgboost ${PROJECT_SOURCE_DIR})
target_link_libraries(runxgboost ${LINK_LIBRARIES})
# Shared library
add_library(xgboost SHARED $<TARGET_OBJECTS:objxgboost>)
target_link_libraries(xgboost ${LINK_LIBRARIES})
set_output_directory(xgboost ${PROJECT_SOURCE_DIR}/lib)
#Ensure these two targets do not build simultaneously, as they produce outputs with conflicting names
add_dependencies(xgboost runxgboost)
My questions in order of importance are:
Is there any way to fix it without modifying xgboost's CMakeLists.txt file?
Is it reasonable to try to add xgboost to my project as a git submodule?
Is there any reason cmake is not instructing to build the library?
Note: There were several edits to this question since I tried to narrow down the problem and to provide more information.
(I would love to ask for few things beforehand in the comment section, but I have too low reputation to do so, so I will just give it a shot ;))
I have few suspects, and one of them is ${CMAKE_SOURCE_DIR} of the submodule's root CMakeLists.txt. Although the paths are set properly when you run that CMakeLists.txt alone, cmake gets confused the moment you add it as your subdirectory. Have you looked into another directories for your output binaries?
First I would suggest testing this hypothesis, and then I would suggest writing similar, but separate CMakeLists.txt file for xgboost library, and then substitute it in the project temporarily. Unfortunately the CMakeLists.txt filename is hardcoded and there is no possibility to have two files of that kind in one directory; so it seems that the answer to 1) is, that you rather have to change the file.
For the 2): as long as it does not require huge additional logic in your CMakeLists.txt, it makes sense. Other viable option is to create an install target, which you can use to install your xgboost library locally (using CMAKE_INSTALL_PREFIX(doc) variable), and then add the installation path to your CMAKE_LIBRARY_PATH(doc).

CMake and external dependency

I'd like to add an external dependency to my project. The one I'm trying to add is the Leptonica library as a submodule.
My project has the following directory structure:
|root
CMakeLists.txt
|-bin
|-build
|-buildsystem
|-executable
|-leptonica
|--CMakeLists.txt
|--cmake
|---Configure.cmake
|-production
In my root CMakeLists.txt file I added ADD_SUBDIRECTORY(${ROOT_DIR}/leptonica)
Unfortunately, CMake is not searching for Configure.cmake in the proper directory:
CMake Error at leptonica/CMakeLists.txt:107 (include):
include could not find load file:
Configure
CMake Error: File
<root>/cmake/templates/LeptonicaConfig-version.cmake.in does not exist.
CMake Error at leptonica/CMakeLists.txt:113 (configure_file):
configure_file Problem configuring file
When I build the project by myself, everything goes fine. In my opinion, the problem is with CMAKE_SOURCE_DIR. When using add_subdirectory it has the value of ROOT CMake instead ROOT/leptonica, so it's searching the wrong paths - as you can see in Leptonica CMake, it's used to determinate paths of its files.
What should be the proper way to fix this - should I set CMAKE_SOURCE_DIR to ROOT/leptonica just before calling add_subdirectory and set it back when it's finished, or does some other, more elegant solutions exist?
Not every CMake project is suitable for inclusion via add_subdirectory.
Among those are projects which uses CMAKE_SOURCE_DIR or CMAKE_BINARY_DIR variables.
However, inclusion via ExternalProject_Add (optionally wrapped with execute_process) always works.
Modifying variable CMAKE_SOURCE_DIR (and CMAKE_BINARY_DIR too) is a bad idea: this variable should be changed only by CMake itself. Otherwise you may get weird errors.
Instead, you may replace (automatically, with some script) all references to the variable with another variable, which is not used in the project. This new variable you may safely set before stepping into the subproject.
${CMAKE_SOURCE_DIR} and ${CMAKE_BINARY_DIR} are set relative to the top-level CMakeLists.txt. If you need something relative to your current CMakeLists.txt (leptonica), use ${CMAKE_CURRENT_SOURCE_DIR} and ${CMAKE_CURRENT_BINARY_DIR}.
If you're having trouble finding a cmake file like LeptonicaConfig-version.cmake.in, try appending the appropriate directory to ${CMAKE_MODULE_DIR}.
list(APPEND ${CMAKE_MODULE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/templates)
I prefer to use ${CMAKE_CURRENT_SOURCE_DIR} over ${CMAKE_SOURCE_DIR} any day because using the latter will break your build if you try to integrate it into a super-build later. If I need to pass my current top-level directory to subdirectories, then I do the following and use that later down the chain.
set( LEPTONICA_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR} )

Linking with CMakeLists: ld cannot find library

I have a CMakeLists.txt file, with the following:
target_link_libraries(${PROJECT_NAME} OpenNI2)
When I run cmake, I receive no errors. But when I run make, I receive the following error:
/usr/bin/ld: cannot find -lOpenNI2
However, I have a file called libOpenNI2.so in my build directory. So why can ld not find this? I thought that the build directory was on the search path for target_link_libraries?
Thanks!
That's because when linking, the linker doesn't look in the current directory but only in a set of predefined directories.
You need to tell CMake where the library is, for example by giving the full path to the library in the target_link_library command, or adding it as an imported library.
it works if adding like:
target_link_libraries(${PROJECT_NAME} /path_to_library_build/libOpenNI2.a)
details:
ld is looking for the libraries in a very short list of folders defined in
/etc/ld.so.conf
and it usually looks like following:
include /etc/ld.so.conf.d/*.conf
and actual paths list from those *.conf files usually is like:
# Legacy biarch compatibility support
/lib32
/usr/lib32
# Multiarch support
/usr/local/lib/x86_64-linux-gnu
/lib/x86_64-linux-gnu
/usr/lib/x86_64-linux-gnu
if your project linking library is not in the folder of this list, ld won't find it unless either a special linking variable set LD_LIBRARY_PATH with the path to your library or a complete path/library name provided in cmake target_link_libraries directive.
details on how to proper setup LD_LIBRARY_PATH variable discussed here