cmake include external library - c++

I have 2 static libraries A:\\A.lib, B:\\B.lib I want to link to using MSVS2019
This is my cmakelists.txt
add_executable(main ${src}/main.c)
SET(A "A:/")
SET(B "B:/")
find_library(AA "${A}/A.lib")
find_library(BB "${B}/B.lib")
target_link_libraries(main ${AA})
target_link_libraries(main ${BB})
I have got a lot of unresolved external symbols then.

Don't hardcode paths to external libs.
cmake_minimum_required(VERSION 3.18)
project(foo)
add_executable(bar ...)
find_library(libA NAMES A REQUIRED)
find_library(libB NAMES B REQUIRED)
target_link_libraries(bar PRIVATE ${libA} ${libB})
cmake -S . -B build -DCMAKE_LIBRARY_PATH="<path/to/libA/folder>;<path/to/libB/folder>"
Though, it would be far more robust if these two external libraries could provide a CMake config file with imported targets.

Related

Including a C++ library into another?

I'm trying to build a c++ library, which will be using itself another library.
I would like to output at the end a single .so file, so it is easily copied and used in any other project.
In this library I am using another library, GLFW.
Now, I can create my library fine, but when I am using it I am getting linking errors, where the GLFW functions are not defined. This makes me think that the GLFW lib is not exported with my library.
I've seen this that seemed to be a solution, but i gave me lot of duplicate symbol errors.
I'm quite a beginner with cmake, so maye there is something obvious I'm not seeing. Here is my CMakeLists.txt :
cmake_minimum_required(VERSION 3.22)
project(MyLib)
set(CMAKE_CXX_STANDARD 23)
# define folders path
get_filename_component(ROOT_DIR ${CMAKE_CURRENT_LIST_FILE} PATH)
set(HEADER "${ROOT_DIR}/include")
set(SRCS_PATHS "${ROOT_DIR}/src")
set(TESTS_SRC "${ROOT_DIR}/tests")
# add dependencies
set(DEP_HEADERS "${ROOT_DIR}/dependencies/GLFW/include")
# set the project sources and headers files
include_directories(${HEADER})
include_directories(${DEP_HEADERS})
set(SRCS [...])
add_library(MyLib SHARED ${SRCS})
# set the project property linker language
set_target_properties(MyLib PROPERTIES LINKER_LANGUAGE CXX)
# target tests
add_executable(window ${TESTS_SRC}/window.cpp)
target_link_libraries(window MyLib)
I've seen I'm not the only one with this issue, but most of the answers I've tried won't work and lead to the same problem.
From what I can deduce from your CMakeLists.txt, you should do something like this (I don't like vendoring, not an expert of this approach, so maybe there is something more elegant):
cmake_minimum_required(VERSION 3.20)
project(MyLib)
# glfw static PIC
set(CMAKE_POSITION_INDEPENDENT_CODE_SAVED ${CMAKE_POSITION_INDEPENDENT_CODE})
set(BUILD_SHARED_LIBS_SAVED ${BUILD_SHARED_LIBS})
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(BUILD_SHARED_LIBS OFF)
add_subdirectory(dependencies/GLFW EXCLUDE_FROM_ALL)
set(CMAKE_POSITION_INDEPENDENT_CODE ${CMAKE_POSITION_INDEPENDENT_CODE_SAVED})
set(BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS_SAVED})
# MyLib
add_library(MyLib SHARED [...])
target_include_directories(MyLib PUBLIC $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>)
target_link_libraries(MyLib PRIVATE glfw)
target_compile_features(MyLib PUBLIC cxx_std_23)
# Tests
add_executable(window tests/window.cpp)
target_link_libraries(window PRIVATE MyLib)
target_compile_features(MyLib PRIVATE cxx_std_23)
But honestly it's bad to hardcode all these informations in a CMakeLists, you should have a generic CMakeLists and avoid vendoring:
cmake_minimum_required(VERSION 3.20)
project(MyLib)
# MyLib
find_package(glfw3 REQUIRED)
add_library(MyLib [...])
target_include_directories(MyLib PUBLIC $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>)
target_link_libraries(MyLib PRIVATE glfw)
target_compile_features(MyLib PUBLIC cxx_std_23)
# Tests
add_executable(window tests/window.cpp)
target_link_libraries(window PRIVATE MyLib)
target_compile_features(MyLib PRIVATE cxx_std_23)
And then you would decide at build time how to build each lib:
// build & install glfw once, as static PIC (glfw is not vendored in MyLib source code here)
cd <glfw_source_dir>
cmake -B build -S . -DCMAKE_BUILD_TYPE=Release -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX=<glfw_install_dir>
cmake --build build
cmake --build build --target install
// build MyLib as shared
cd <mylib_source_dir>
cmake -B build -S . -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON -DCMAKE_PREFIX_PATH=<glfw_install_dir>
cmake --build build

Require shared library with dependencies in cmake

I'm new to cmake, so I'm having trouble figuring out how to model the following:
My program requires a third party library that I only have access to in the form of headers and *.so files. It also ships many of its dependencies as *.so files. How do I make sure that everything compiles and links correctly, ideally in the "correct" cmake way?
I've tried this
cmake_minimum_required (VERSION 3.8)
project ("Test")
add_executable (MyApp "MyApp.cpp")
link_directories("/path/to/lib")
target_include_directories(MyApp PUBLIC "/path/to/headers")
This compiles but fails at the linking stage.
Then I tried
cmake_minimum_required (VERSION 3.8)
project ("Test")
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
add_executable (MyApp "MyApp.cpp")
find_package(Library REQUIRED)
target_link_libraries(MyApp PUBLIC Library::Library)
And cmake/FindLibrary.cmake following https://pabloariasal.github.io/2018/02/19/its-time-to-do-cmake-right/ (truncated):
...
if(Library_FOUND AND NOT TARGET Library::Library)
add_library(Library::Library SHARED IMPORTED)
set_target_properties(Library::Library PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${Library_INCLUDE_DIR}"
)
set_target_properties(Library::Library PROPERTIES
IMPORTED_LOCATION "/path/to/library.so"
)
endif()
This compiles and links, but the required other *.so files are not found at runtime. I suppose I need to add them also as libraries somehow, although I don't want them to be exported in FindLibrary.cmake. How do I do this correctly?
You can use IMPORTED libraries:
add_library(externalLib SHARED IMPORTED GLOBAL)
set_property(TARGET externalLib PROPERTY IMPORTED_LOCATION "/path/to/lib.so")
target_include_directories(externalLib INTERFACE "/path/to/headers")
target_link_directories(externalLib INTERFACE /path/to/libs)
target_link_libraries(externalLib INTERFACE -ldep1 -ldep2)
Then you can just depend on it:
target_link_libraries(MyApp PRIVATE externalLib)

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
)

Remove boost dependency when linking to a library that uses boost with cmake

I am trying to create a library which uses boost filesystem within but I don't want to have to add that dependency to the executable that uses that library. I am using CMAKE as it has to be cross platform.
In the cmake file I have added a preprocessor definition 'BOOST_ALL_NO_LIB' to not auto link and set cmake to use static libraries. This all compiles and creates the library. The cmake code is shown below.
set(Boost_USE_STATIC_LIBS ON)
add_definitions(-DBOOST_ALL_NO_LIB)
find_package(Boost REQUIRED COMPONENTS system filesystem)
include_directories(${Boost_INCLUDE_DIRS})
link_directories(${Boost_LIBRARY_DIR})
target_link_libraries(Utility
${Boost_LIBRARIES})
But when I link in the library I get errors like the one below.
Error 1 error LNK2001: unresolved external symbol "class boost::system::error_category const & __cdecl boost::system::system_category(void)" ...
If I add the boost filesystem and boost system libraries to the the additional dependencies in the Librarian for my Library, the errors in the executable go away. Is there a way I can do this with CMAKE?
It looks like it is possible with visual studio but not generally:
Linking static libraries to other static libraries
There is an option in the Boost find module called Boost_USE_STATIC_LIBS. Set it to "ON". My canned Boost CMake looks like:
set(Boost_USE_STATIC_LIBS ON)
set(Boost_USE_MULTITHREADED ON)
find_package(Boost ...)