Declare association of headers with targets
Currently we support adding headers as sources of a target for generation or IDE integration, but not for installation. The PUBLIC_HEADER
property works only for flat header files (bar.h
rather than foo/bar.h
). CMake doesn't currently model the path by which headers are meant to be included. We only model the include directory. Content inside it must be separately handled, and reproduced in installations by install(FILES)
or install(DIRECTORY)
. See #17790 (closed) and #20208 for related discussion.
In this issue I propose a new target_headers()
command and associated set of target properties to properly model a set of header files along with their base include directory.
We define a notion of "header sets" associated with a target. There is an optional anonymous header set, plus zero or more named header sets. Each header set has an associated include directory and a list of header paths inside it.
We add a target_headers
command to define these:
target_headers(<target>
[<PUBLIC|PRIVATE|INTERFACE>
[HEADER_SET <name>]
[DIRECTORY <dir>]
HEADERS <headers>...
]...)
If DIRECTORY <dir>
is not given, it defaults to the current source directory.
If HEADER_SET <name>
is not given, the anonymous header set is defined in
target properties as follows:
-
HEADER_DIR
is defined to<dir>
. -
HEADER_SET
is defined to<headers>...
as a;
-separated list. -
[INTERFACE_]INCLUDE_DIRECTORIES
is appended with$<BUILD_INTERFACE:<dir>>
.
If HEADER_SET <name>
is given, a named header set is defined in
target properties as follows:
-
[INTERFACE_]HEADER_SETS
is appended with<name>
. -
HEADER_DIR_<name>
is defined to<dir>
. -
HEADER_SET_<name>
is defined to<headers>...
as a;
-separated list. -
[INTERFACE_]INCLUDE_DIRECTORIES
is appended with$<BUILD_INTERFACE:<dir>>
.
We extend the install(TARGETS)
command to specify installation of header sets:
install(TARGETS <targets>...
[HEADERS DESTINATION <dest>] # anonymous header set
[HEADER_SET <name>... DESTINATION <dest>]... # named header sets
)
This installs the header files from each set to the same path inside its destination.
If the target installation is included in an EXPORT
, then a corresponding
install(EXPORT)
will define properties on the imported target:
-
HEADER_DIR
is defined to the anonymous header set's install destination, if any. -
HEADER_SET
is defined to the anonymous header set's headers. This is copied from the original target. -
HEADER_DIR_<name>
is defined to the named set's install destination. -
HEADER_SET_<name>
is defined to the named set's headers. This is copied from the original target. -
INTERFACE_HEADER_SETS
lists the named header sets that were installed. -
INTERFACE_INCLUDE_DIRECTORIES
lists all the header set install destinations.
Additional details:
- The anonymous header set can only be specified as
PUBLIC
orINTERFACE
, and can always be installed. - Named header sets can be installed only if they are listed in
INTERFACE_HEADER_SETS
. - Header sets can be defined more than once, but must all share the same
DIRECTORY
andPUBLIC/PRIVATE/INTERFACE
specifiers. - Headers listed in the anonymous header set, and in named header sets listed in
HEADER_SETS
, are treated likeSOURCES
for purposes of IDE integration. Validation would consider these as usable by sources in the target itself.
Example using the anonymous header set (simple/common case):
add_library(foo INTERFACE)
target_headers(foo PUBLIC HEADERS foo/foo1.h foo/foo2.h)
install(TARGETS foo HEADERS DESTINATION include)
Example using named header sets:
add_library(bar bar1.c bar2.c)
target_headers(bar
PUBLIC
HEADER_SET bar1 DIRECTORY bar1/include HEADERS bar1a.h bar1b.h
HEADER_SET bar2 DIRECTORY bar2/include HEADERS bar2a.h bar2b.h
PRIVATE
HEADER_SET barPriv DIRECTORY barPriv/include HEADERS barPrivA.h barPrivB.h
)
install(TARGETS bar
HEADER_SET bar1 DESTINATION include/bar1
HEADER_SET bar2 DESTINATION include/bar2
)