I am using CMake FetchContent to download and build a third party library(realsense2 in this case). After trying out the googletest example from the official documentation (https://cmake.org/cmake/help/v3.11/module/FetchContent.html) I was impressed how easy it works. Including headers was done magically. Now with realsense2 SDK I have a problem.
I need to do add an additional include_directories command like this:
FetchContent_Declare(
realsense2
GIT_REPOSITORY https://github.com/IntelRealSense/librealsense.git
GIT_TAG v2.23.0
)
FetchContent_MakeAvailable(realsense2)
FetchContent_GetProperties(realsense2)
if(NOT realsense2_POPULATED)
FetchContent_Populate(realsense2)
add_subdirectory(${realsense2_SOURCE_DIR} ${realsense2_BINARY_DIR})
endif()
//I should not be required to do this according to documentation
include_directories(${realsense2_SOURCE_DIR}/include)
If I do not do this, some headers are not found. Any suggestions regarding this problem?
EDIT: To clarify, this is how I added the libraries:
target_link_libraries(TestExe gtest gtest_main)
and the other exactly the same but this time it is not an exe its a dll
add_library(TestLib SHARED ${TestLib_HEADERS} ${TestLib_SOURCES} )
target_link_libraries(TestLib realsense2)
At this point I am more concerned about why I do not have to add any includes for googletest framework
The main purpose of FetchContent is a garantee that at the time of call
add_subdirectory(${Foo_SOURCE_DIR} ${Foo_BINARY_DIR})
the "fetched" project will be (as sources) in the ${Foo_SOURCE_DIR} directory.
How to use the project inluded via add_subdirectory is completely up to that project:
Some projects (including gtest) create the library target Foo in a "modern" CMake way, by associating properties with it using target_include_directories and other commands. So, to use a library like this, it is sufficient to call target_link_libraries.
Some other projects require both include_directories and target_link_libraries to work with them.
Finally, there are many projects, which simply don't work when included via add_subdirectory. So FetchContent makes little sense for them.
Only a small subset of projects describe how to work with them via add_subdirectory approach. And gtest is among them.
But most projects simply don't describe this; if you want to use add_subdirectory with such a project, then you need to investigate the internals of that project to understand its usage (or use trial and error).
Related
I'd like to import external boost in a c++ cmake project.
I did several tries and finally got the following CMakeLists.txt works:
cmake_minimum_required(VERSION 3.14)
project(my_project
VERSION 0.0.1
LANGUAGES C CXX)
set(CMAKE_CXX_STANDARD 11)
include(FetchContent)
FetchContent_Declare(
boost
URL https://boostorg.jfrog.io/artifactory/main/release/1.78.0/source/boost_1_78_0.tar.bz2
)
FetchContent_MakeAvailable(boost)
set(Boost_ROOT "${CMAKE_BINARY_DIR}/_deps/boost-src")
set(BOOST_INCLUDEDIR ${Boost_ROOT})
find_package(Boost 1.78)
include_directories(${Boost_INCLUDE_DIRS})
add_executable(
boost_test
tests/boost_test.cpp)
However, I feel a little uncomfortable because set(Boost_ROOT "${CMAKE_BINARY_DIR}/_deps/boost-src") looks hacky -- it has a strong assumption that the fetched content will be stored in _deps folder, which I rather want to put it in a dedicated folder like 3rd_party or external as typical C++ project layout conventions.
I found there're 2 ways for doing that:
Use FetchContent_Declare to download the project. However, I don't know how to execute extra build commands to compile non-header-only libraries.
Use ExternalProject_Add to download and compile the boost project. It looks like an old-fashing way, besides, I don't know how to add boost as part of dependencies to top-level CMakeLists.txt in this way.
The way I adopted is the former one, but I wonder if it's the correct way to include external boost (not system-default installed one) as part of a C++ project, or is there a best practice for doing this?
Any comments, suggestions, answers are welcome. Thanks.
The best way to use Boost in a CMake project is like this:
cmake_minimum_required(VERSION 3.25)
project(boost-example)
find_package(Boost REQUIRED)
add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE Boost::headers)
target_compile_features(myapp PRIVATE cxx_std_11)
Boost::headers is the library to link to for all of the Boost header-only components. If you need a component that includes a real library, then link to Boost::<component>, too, and be sure to name <component> after the REQUIRED argument to find_package.
Don't mess with FetchContent. Your users will know how to get the necessary dependencies. You can provide a vcpkg.json file or a conanfile.txt to make it easier to use Vcpkg and/or Conan, respectively.
I'm having problems with the "proper" way of installation third-party libraries for use with CMake projects (C++). For most of the time I haven't been bothering myself with this stuff and just hardcoding library paths in my CMakeLists.txt, but I really want to start using this properly.
I ultimately want to have something where I can just type, for example, find_package(SDL2 REQUIRED) in a project and the library is there, found, and works perfectly.
What I have right now is I have a folder C:\CMakeLibs where I --install all 3rd party libraries, so for example there is C:\CMakeLibs\SDL2 and in it, there are folders like cmake, include, lib, etc. Then I include the folder C:\CMakeLibs\SDL2 in CMAKE_PREFIX_PATH in system environment variables, so that CMake can find the SDL2Config.cmake file and there's no need for any FindSDL2.cmake. This way I can just type following code and it straight works, and is also probably the most portable for open-source projects.
find_package(SDL2 REQUIRED)
add_executable(app main.cpp)
target_link_libraries(app PRIVATE SDL2::SDL2main SDL2::SDL2-static)
My only concern here is that I have to manually add any new libraries to CMAKE_PREFIX_PATH. I'd like just have only C:\CMakeLibs as my CMAKE_PREFIX_PATH and everything working fine. I could technically --install the libraries directly to C:\CMakeLibs but I fear of any possible incompatibilities when having all the different files in one shared folders, it doesn't feel like the best solutions.
I know this question might be one of those "without the best answers/based on opinion". I've been looking for solutions on internet for long time already, ever since I started using CMake, so I decided to just write this post here. I hope I described my problem clearly, English isn't my native language.
I am trying to write a web service in C++ (I am very new to the language) using restbed for HTTP handling. Both projects seem to be using cmake to do build configuration and dependency management, so I figured I would do so as well. But I am confused about how to go about it.
Here is my minimal CMakeLists.txt
cmake_minimum_required(VERSION 3.7)
project(WebService)
set (WebService_VERSION_MAJOR 1)
set (WebService_VERSION_MAJOR 0)
set(CMAKE_CXX_STANDARD 11)
add_executable(${PROJECT_NAME} main.cpp)
I understand that there are a few ways to add dependencies in cmake. I have tried doing things like find_package(restbed), but obviously it doesn't know where to find the package I want and keeps asking for extra cmake files to help it. I am not sure where I am supposed to find these. Am I supposed to write one myself?
I have tried using the ExternalProject_Add directive. I added the following.
ExternalProject_Add(
restbed
GIT_REPOSITORY "git#github.com:Corvusoft/restbed.git"
SOURCE_DIR "${CMAKE_SOURCE_DIR}/deps/restbed"
CMAKE_ARGS -DBUILD_SSL=OFF
)
set (restbed_INCLUDE ${CMAKE_SOURCE_DIR}/deps/restbed/distribution/include/)
set (restbed_LIB ${CMAKE_SOURCE_DIR}/deps/restbed/distribution/library/librestbed.a)
add_dependencies(${PROJECT_NAME} restbed)
include_directories(${restbed_INCLUDE})
target_link_libraries(${PROJECT_NAME} ${restbed_LIB})
The problem I am having is that apparently all the steps for setting up an external project are performed during the build and not during cmake configuration. This means that every external project rebuilds and reinstalls everything every time I run make. My service is small and this seems very wasteful.
Is there a better way to deal with dependencies? Is there an easier tool than cmake? Is there something for C++ that is similar to Rust's cargo or Haskell's stack maybe?
I have a C++ project that I initially developed using Xcode on my Mac and I would like to set up the very same project using CMake. The project makes use of some external libraries, including eigen and boost. Xcode has a rather long list of Build Settings, including paths to external libraries, specification of the compiler version, additional linker flags, compiler optimization level, etc... and I am wondering where all of this information goes in the CMakeLists.txt file. I've searched extensively for help on this but have found precious little. I am new to CMake and have never written make files before. If there were a utility that could convert my Xcode project into a CMake project, that would be ideal. But I would be very glad to know of a tutorial on this, or to have some specific guidance. Any help on this would be greatly appreciated.
Conversion Strategy
I am pretty sure that the easiest and fastest way to move to CMake is a mixture of looking at comparable projects that link the same dependencies, and maybe copying a few relative file paths to your source files from XCode.
A well-written CMakeLists.txt that uses Eigen and Boost can be very small (and platform-independent).
One reason for this is CMake has a very different way to use dependencies.
For well-known libraries such as Boost, CMake includes scripts to include headers and link libraries.
Minimal CMakeLists.txt
A minimal example may look like this:
find_package(Eigen3 REQUIRED)
find_package(Boost REQUIRED COMPONENTS system)
add_executable(
app
src/main.cpp
)
target_include_directories(
app
${Eigen3_INCLUDE_DIRS}
${Boost_INCLUDE_DIR}
)
target_link_libraries(
app
${Boost_LIBRARIES}
)
Example Projects
For Eigen, there is no such pre-installed script that can be called by find_package.
Here are two real-world example projects that link Eigen and Boost: https://github.com/ompl/ompl/blob/master/CMakeLists.txt
https://github.com/roboticslibrary/rl
Here is an example for a find script for Eigen.
As a piece of advice, some CMake projects are out there are convoluted and use old syntax, newer project files tend to use small letters in commands.
If it happens that directly target linking libraries doesn't work, you'll have to write your own Find*.cmake files by yourself.
Here's a spec of what you should cover to make it (this is just one approach):
Imagine you have a library XYZ:
Your file
FindXYZ.cmake should have as a result a set of variables to use:
XYZ_LIBRARIES
XYZ_INCLUDE_DIR
For example:
set(XYZ_LIBRARIES ${SOME_PATH_TO_LIBRARY1} ${SOME_PATH_TO_LIBRARY2})
find_path(XYZ_INCLUDE_DIR NAMES xyz/xyz.h HINTS ${PLACE_WHERE_INCLUDE_SHOULD_BE}
PATH_SUFFIXES include
)
From your main cmake maybe you 'use' it:
find_package(XYZ REQUIRED)
# Now we use both _INCLUDE_DIR and _LIBRARIES
include_directories(SYSTEM "${XYZ_INCLUDE_DIR}")
# TARGET_NAME is the current target, the 'client' of XYZ
# append XYZ to our list of libraries to link
target_link_libraries(${TARGET_NAME} ${XYZ_LIBRARIES})
I'm surely missing details but that's the general idea. Here are some cmake scripts that I know work in a big project:
Example with OpenSSL
Find:
https://github.com/highfidelity/hifi/blob/Android/cmake/modules/FindOpenSSL.cmake
Client (in this case is a library that indeed needs OpenSSL):
https://github.com/highfidelity/hifi/blob/Android/domain-server/CMakeLists.txt
I want to use cmake to generate my build files for a C++ project. I have an existing Makefile.
I am having problems generating this Makefile using the standard cmake syntax.
How do I include standard C++ libraries like -lstdc++ -lpthread -lboost_thread-mt in the TARGET_LINK_LIBRARIES section of cmake? Or should these files be included in the ADD_DEPENDENCIES section.
(OR)
Is there a simple tool which generates a CMakeList.txt file from a Makefile
Unfortunately, there is no straightforward 1:1 conversion from Makefiles to CMakeLists. Since CMake is supposed to run on all platforms, it can not rely on platform specific assumptions like GNU make does, which complicates things in certain places.
In particular, CMake offers a very powerful and rather complex mechanism for using libraries: You call find_package with the name of your library, which will invoke a library search script from your cmake module path. This script (which is also written in CMake) will attempt to detect the location of the library's header and lib files and store them in a couple of CMake variables that can then be passed to the according CMake commands like include_directories and target_link_libraries.
There are two problems with this approach: First, you need a search script. Fortunately, CMake ships with search scripts for Pthreads, Boost and a couple of others, but if you are using a more exotic library, you might have to write the search script yourself, which is kind of an arcane experience at first...
The second major problem is that there is no standard way for a search script to return its results. While there are naming conventions for the used variables, those often don't apply. In practice that means you will have to check out a search script's source to know how to use it. Fortunately, the scripts that come with CMake are mostly very well documented.
The builtin scripts are located somewhere like <cmake-install-prefix>/share/cmake-2.8/Modules. For your question, look at the FindBoost.cmake and FindThreads.cmake files (CMake should automatically link with the standard library). Anycorn already gave some sample code for using the Boost script, everything else you need to know is in the CMake documentation or directly in the search script files.
Like this:
target_link_libraries(your-target-name pthread boost_thread-mt etc)
You should not use add_dependencies when you want to link libraries. Linking implies a dependency, but the dependency alone will not be sufficient when you need to link.
With Boost you really need to use package finder
set(Boost_ADDITIONAL_VERSIONS "1.46" "1.46.0" "1.46.1")
set(Boost_USE_MULTITHREADED ON) # for -mt
find_package(Boost COMPONENTS thread)
if(Boost_FOUND)
MESSAGE(STATUS "Found Boost: ${Boost_LIBRARY_DIRS}")
MESSAGE(STATUS "Found Boost libraries: ${Boost_LIBRARIES}")
set(LIBRARIES "${LIBRARIES};${Boost_LIBRARIES}")
else()
MESSAGE(FATAL_ERROR "Boost Thread NOT FOUND")
endif()
target_link_libraries(executable ${LIBRARIES})