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?
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.
There are a lot of libraries' packages which do not provide a CMake config file, and in order to find and use them with cmake, one would have to resort to using a FindPackage.cmake script. Some scripts (i.e. SDL) are available within the cmake itself, so finding a package is relatively easy.
Though in my case, SDL-searching scripts (SDL, SDL_image, SDL_mixer) are available almost since the dawn of modern cmake (at least 3.1), they do not provide the means for the modern approach - they do not define imported cmake Targets. SDL as a target is available only since 3.19, and it does not define IMPORTED_LOCATION property.
So, the logical thing is to define those targets and properties.
A naive approach would possibly be to just copy the contents of FindSDL.cmake from a newer cmake bundle and paste it with modifications.
But I would like to keep those files from a cmake bundle (or another good enough script from external source) intact and just wrap them.
So, the main CMakeLists.txt would be like:
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/modules")
find_package(SDL REQUIRED)
find_package(SDL_mixer REQUIRED)
find_package(SDL_image REQUIRED)
cmake/modules/FindSDL.cmake:
find_package(SDL REQUIRED)
if (NOT TARGET SDL::SDL)
# add target and properties here
endif()
However, the way I wrote it will not work because of the endless recursion.
How could I resolve the endless recursion with find_package when writing a wrapper FindPackage.cmake that uses the original FindPackage.cmake file?
This looks promising:
How to execute CMake's default find module from my own find module with the same name?
The first thing that comes to mind is to delete the current path from CMAKE_MODULE_PATH before calling find_package(), and then restore it.
list(REMOVE_ITEM CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR})
find_package(SDL)
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR})
if (NOT TARGET SDL::SDL)
# add target and properties here
endif()
My troubles with CMake keep compounding D:
What I have in mind is to create my project so that when I build it, it runs unit tests and if unit tests fail, it cleans up after itself. Otherwise, if they succeed, the build spits the runnable executable into the run/ directory.
My project directory contains subdirectories sources, headers, build, run, and tst/ut and thus far I have cmakelists at project root as well as inside sources and headers directories
CMakeLists.txt at the project root
project(TaskFour LANGUAGES CXX)
enable_testing()
set(BIN ${CMAKE_PROJECT_NAME})
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED)
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core REQUIRED)
add_library(${BIN}_lib)
add_executable(${BIN}_run)
include_directories(sources headers tst/ut)
cmakelists at sources/
set(SRC
main.cpp)
target_sources(${BIN} PUBLIC ${SRC})
and headers
#contents of this list are empty by design for now
set(SRC
"")
target_sources(${BIN} PUBLIC ${SRC})
With these CMake list files, QtCreator fails to see anything beyond the top-level CMake list file. It doesn't acknowledge the existence of the subdirectories in the project directory, the other two cmakeLists.txt files nor the main.cpp that very much exists inside the sources directory.
I have attempted to use the add_subdirectory command and that seemed to help as that made all the subdirectories show up. However, I quickly realized that QtCreator seemingly treated every subdirectory like its own project, and trying to add files into these newfangled "projects" resulted in a simple "failed to add one or more files to project foo" message.
I have understood that the point of CMake list files is to have several per project, each governing their own subsection of the larger project, and the top CMake list file gathering it all together. Before I did my CMake file as one big file at the root of the project, and I've understood that to not be good. I also understand that dividing headers and sources like I do is not exactly the most common practice but I hope you forgive me and help me get this thing working.
So, my actual question is, how do I do this? What am I currently doing wrong in my top-level cmakelists file to cause qtCreator to not find the files in the project? How do I write the CMake lists so that it would allow me to use the QTest framework, maybe even build the skeleton from QtCreator's project wizard (as currently the option to add the autotest project as a subproject is greyed out)?
Any insight is appreciated, and feel free to go into detail about CMake if you feel like it. This is an important piece of software development that I feel I've been missing out on.
There's a couple problems here with how you've organized your CMake file. This should at least get you started on the right path
You're calling add_library(${BIN}_lib) and add_executable(${BIN}_run) but then in your child CMake file you're calling target_sources(${BIN} ...) - target names are evaluated as strings - there is no target named BIN (or the value you set to BIN in this case), and adding sources to a target named BIN would not propagate those sources to targets named BIN_lib and BIN_run
include_directories is for adding entries to the C++ compiler's include search path, not for adding additional directories containing sources/other CMake files. For that, as you seem to have already figured out, you need add_subdirectories(...)
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).
I have learned the basics of C++ programming and thought of ways how I could proceed, in order to practise. One thing that caught my interest was Web Scraping. Since I only knew BeautifulSoup, I searched for an alternative for C++ and found libXML for C++, however I'm trying to install it but don't seem to get it to work, since I barely have an idea on how to configure a CMake file. I'm using CLion as an IDE and Windows as my operating system, if it matters. My project folder is
C:\Users\Laurenz\Documents\Programming\untitled10
and the place where I've put the libXML library is
C:\Users\Laurenz\Documents\Programming\untitled10\libXML
however, I think the "main" directory is the one below (since it contains most of the Source files), but I'm not sure which of both I need to include.
C:\Users\Laurenz\Documents\Programming\untitled10\libXML\libxml++
I've searched around in the internet and found dozens of methods on how people include libraries, so I'm not sure which I need to use. What I currently have:
cmake_minimum_required(VERSION 3.6)
project(untitled10)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set(SOURCE_FILES main.cpp)
add_executable(untitled10 ${SOURCE_FILES})
set(CMAKE_PREFIX_PATH "C:\\Users\\Laurenz\\Documents\\Programming\\untitled10\\libXML\\libxml++")
find_package(LibXML++ REQUIRED)
include_directories(${LibXML++_INCLUDE_DIRS})
set(LIBS ${LIBS} ${LibXML++_LIBRARIES})
target_link_libraries(untitled10 ${LIBS})
However, I get the following error message:
Error:By not providing "FindLibXML++.cmake" in CMAKE_MODULE_PATH this project has asked CMake to find a package configuration file provided by "LibXML++", but CMake did not find one.
Could not find a package configuration file provided by "LibXML++" with any of the following names:
LibXML++Config.cmake libxml++-config.cmake
Add the installation prefix of "LibXML++" to CMAKE_PREFIX_PATH or set "LibXML++_DIR" to a directory containing one of the above files. If "LibXML++" provides a separate development package or SDK, be sure it has been installed.
Could someone tell me how to properly configure LibXML? And maybe someone knows some resources where I can learn how CMake files work, because it confuses me a bit.
Thanks!