install(EXPORT): Importing File Sets silently ignored with CMake < 3.23.0
Perhaps I'm making fuss about effectively nothing, but meh let's see.
I've been meaning to try and migrate from my current technique of installing headers to using File Sets, so I gave it a shot on one of my more complex projects.
I replaced this:
target_include_directories(mylib PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include/prefix>
)
install(DIRECTORY include/
DESTINATION "include/prefix/"
)
with this:
target_sources(mylib PUBLIC
FILE_SET HEADERS
BASE_DIRS include
FILES include/header.h
)
install(
TARGETS a
EXPORT mylibTargets
FILE_SET HEADERS
DESTINATION include/prefix
)
and it works quite well. In a way almost too well, as the documentation for File Sets I think is sometimes a little too vague given it works so "automagically".
Ultimately this resulted in the following in the target import script:
if(NOT CMAKE_VERSION VERSION_LESS "3.23.0")
target_sources(MyLib::MyLib
INTERFACE
FILE_SET "HEADERS"
TYPE "HEADERS"
BASE_DIRS "${_IMPORT_PREFIX}/include/prefix"
FILES "${_IMPORT_PREFIX}/include/prefix/header.h"
)
endif()
Small Tangent Regarding Documentation
Click to expand
It's awesome that the import version of the file set has it's `BASE_DIRS` recalculated relative to their install destination and included via target_sources; however, the latter isn't really stated in the documentation so it took me a while to pull the trigger on trying this as I was left confused on how the input `BASE_DIRS` would translate correctly to my install tree. While I saw the comments about how the 'FILE_SET' option modifies `INTERFACE_INCLUDE_DIRECTORIES` with a `$` wrapper, it says nothing about $ so I was further confused on how the import script accounted for the different include layout in the install tree. Of course, it turns out that because this new approach doesn't rely on the `INTERFACE_INCLUDE_DIRECTORIES` property and just uses `target_sources()` you don't need to worry about this at all, which ironically feels weird coming from the older approach where you had to explicitly tell CMake the difference via `target_include_directories()` and generator expressions. Just a simple note somewhere saying that File Sets handles this transparently for you would help a lot I think.This Issue:
Anyway, the issue at hand is this: if(NOT CMAKE_VERSION VERSION_LESS "3.23.0")
.
Because of this, and because the version check at the top of the generated script is just if(CMAKE_VERSION VERSION_LESS "2.8.3")
, if you try to import this package on '3.23.0 > CMake >= 2.8.3' you'd mistakenly believe that the package is broken since the headers seem inexplicitly missing from your include path, when in reality your CMake version is just too low. But CMake won't tell you this with a sensible error, you'll instead just see file not found errors everywhere from your compiler.
Is this really intentional? I'm surprised that the generated statement didn't have an extra bit like this:
if(NOT CMAKE_VERSION VERSION_LESS "3.23.0")
target_sources(MyLib::MyLib
INTERFACE
FILE_SET "HEADERS"
TYPE "HEADERS"
BASE_DIRS "${_IMPORT_PREFIX}/include/prefix"
FILES "${_IMPORT_PREFIX}/include/prefix/library/test.h"
)
else()
target_include_directories(MyLib::MyLib INTERFACE "${_IMPORT_PREFIX}/include/prefix")
endif()
with any additional BASE_DIRS
also placed under the new statement, or simply trigger a FATAL_ERROR if < 3.23.0.