I am writing a C++ library (header-only) and am using CMake to generate my (Visual Studio) project and solution files. I'm also writing a test suite, which is part of the same CMake project.
My problem occurs when I call target_include_directories() on the target that represents my header-only library, so that consumers of my library may find its header files. I get the following error message (even though generation is NOT aborted).
CMake Error in CMakeLists.txt:
Target "Fonts" INTERFACE_INCLUDE_DIRECTORIES property contains path:
"D:/Projects/GPC/fonts/include"
which is prefixed in the source directory.
(D:/Projects/GPC/Fonts being the top-level directory of my library project. Btw the problem remains if I move my header files to the top directory.)
The offending line in my CMakeLists.txt is this (adapted for simplicity):
target_include_directories(Fonts INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include")
I do not understand what I'm doing wrong. Without target_include_directories(), code of consumer projects simply can't include my header files (unless in installed form, but I haven't gotten to that yet, and in any case I want to be able to use my library from its build tree, without installation.)
I feel like I'm missing something basic here; yet I've searched for hours without finding a solution or explanation.
The origin of the problem is not the
target_include_directories
command itself, but the attempt toinstall
a target that has a public or interface include directory prefixed in the source path.While it is perfectly fine and desirable to use absolute paths when building the library from scratch, a third party library that pulls in a prebuilt version of that library will probably want to use a different include path. After all, you do not want all of your users to mirror the directory structure of your build machine, just to end up in the right include path.
CMake's packaging mechanism provides support for both of these use cases: You may pull in a library directly from the build tree (that is, check out the source, build it, and point
find_package()
to the directory), or from an install directory (runmake INSTALL
to copy built stuff to the install directory and pointfind_package()
to that directory). The latter approach needs to be relocatable (that is, I build and install on my machine, send you the resulting directory and you will be able to use it on your machine from a different directory structure), while the former is not.This is a very neat feature, but you have to account for it when setting up the include directories. Quoting the manual for
target_include_directories
:The
BUILD_INTERFACE
andINSTALL_INTERFACE
generator expressions do all the magic: