Creating and using a library with CMake - c++

I'm trying to learn CMake, but I can't get to grips with the tutorials that are available.
Let's say my project has the structure below, and I want to make my_lib available through its CMakeLists file and use it in my main.cpp, what would my CMakeLists files look like?
├── CMakeLists.txt
├── externals
│ └── my_lib
│ └── src
│ ├── CMakeLists.txt
│ ├── MyClass.h
│ └── MyClass.cpp
└── main.cpp
Should I use include_directories or add_subdirectory?

To match the directory structure you indicated, your CMakeLists.txt files could look like this:
/CMakeLists.txt:
cmake_minimum_required(VERSION 3.0)
project(Main)
add_subdirectory(externals/my_lib/src)
add_executable(my_main main.cpp)
target_link_libraries(my_main my_lib)
/externals/my_lib/src/CMakeLists.txt:
cmake_minimum_required(VERSION 3.0)
project(MyLib)
add_library(my_lib MyClass.cpp MyClass.h)
target_include_directories(my_lib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
Using PUBLIC in the target_include_directories call means that not only does your library have this as an "include path", but so does any target linking to it.
The downside with this setup however is that you're also going to expose any internal headers of my_lib to consuming targets too. Say you also have an "Internal.h" as part of my_lib which you don't want to expose. You can do this by moving the intentionally-public headers into a separate folder, e.g. "include":
├── CMakeLists.txt
├── externals
│ └── my_lib
│ ├── CMakeLists.txt
│ ├── include
│ │ └── MyClass.h
│ └── src
│ ├── Internal.h
│ └── MyClass.cpp
└── main.cpp
Your top-level CMakeLists.txt wouldn't change, but /externals/my_lib/CMakeLists.txt (which has moved up a level) now reads:
cmake_minimum_required(VERSION 3.0)
project(MyLib)
add_library(my_lib src/MyClass.cpp src/Internal.h include/MyClass.h)
target_include_directories(my_lib
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src
)
Now, since your library's "src" folder is PRIVATE to my_lib, main.cpp won't be able to #include "Internal.h".

Related

How can I tidy my relative references using CMake?

I'm quite new to C++ and trying to get to grips with CMake and referencing files elsewhere in the project. My references within my test file currently looks like this:
#include "catch.hpp"
#include "../../src/containers/containers.h"
How can I tidy this up using CMake to have the following?
#include "catch.hpp"
#include "src/containers/containers.h"
My folder structure is as follows:
Project
├── lib
│ └── Catch2
│ └── catch.hpp
├── src
│ ├── containers
│ │ ├── containers.cpp
│ │ └── containers.h
│ └── CMakeLists.cpp
├── tests
│ ├── bin
│ ├── containers
│ │ └── test_containers.html
│ ├── CMakeLists.cpp
│ └── tests_main.cpp
├── CMakeLists.cpp
└── main.cpp
And my current CMakeLists.txt in ./tests/ is as follows:
set(CATCH_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/lib/Catch2")
add_library(Catch INTERFACE)
target_include_directories(Catch INTERFACE ${CATCH_INCLUDE_DIR})
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/tests/bin)
add_executable(test_containers tests_main.cpp containers/test_containers.cpp)
target_link_libraries(test_containers Catch containers)
add_test(NAME test_containers COMMAND test_containers)
enable_testing()
I include the following directories in my base CMakeLists.txt:
add_subdirectory(src)
add_subdirectory(tests)
And in src, I make containers a library:
add_library(containers STATIC
containers/containers.cpp
containers/containers.h)
Adding the line target_include_directories(containers PUBLIC "${CMAKE_SOURCE_DIR}/src") solved the issue and allowed my references to be reduced as desired.
following my comments, supposing you want to use #include "containers.h"
target_include_directories(containers
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/containers>
$<INSTALL_INTERFACE:include/containers>)
set_target_properties(containers PROPERTIES
PUBLIC_HEADER containers/containers.h)
# Install
include(GNUInstallDirs)
install(TARGETS containers
EXPORT ${PROJECT_NAME}Targets
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/containers
# ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
# LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
# RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
ref:
https://cmake.org/cmake/help/latest/command/target_include_directories.html
https://cmake.org/cmake/help/latest/variable/CMAKE_CURRENT_SOURCE_DIR.html
https://cmake.org/cmake/help/latest/module/GNUInstallDirs.html
https://cmake.org/cmake/help/latest/command/install.html#targets

how to create a c++ dynamic library using project class?

I have a c++ project with many classes and sub-directory. I want to convert my communication code subdirectory to share library(dynamic library). But I have little experience with C++ and cmake. I don't know how to do. I share my test code tree and cmakelist.txt
.
├── CMakeLists.txt
├── main.cpp
└── sim_haberlesme
├── hab_ortak
│   ├── haberlesmeAraKatman.cpp
│   └── haberlesmeAraKatman.h
├── paketleyici2.cpp
├── paketleyici2.h
├── paketleyici.cpp
├── paketleyici.h
└── protobuf
├── protobuf_deserialize.cpp
├── protobuf_deserialize.h
├── protobuh_serialize.cpp
└── protobuh_serialize.h
cmake_minimum_required(VERSION 3.5)
project(CmakeTest)
set(CMAKE_CXX_STANDARD 14)
add_executable(CmakeTest main.cpp sim_haberlesme/paketleyici2.cpp sim_haberlesme/paketleyici2.h sim_haberlesme/paketleyici.cpp sim_haberlesme/paketleyici.h sim_haberlesme/protobuf/protobuh_serialize.cpp sim_haberlesme/protobuf/protobuh_serialize.h sim_haberlesme/protobuf/protobuf_deserialize.cpp sim_haberlesme/protobuf/protobuf_deserialize.h sim_haberlesme/hab_ortak/haberlesmeAraKatman.cpp sim_haberlesme/hab_ortak/haberlesmeAraKatman.h)
(If you want me to share the code, I'll share)
I want to create dynamic lib with using sim_haberlesme directory. But this directory have too many classes and subdirectory. In this reason i don't know how to start and how to edit cmakelist file.
Can you tell me how to start convert .cpp file to .so file and how to edit cmakelist.txt file?
By the way you can share tutorials
Something like that in your CMakeLists:
cmake_minimum_required(VERSION 3.5)
project(CmakeTest)
set(CMAKE_CXX_STANDARD 14)
add_library(sim_haberlesme SHARED)
target_sources(sim_haberlesme PRIVATE
hab_ortak/haberlesmeAraKatman.cpp
hab_ortak/haberlesmeAraKatman.h
paketleyici2.cpp
paketleyici2.h
paketleyici.cpp
paketleyici.h
protobuf/protobuf_deserialize.cpp
protobuf/protobuf_deserialize.h
protobuf/protobuh_serialize.cpp
protobuf/protobuh_serialize.h
)
add_executable(test)
target_sources(test PRIVATE main.cpp)
target_link_libraries(test PRIVATE sim_haberlesme)

Cant find a OpenCV header file (CMake, OpenCV4, C++)

When I am creating custom libraries using OpenCV4 in the libs directory I am getting the following error:
/Documents/opencv-palisade/libs/image.cpp:2:10: fatal error: opencv2/opencv.hpp: No such file or directory
2 | #include <opencv2/opencv.hpp>
| ^~~~~~~~~~~~~~~~~~~~
Directory Structure:
.
├── build
│ ├── CMakeCache.txt
│ ├── CMakeFiles
│ ├── cmake_install.cmake
│ ├── image.jpg
│ ├── include
│ ├── libs
│ ├── Makefile
│ ├── opencv-palisade
│ ├── test.jpg
│ └── transformed_image.jpg
├── CMakeLists.txt
├── libs
│ ├── CMakeLists.txt
│ ├── image.cpp
│ └── image.hpp
└── main.cpp
5 directories, 12 files
libs/CMakeLists.txt:
project(libs)
include_directories(${OpenCV_INCLUDE_DIRS})
add_library(
image_compute
SHARED
image.hpp
image.cpp
)
root CMakeLists.txt:
cmake_minimum_required(VERSION 3.16.3)
project(opencv-palisade VERSION 1.0)
#Include header files
add_subdirectory(libs)
include_directories(libs)
# Import OpenCV library
find_package(OpenCV 4 REQUIRED)
if(OpenCV_FOUND)
message(STATUS "Found OpenCV version ${OpenCV_VERSION}")
message(STATUS "OpenCV directories: ${OpenCV_INCLUDE_DIRS}")
message(STATUS "OpenCV libraries: ${OpenCV_LIBS}")
include_directories(${OpenCV_INCLUDE_DIRS})
else()
message(FATAL_ERROR "OpenCV not found, please read the README.md")
endif(OpenCV_FOUND)
#build main.cpp
add_executable(${PROJECT_NAME} main.cpp)
target_link_directories(${PROJECT_NAME} PUBLIC libs)
target_link_libraries(${PROJECT_NAME} ${OpenCV_LIBS} image_compute)
How can I fix this issue to be able to create libraries using opencv api?

Compile and link OpenCV to my project in my main CMakeLists.txt

I am new to cmake. I have a project which uses dlib and opencv. They are defined as submodules which are in third_party folder. I want to link them to my main project which is 'node' with cmake but I could not achieved. I am sharing my project tree. I did with find_package(OpenCV) and target_link_libraries(recognition-node ${OPENCV_LIBS}) way but I need to compile from source without installing anything. At last, I just want to write 'cmake . && make'
.
├── CMakeLists.txt
├── node
│ ├── build.sh
│ ├── CMakeLists.txt
│ ├── configure.sh
│ ├── findfacestask.cpp
│ ├── findfacestask.h
│ ├── main.cpp
│ ├── matrixwrapper.h
│ ├── poolcontext.cpp
│ ├── poolcontext.h
│ ├── recognition.dat
│ ├── recognizefacetask.cpp
│ ├── recognizefacetask.h
│ ├── runscript
│ ├── sp.dat
│ ├── task.cpp
│ ├── task.h
│ ├── unhandledexception.cpp
│ ├── unhandledexception.h
│ ├── webcamfeed.cpp
│ ├── webcamfeed.h
│ ├── wrapper.cpp
│ └── wrapper.h
└── third_party
├── dlib
│ ├── appveyor.yml
│ ├── CMakeLists.txt
│ ├── dlib
│ ├── docs
│ ├── examples
│ ├── MANIFEST.in
│ ├── python_examples
│ ├── README.md
│ ├── setup.py
│ └── tools
└── opencv
├── 3rdparty
├── apps
├── cmake
├── CMakeLists.txt
├── CONTRIBUTING.md
├── data
├── doc
├── include
├── LICENSE
├── modules
├── platforms
├── README.md
└── samples
Content of my top CMakeLists.txt
cmake_minimum_required(VERSION 2.8.12)
set (CMAKE_CXX_STANDARD 11)
add_subdirectory(node)
add_subdirectory(third_party/dlib)
add_subdirectory(third_party/opencv)
Content of node/CMakeLists.txt
cmake_minimum_required(VERSION 2.8.12)
project(recognition-node)
set(CMAKE_AUTOMOC ON)
find_package(Qt5Widgets REQUIRED)
add_executable(recognition-node main.cpp
webcamfeed.cpp
poolcontext.cpp
unhandledexception.cpp
task.cpp
findfacestask.cpp
wrapper.cpp
recognizefacetask.cpp)
target_link_libraries(recognition-node Qt5::Widgets)
target_link_libraries(recognition-node dlib::dlib)
target_link_libraries(recognition-node opencv::core)
It gives error in 'make' stage which is :
/home/arnes/workspace/recognition-node/node/poolcontext.h:10:28: fatal error:
opencv2/core.hpp: No such file or directory
Since you insist on keeping the opencv in your project tree
It is easier way but I just want it to do in this way.
Here is the solution that for sure works fine with your project tree that you posted in the question and with opencv-3.4.1. For simplicity I will neglect dlib library and Qt dependency, since you didn't have any problem with it.
Root CMakeLists.txt should have the following content:
cmake_minimum_required(VERSION 2.8.11) # or anything higher, if you wish
project(recognition-node CXX)
add_subdirectory(node)
The CMakeLists.txt under the node directory should have the following content:
add_subdirectory(third_party)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -g") # or any other additional flags
# at this point you can add find_package(Qt5Widgets REQUIRED) and later link your binary against Qt5::widgets as well
add_executable(myExec main.cpp
# and put here all the other source files of your project ...
)
# for linking libs I have put additionally highgui and imgproc to check the solution against OpenCV official sample
target_link_libraries(myExec opencv_core opencv_highgui opencv_imgproc)
target_include_directories(myExec PUBLIC
third_party/opencv/modules/calib3d/include
third_party/opencv/modules/core/include
third_party/opencv/modules/cudaarithm/include
third_party/opencv/modules/cudabgsegm/include
third_party/opencv/modules/cudacodec/include
third_party/opencv/modules/cudafeatures2d/include
third_party/opencv/modules/cudafilters/include
third_party/opencv/modules/cudaimgproc/include
third_party/opencv/modules/cudalegacy/include
third_party/opencv/modules/cudaobjdetect/include
third_party/opencv/modules/cudaoptflow/include
third_party/opencv/modules/cudastereo/include
third_party/opencv/modules/cudawarping/include
third_party/opencv/modules/cudev/include
third_party/opencv/modules/dnn/include
third_party/opencv/modules/features2d/include
third_party/opencv/modules/flann/include
third_party/opencv/modules/highgui/include
third_party/opencv/modules/imgcodecs/include
third_party/opencv/modules/imgproc/include
third_party/opencv/modules/ml/include
third_party/opencv/modules/objdetect/include
third_party/opencv/modules/photo/include
third_party/opencv/modules/shape/include
third_party/opencv/modules/stitching/include
third_party/opencv/modules/superres/include
third_party/opencv/modules/ts/include
third_party/opencv/modules/video/include
third_party/opencv/modules/videoio/include
third_party/opencv/modules/videostab/include
third_party/opencv/modules/viz/include
third_party/opencv/modules/world/include
)
The CMakeLists.txt under third_party should contain only:
add_subdirectory(opencv)
# add_subdirectory(dlib) # if you will use dlib, of course also add dlib
The sample I used to verify the build is contours2.cpp (just copy pasted the content into main.cpp).
However, I still think that it is a terrible idea to use this solution.
OpenCv takes really a lot of time to compile
you have to manually add include dirs (you can use some macro generators, but usually it looks even more ugly)
in your build system you have a lot of targets (over 300) that you don't really need, including install target
So, my recommendation is: if you want, use this solution for scientific purpose, but just compile and install OpenCv system-wise (or locally, if you are not the admin) when you really need to use it.

CMake 'no rule to make target' with external library

I am trying link one of my programs to libevent. I am using CMake as build system. My project structure is as follows:
my_project
├── CMakeLists.txt
├── README.md
├── build
│  └── Build stuff
└── software
├── README.md
├── CMakeLists.txt
├── include
├── libraries
│   ├── libevent
│ │   └── CMakeLists.txt
│   └── anotherlibrary
│      └── CMakeLists.txt
├── prog1
│   ├── CMakeLists.txt
├── prog2
│   ├── CMakeLists.txt
└── prog3
└── CMakeLists.txt
CMakeList.txt of prog1 (the one that's needs to be linked to libevent)
cmake_minimum_required(VERSION 2.6)
project (prog1)
file(GLOB prog1
"*.h"
"*.cpp"
)
include_directories("${PROJECT_INCLUDE_DIR}/libevent/include")
add_executable(${PROJECT_NAME} ${prog1})
target_link_libraries(${PROJECT_NAME} event_core)
But when I build the project make can't find the library build by libevent. it searched for: libraries/libevent/lib/libevent_core.a this is the wrong path since libevent builds it libs inside: my_project/build/software/libraries/libevent/lib/libevent_core.a
How do I tell CMake to search there for the library? I already added the following lines to my Cmake file but this wasn't working
link_directories(/my_project/build/software/libraries/libevent/lib/)
SET(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/build/lib)
SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/build/bin)
Anyone a suggestion?
I fixed the problem myself by removing the content from the build directory and re running cmake .. inside the build directory.
I think CMake was somehow not aware of the changes I made and by rebuilding the project the problem was fixed.