Incomplete cmake code generated for `<Libname>Targets.cmake` if Library contains `FILE_SET`
Environment
- Affected CMake Versions: 3.23 to 3.26.3
- Operating System: Windows 11
- Generator: Ninja Multi-Config
Problem
-
Create a library with cmake with the following properties:
- Use a cmake version that support
FILE_SET
intarget_sources(...)
: 3.23 or above. - Use
target_sources(... PUBLIC FILE_SET HEADERS ...)
to specifiy the public headers related to the library -
EXPORT
the library
- Use a cmake version that support
-
Write a new project importing the exported library created in step 1.
- Use cmake version below 3.23
- Build the project: Result: Error, because the header files of the library cannot be found.
- If using cmake with at least version 3.23, building of the project succeeds. (I only tried it with 3.26.3)
Example
As an example I used the MathFunctions Project provide in the CMake source code distribution (Found under Importing-Exporting\MathFunctions
) and modified it removing all target_include_directory(...)
statements and references to include directories for installation in the install(...)
statement. I also decided to install the header (MathFunction.h
) in a subdirectory of the normal installation directory.
So the resulting CMakeList.txt
looks like the following:
# specify the C++ standard
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
# create library
add_library(MathFunctions STATIC)
target_sources(
MathFunctions
PRIVATE MathFunctions.cxx
PUBLIC FILE_SET HEADERS FILES MathFunctions.h)
# install the target and create export-set and install header file
install(
TARGETS MathFunctions
EXPORT MathFunctionsTargets
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
FILE_SET HEADERS
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/MathFunctions)
# generate and install export file
install(
EXPORT MathFunctionsTargets
FILE MathFunctionsTargets.cmake
NAMESPACE MathFunctions::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/MathFunctions)
# include CMakePackageConfigHelpers macro
# ... Rest of code identical to original CMakeLists.txt file
The reason for this change was to prevent generator expressions which generally are hard to read.
Now create and compile the code from the project Importing-Exporting\Downstream
(also part of the CMake source distribution). If the generation is done with a CMake version at minimum 3.23, everything builds as expected. If a version less than 3.23 is used, that build fails, because MathFunctions.h
cannot be found. Looking inside the generated MathFunctionsTargets.cmake
the following code section can be found:
if(NOT CMAKE_VERSION VERSION_LESS "3.23.0")
target_sources(MathFunctions::MathFunctions
INTERFACE
FILE_SET "HEADERS"
TYPE "HEADERS"
BASE_DIRS "${_IMPORT_PREFIX}/include/MathFunctions"
FILES "${_IMPORT_PREFIX}/include/MathFunctions/MathFunctions.h"
)
endif()
For versions of cmake starting with 3.23 (those that support FILE_SET
in the target_sources(...)
this is the reason why the compilation succeeds, because a FILE_SET
of type HEADERS
automatically addes the necessary properties (in this case HEADER_DIRS
to the imported libray MathFunctions::MathFunctions
. So here an else()
branch is missing setting the properties from the FILE_SET
to the target.
NOTE: I am not sure, if it is only necessary to set the HEADER_DIRS
property or whether all the properties documented under target_sources: FILE_SET related to the HEADERS
type shall be added to the else()
branch.
Workaround
There is a workaround available: Simply leave the INCLUDES
part of the install(...)
statement in place:
install(
TARGETS MathFunctions
EXPORT MathFunctionsTargets
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/MathFunctions
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
FILE_SET HEADERS
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/MathFunctions)
This leads to code duplication which in general is not a good idea: You have to specifiy DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/MathFunctions)
for header lookup twice. The resulting lines in MathFunctionsTargets.cmake
are:
set_target_properties(MathFunctions::MathFunctions PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include/MathFunctions"
)