C++ and CMake newbie question regarding how to integrate a third-party library into my own code. I'm trying to add Datadog metrics to C++ application. The officially-endorsed library doesn't state how it can be integrated. I imagine it should tell me how to import it like this:
find_package(<PACKAGE> REQUIRED)
add_executable(foobar src/main.cpp)
target_include_directories(foobar PUBLIC ${<PACKAGE_DIRECTORIES>})
target_link_libraries(foobar ${<PACKAGE_LIBRARIES>})
This is my understanding of how to integrate a third-party library (don't you wish there is "pip" in C++?). But the names in <> are not provided in the README. I certainly don't have to do it like this as long as I can use CMake. Any help is appreciated!
Easy solution is to fetch this library directly and do add_subdirectory. But this requires cmake >= 3.11.
Create dir cmake and file cmake/cpp-datadogstatsd.cmake
cpp-datadogstatsd.cmake:
FetchContent_Declare(
datadogstatsd
GIT_REPOSITORY https://github.com/BoardiesITSolutions/cpp-datadogstatsd
# try v1.1.0.5 if this does not work
GIT_TAG 1.1.0.5
)
FetchContent_GetProperties(datadogstatsd)
if(NOT datadogstatsd_POPULATED)
message(STATUS "Downloading datadogstatsd...")
FetchContent_Populate(datadogstatsd)
add_subdirectory(${datadogstatsd_SOURCE_DIR} ${datadogstatsd_BINARY_DIR} EXCLUDE_FROM_ALL)
endif()
Then, include this cmake file, and link DataDogStatsD_static to your lib/exe:
include(cmake/cpp-datadogstatsd.cmake)
add_executable(test main.cpp)
target_link_libraries(test DataDogStatsD_static)
Related
How to create a cmake header-only library that depends on external header files? is close but different.
I have a single-header library, MyHeaderLib. In MyHeaderLib/MyHeader.h I have #include <QString>, so anyone doing #include "MyHeaderLib/MyHeader.h" had better have QString in their path (i.e., Qt5Core to CMake, I think(?)) and it they'll need to link to Qt5Core.
What belongs in my CMakeLists.txt for MyHeaderLib? I have
cmake_minimum_required(VERSION 3.12)
add_library(MyHeaderLib INTERFACE)
target_include_directories(MyHeaderLib include/)
# (^ Where include/ contains MyHeaderLib/MyHeader.h)
Anything I try with target_link_libraries(MyHeaderLib requires INTERFACE and if I do target_link_libraries(MyHeaderLib INTERFACE Qt5Core) that doesn't suffice.
Ultimately I got it to work as follows, but I don't understand what is going on:
cmake_minimum_required(VERSION 3.12)
find_package(Qt5Core REQUIRED) # <- Can't be Qt5::Core
add_library(MyHeaderLib INTERFACE)
target_include_directories(MyHeaderLib include/)
# (^ Where include/ contains MyHeaderLib/MyHeader.h)
target_link_libraries(MyHeaderLibrary
INTERFACE
Qt5::Core # <- Can't be Qt5Core
)
I gather the targets with :: in them are aliases, but I'm perplexed why it needs to be exactly like this. Furthermore, I can't find add_library(Qt5::Core ALIAS Qt5Core) anywhere. What is going on? Why do I have to find_package(Qt5Core REQUIRED) and not find_package(Qt5::Core REQUIRED) and why can't target_link_libraries take Qt5Core?
Packages are responsible for defining targets. The Qt maintainers chose to name the package Qt5Core while deciding to define the Qt5::Core target.
Usually the convention with CMake packages is that a package named package-name will define package-name::package-name with maybe other optional targets or subcomponents of package-name::package-name.
As to answer why Qt don't act like this, look inside Qt5CoreConfig.cmake, you'll see this line:
add_library(Qt5::Core SHARED IMPORTED)
Here you go. The file is named Qt5CoreConfig so it needs find_package(Qt5Core), but the target is under the Qt5 namespace as they choose to define it.
This is maybe because Qt5 Also has a general package which you can use components:
find_package(Qt5 REQUIRED COMPONENTS Core)
# Here Qt5::Core kinda make sense.
I'm still very new to CMake so feedback is definitely welcome. So, I'm trying to build a simple application that should eventually create a pdf using the library libharu.
I think i figured it out how to link the library. But I still receive build errors for the findpng module (I suppose libharu depends on it)
CMakeLists.txt:
cmake_minimum_required(VERSION 3.2.0 FATAL_ERROR) # current latest stable version (if lower give FATAL_ERROR)
project(pdf_generator VERSION 0.1.0) # name of the project, version.
file(GLOB TARGET_SRC "./src/*.cpp") # Creates variable, using globbing.
include_directories(${PROJECT_SOURCE_DIR}/include) # list of directories to be used as header search paths.
add_executable(main ${TARGET_SRC}) # Create an executable of set of source files [exe name files to bundle].
find_library(libhpdf_location NAMES libhpdf.a) # find the location of libhpdf.a and save the value in the variable libhpdf_location.
message(STATUS ${libhpdf_location}) # print status of variable.
add_library(libhpdf STATIC IMPORTED) # Add library via a static import.
set_target_properties(
libhpdf PROPERTIES
IMPORTED_LOCATION ${libhpdf_location}
)
target_link_libraries(main libhpdf)
I've never worked with that particular library before, but skimming their CMakeLists.txt on GitHub it seems like libharu has optional dependencies on libpdf and zlib. Without knowing how you built your version of libharu I'm going to assume that both are needed.
Luckily, CMake comes with find-modules for both libpng and zlib, so adding the following should work:
find_package(PNG REQUIRED)
find_package(ZLIB REQUIRED)
set_target_properties(libhpdf
PROPERTIES
INTERFACE_LINK_LIBRARIES "ZLIB::ZLIB;PNG::PNG"
)
Looks like all you need to do is tell cmake to link libpng.
When I vcpkg install simdjson , it returns :
The package simdjson:x64-linux provides CMake targets:
find_package(simdjson CONFIG REQUIRED)
target_link_libraries(main PRIVATE simdjson::simdjson simdjson::simdjson-flags simdjson::simdjson-headers)
So I add
find_package(simdjson CONFIG REQUIRED)
target_link_libraries(main PRIVATE simdjson::simdjson simdjson::simdjson-flags simdjson::simdjson-headers)
to CMakeLists.txt to use the package simdjson
But when I vcpkg install redis-plus-plus[cxx17] , it returns nothing . What should I do to let cmake use this package ?
Unfortunately, redis-plus-plus doesn't supply CMake config files. Someone should open an issue with upstream. It's honestly pretty unacceptable to not support find_package for your library. Thus, thanks to the authors' negligence, you will have to create an imported target for their library yourself. Here's an example CMakeLists.txt, step by step. We'll start with the standard boilerplate:
cmake_minimum_required(VERSION 3.19)
project(test-redis)
Then we need to find hiredis, which is one of Redis++'s dependencies:
find_package(hiredis REQUIRED)
This will create a target called hiredis::hiredis we'll link to later. Now we'll create a target to hold the Redis++ usage information.
add_library(redis++::redis++ UNKNOWN IMPORTED)
Now we need to actually find the header path and redis++ libraries:
find_path(REDIS_PP_HEADER sw REQUIRED)
find_library(REDIS_PP_LIB redis++ REQUIRED)
And now we can tell CMake that the target we just created manages the library we just found:
set_target_properties(redis++::redis++ PROPERTIES IMPORTED_LOCATION "${REDIS_PP_LIB}")
And finally we can set up the include paths and dependency on Hiredis.
target_include_directories(redis++::redis++ INTERFACE "${REDIS_PP_HEADER}")
target_link_libraries(redis++::redis++ INTERFACE hiredis::hiredis)
We're now ready to use the library like we ought to be able to expect to.
add_executable(main main.cpp)
target_link_libraries(main PRIVATE redis++::redis++)
I'm making one program that is basically a stresstest for a couple of different machines, and I have to write the testresults to a jsonformat.
Because I don't want to manually install jsoncpp on every machine I decided to use Fetchcontent in the CMakeLists file:
cmake_minimum_required(VERSION 3.15)
project(Programma)
include(FetchContent)
FetchContent_Declare(
jsoncpp
GIT_REPOSITORY https://github.com/open-source-parsers/jsoncpp.git
GIT_TAG master
)
FetchContent_GetProperties(jsoncpp)
if (NOT jsoncpp_POPULATED)
FetchContent_Populate(jsoncpp)
add_subdirectory(${jsoncpp_SOURCE_DIR} ${jsoncpp_BINARY_DIR})
message(${jsoncpp_SOURCE_DIR})
message(${jsoncpp_BINARY_DIR})
endif ()
#FetchContent_MakeAvailable(jsoncpp)
set(CMAKE_CXX_STANDARD 17)
add_executable(Programma main.cpp)
add_library(TestSubjects.cpp TransformTests.cpp FoldTests.cpp
TestResults.h SortTests.cpp FindTests.cpp)
target_link_libraries(Programma Tests jsoncpp)
but I tried a couple of header includes like <json.h> <jsoncpp/json.h> json/json.h> but none of them work. What am I doing wrong?
While being built, the project jsoncpp doesn't provide jsoncpp target. Instead, it provides separate targets for different kind of library:
jsoncpp_static for STATIC library,
jsoncpp_lib for SHARED library,
jsoncpp_object for OBJECT library.
By default, all 3 library kinds are built, so you may choose any of them for link with:
target_link_libraries(Programma jsoncpp_lib)
Also, the correct include directive is
#include <json/json.h>
When I use PyTorch for C++, it's pretty easy to just use find_package to set up the dependency. And here is the CMakeLists.txt:
cmake_minimum_required(VERSION 3.0 FATAL_ERROR)
project(dcgan)
set(CMAKE_PREFIX_PATH /User/root/libtorch) # I added this line, does it effect?
find_package(Torch REQUIRED)
add_executable(dcgan dcgan.cpp)
target_link_libraries(dcgan "${TORCH_LIBRARIES}")
set_property(TARGET dcgan PROPERTY CXX_STANDARD 14)
There isn't any explicit command to include the header, but the header could be found if target_link_libraries(dcgan "${TORCH_LIBRARIES}") exists. I am curious why the header file could be found even there is no target_include_directories(dcgan PUBLIC ${TORCH_INCLUDE_DIRS}).
The code is on the official website of PyTorch and it works on MacOS and Linux. What happened.
ADD:
The package is in a directory where the compiler knows nothing about it.
The include path can be set as propagated setting in the dependency:
target_link_libraries
Specify libraries or flags to use when linking a given target and/or
its dependents. Usage requirements from linked library targets will be
propagated. Usage requirements of a target’s dependencies affect
compilation of its own sources.
https://cmake.org/cmake/help/latest/command/target_link_libraries.html
That means that target_link_libraries will configure the target . It will set target_compile_features, target_compile_options, target_compile_directories, if they're set as INTERFACE or PUBLIC in the dependency.
E.g.
add_library(Lib ${SRCS_LIB})
target_include_directories(Lib INTERFACE ${DIRECTORY})
add_exectuable(Exe ${SRCS_EXE})
target_link_libraries(Exe PRIVATE Lib)
In this example Exe will inherit the include directories from Lib. You don't need to set them explicitly.
That's also how Conan works, e.g. Getting started
cmake_minimum_required(VERSION 2.8.12)
project(MD5Encrypter)
add_definitions("-std=c++11")
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup()
add_executable(md5 md5.cpp)
target_link_libraries(md5 ${CONAN_LIBS})
and how it's described in Effective Modern CMake
Use exported targets of external packages.
Don’t fall back to the old
CMake style of using variables defined by external packages. Use the
exported targets via target_link_libraries instead.
Best practice is to not use target_include_directories for your dependencies.