Add preprocessor definitions to FetchContent in cmake - c++

I'm using CMake to compile my C++ project and I have dependency, which is downloaded using FetchContent, but by default, that dependency is using exceptions, that are disabled in my project and so I have to pass preprocessor definition to this dependency to disable them. This is what I do:
FetchContent_Declare(
sioclient
GIT_REPOSITORY https://github.com/socketio/socket.io-client-cpp.git
GIT_TAG 3.1.0
)
FetchContent_MakeAvailable(sioclient)
add_compile_definitions("_WEBSOCKETPP_NO_EXCEPTIONS_")
Looks like CMake passes add_compile_definitions into anything in my project, but not into dependency. How can I set preprocessor definitions for dependencies downloaded and populated by FetchContent?

It seams to work when add_compile_definitions(_WEBSOCKETPP_NO_EXCEPTIONS_) is added before the FetchContent stuff.
Or alternatively, it works with a target specific definition:
target_compile_definitions(sioclient PUBLIC _WEBSOCKETPP_NO_EXCEPTIONS_)

Related

Very slow intellisense for CMake-configured Vulkan-project

I have a Vulkan project configured with the following CMakeLists.txt on Windows:
cmake_minimum_required(VERSION 3.20)
project(Custom_Vulkan)
set(MINGW_INCLUDE "C:\\msys64\\mingw64\\include")
find_package(Vulkan REQUIRED)
find_package(glfw3 REQUIRED HINTS "C:\\msys64\\mingw64\\lib\\cmake")
add_subdirectory(shaders)
add_executable(AppTest main.cpp)
target_link_libraries(AppTest PUBLIC Vulkan::Vulkan glfw)
include_directories(${MINGW_INCLUDE})
Extensions for CMake (CMake & CMake Tools), and C++ (C/C++, C/C++ Extension Pack, C/C++ Themes) are installed, and I have configured and built my executable using the CMake Tools-extension.
Intellisense can at times be unreasonably slow and freezes completely, resulting in 0 autocompletion suggestions for both constants from built-in libraries like M_PI from cmath, but also for the Vulkan/GLFW-APIs which have been located by CMake.
Where should I search in order to locate the dependency that stalls Intellisense?
Is it possible to monitor/profile the Intellisense-search in order to find the issue?
The CMake-tools project is fully able to detect the dependencies, and compiles fine.
The Intellisense-freezes were in this case caused by dependency-issues between a FetchContent repository dependency in CMake and the C# vscode extension pack which caused an OmniSharp server-handle to remain open.
This was a standard FetchContent-pull:
FetchContent_Declare(
imgui_repo
GIT_REPOSITORY "https://github.com/ocornut/imgui.git"
)
FetchContent_MakeAvailable(imgui_repo)
Right-clicking the extension followed by disable(workspace) solved the issue.

CMake FetchContent does not copy libraries

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).

propagate dependencies to header-only ExternalProject with cmake

I'm trying to build a header-only library using CMake (Microsoft/GSL), in such a way that I can use variables like GSL_INCLUDE_DIRS and GSL_LIBRARIES to link to the target and propagate the appropriate dependencies.
The project I'm working on has a TON of sub-directories, and all the external projects are built in their own sub-directories, hence why the variables are important.
I'm using CMake 3.2.3
Typically (for a library with an actual .lib or .a) I'd do something like:
SET(TARGET_NAME gsl)
include(ExternalProject)
ExternalProject_Add(
${TARGET_NAME}-ext
URL "http://target/url"
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
) # download/unzip the header-only project
# Specify include dir
SET(${TARGET_NAME}_INCLUDE_DIRS ${CMAKE_CURRENT_BINARY_DIR}/include CACHE STRING "${TARGET_NAME} include directory")
# Library
add_library(${TARGET_NAME} SHARED IMPORTED GLOBAL)
SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES
IMPORTED_LOCATION "some/path/to/some/lib"
)
add_dependencies(${TARGET_NAME} ${TARGET_NAME}-ext)
SET(${TARGET_NAME}_LIBRARIES ${TARGET_NAME} CACHE STRING "${TARGET_NAME} library location")
MARK_AS_ADVANCED(${TARGET_NAME_UPPER}_DIR ${TARGET_NAME_UPPER}_INCLUDE_DIRS ${TARGET_NAME_UPPER}_LIBRARIES)
The problem here is that the header-only library has no lib to set the imported path for, so I can't use an IMPORTED library. If I don't use a library at all, then I can't set the dependencies in other modules on GSL without building (i.e. downloading/unzipping) every single time, which I don't want to do. a custom_target would have the same problem, so that's a no-go.
I think what I want is an interface library, something like
add_library(${TARGET_NAME} INTERFACE)
add_dependencies(${TARGET_NAME} ${TARGET_NAME}-ext)
but then cmake complains that
CMake Error at 3rdParty/gsl/CMakeLists.txt:33 (add_dependencies):
add_dependencies Cannot add target-level dependencies to INTERFACE library
target "gsl".
Is there someway I can use the interface library (or something) to propagate the dependency on the external project?
Disallowing dependencies on INTERFACE libraries was an oversight that was corrected in CMake version 3.3. After upgrading to the latest-stable release, I was able to use the methodology described in the question, and it worked exactly as desired.

How do you exclude a CMake target from just one configuration?

I've recently added a module to a CMake project that depends on a library that I only have compiled against the release CRT. It looks like this in CMakeLists.txt:
IF(WIN32)
ADD_LIBRARY(mymodule MODULE ${MY_LIBRARY_FILES})
TARGET_LINK_LIBRARIES(mymodule libVendor)
INSTALL(TARGETS mymodule LIBRARY)
ENDIF(WIN32)
If I try to compile this module in MSVC with debug settings, the compile fails. So what I want to do is exclude it from being compiled and installed in the debug configuration. In the release configuration, it would be used as normal. Is it possible to do this with CMake?
What you can also do is exclude the target from the default build in a certain configuration:
SET_TARGET_PROPERTIES(mymodule PROPERTIES EXCLUDE_FROM_DEFAULT_BUILD_DEBUG True)
You can't have a target that is left out of a configuration, but you can have a library that is empty (or nearly empty) due to conditional compilation of its source code. And you can link to another library in a configuration specific way using the "optimized" and "debug" keywords to target_link_libraries.
For example, in your library source files, you could do:
#ifdef _DEBUG
// ... Debug code, possibly just a dummy function if necessary, goes here
#else
// ... Release code, the real deal, goes here
#endif
Then, you can specify that you only link to libVendor in the Release build, by using the "optimized" keyword for target_link_libraries, like this:
if(WIN32)
add_library(mymodule ...)
target_link_libraries(mymodule optimized libVendor)
install(TARGETS mymodule LIBRARY)
endif()
The target_link_libraries documentation explains the use of these keywords, and also mentions that you can define IMPORTED targets to achieve per-configuration effects. In order to define IMPORTED targets, though, the library files must have already been built, and you'd have to point to them. So... conditional compilation is probably the easiest way to do what you want to do.

How do I set up CMake to generate header-only projects?

I want to set up header-only C++ (or C) library projects, but can't find a clean way.
After some searches I've found that you can't set up a normal library using add_library to do this because it requires a compilable source file.
A way to do this would be to use add_custom_target instead, this way:
# Get all headers (using search instead of explicit filenames for the example)
file( GLOB_RECURSE XSD_HEADERS
*.hxx
)
add_custom_target( libsxsd SOURCES ${XSD_HEADERS} )
But that doesn't seem to work completely here as I can't see the sources in the project generated in VS2010. I don't know if it's a bug or if I'm doing it wrong or if there is a preferred way to do this.
Update: CMake will soon include a library target called INTERFACE that is ideal for header-only projects. This feature is currently in the master branch. Reference.
Using the command add_custom_target as you propose works for me (VS2010). The files are neatly listed within my project but it has the drawback that you can't define any "Additional Include Directories" with a custom target. Instead, I now use the following:
add_library(HEADER_ONLY_TARGET STATIC test1.hpp test2.hpp)
set_target_properties(HEADER_ONLY_TARGET PROPERTIES LINKER_LANGUAGE CXX)
This sets up your header-only project as a dummy archive target. Don't worry, no actual binaries will be generated if you should try and build it (at least not in VS2010 and Xcode 4). The command set_target_properties is there because CMake will otherwise complain that it cannot infer the target language from .hpp files only.
You can do this using the recent Interface Library feature:
add_library(mylib INTERFACE)
target_include_directories(mylib INTERFACE my_include_dir1 my_include_dir2)
This creates a library target without any source files, and adds the include directories to the INTERFACE_INCLUDE_DIRECTORIES property of the target. This means that any target that links to this library will get these directories as include paths (-I) when built.
For instance, to use the library with an executable target, just do:
add_executable(myexec ${MY_SOURCES})
target_link_libraries(myexec mylib)
I think what you are looking for is just adding an include directory using the
"include_directories" command for cmake.
When doing this, if it is a third party tool that you don't have control over, I would also add the "SYSTEM" flag.
So you command would look like something like this:
include_directories(SYSTEM ${GTEST_INCLUDE_DIRS})