# Installing headers the modern way, regurgitated and revisited

**URL:** <https://discourse.cmake.org/t/installing-headers-the-modern-way-regurgitated-and-revisited/3238>\
**Category:** Code\
**Created:** [April 29, 2021, 8:29pm UTC](https://discourse.cmake.org/t/installing-headers-the-modern-way-regurgitated-and-revisited/3238 "2021-04-29T20:29:15Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![Braden](https://discourse.cmake.org/user_avatar/discourse.cmake.org/braden/32/358_2.png) [@Braden](https://discourse.cmake.org/u/Braden)\
**Post date:** [April 29, 2021, 8:29pm UTC](https://discourse.cmake.org/t/installing-headers-the-modern-way-regurgitated-and-revisited/3238/1 "2021-04-29T20:29:15Z")

</div>

Some weeks ago, under [Installing headers the modern way](https://discourse.cmake.org/t/installing-headers-the-modern-way/2934), I posed a question regarding the relationship (or lack thereof) between `target_sources(mylib PUBLIC ...)` and `install(TARGETS mylib PUBLIC_HEADER ...)`. This is a part of CMake’s current design/evolution that just doesn’t seem especially cohesive.

Well, I recently undertook writing/generating my first CMake package config file. And while I’m at it, hey, let’s make this thing relocatable, too.

Oh, boy.

So, let’s say we have:

```cmake
target_sources(
  mylib
  PUBLIC
    myfirstpublicheader.h
    mysecondpublicheader.h
)

```

Well, that just won’t do. To satisfy relocatability requirements, the `$<INSTALL_INTERFACE:>` generator expression must be used. For each header.

```cmake
target_sources(
  mylib
  PUBLIC
    $<INSTALL_INTERFACE:myfirstpublicheader.h>
    $<INSTALL_INTERFACE:mysecondpublicheader.h>
)

```

Um, ew. But okay.

Oh, wait… that little bit of code I used to copy the `INTERFACE_SOURCES` property to the `PUBLIC_HEADER` property?

```cmake
get_target_property(MYLIB_PUBLIC_HEADERS mylib INTERFACE_SOURCES)
set_target_properties(
  mylib
  PROPERTIES
    PUBLIC_HEADER "${MYLIB_PUBLIC_HEADERS}"
)

```

Well, after decorating things with `$<INSTALL_INTERFACE:>`, my headers aren’t getting installed anymore.

Hm. Okay. Well, it seems that has a perfectly trivial solution: just use the `$<BUILD_INTERFACE:>` generator expression:

```cmake
target_sources(
  mylib
  PUBLIC
    $<INSTALL_INTERFACE:myfirstpublicheader.h>
    $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/myfirstpublicheader.h>
    $<INSTALL_INTERFACE:mysecondpublicheader.h>
    $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/mysecondpublicheader.h>
)

```

And now my headers install! Yay!  
…

Oh, dear. What have I done? See, I don’t have just a couple of public headers like this contrived example code; I have tens of them, like many, many other moderately-sized C++ projects. And this thing has just exploded.

I am hoping that someone will tell me that I’ve missed a trick here. But failing that, I respectfully suggest that something has gone off the rails with this design.

---

<div class="post-metadata">

**Author:** ![Mizux](https://discourse.cmake.org/user_avatar/discourse.cmake.org/mizux/32/1920_2.png) [@Mizux](https://discourse.cmake.org/u/Mizux)\
**Post date:** [January 5, 2022, 12:47pm UTC](https://discourse.cmake.org/t/installing-headers-the-modern-way-regurgitated-and-revisited/3238/2 "2022-01-05T12:47:42Z")

</div>

My 2 cents sharing my experiences/test (with the help of Sarcasm)  
disclaimer: tests was performed during October 2018 and I didn’t check if CMake/QtCreator change their behaviour  
disclaimer 2: I don’t think it is a necro posting since it can be seen as a follow up of the author investigation but done few years before ^^;

TLDR: Don’t use “PUBLIC” in target source it will pollute your IDE project layout (for good reason)

The main problem with this solution is `myfirstpublicheader.h` and `mysecondpublicheader.h` will now be part of any dependent target sources.

e.g.  
Suppose I have an `App` depending on `A` and `B` libraries.  
`B/CMakeLists.txt`:

```cmake
...
target_sources(B
  PRIVATE
    "src/B.cc"
  PUBLIC
    $<BUILD_INTERFACE:"${CMAKE_CURRENT_SOURCE_DIR}/include/b/B.h">
    $<INSTALL_INTERFACE:"include/b/B.h">)
...
set_target_properties(B PROPERTIES
  PUBLIC_HEADER "include/b/B.h")

```

But for A I’ve used:  
`A/CMakeLists.txt`:

```cmake
target_sources(A
  PRIVATE
    "include/a/A.h"
    "src/A.cc"
  )
set_target_properties(A PROPERTIES
  PUBLIC_HEADER "include/a/A.h")

```

and finally  
`App/CMakelists.txt`:

```cmake
target_link_libraries(App PRIVATE A B)

```

note: You can found the full project here: [GitHub - Mizux/target\_sources: Test target\_source(tgt PUBLIC ...) and PUBLIC\_HEADER](https://github.com/Mizux/target_sources)

so here `B` is like your proposal/investigation unfortunately this will add the source file `b/B.h` as a source of App, fortunately it is a header so it will be ignored by the compiler but it is not what you want.  
Also in ide (QtCreator you’ll see B.h duplicate everywhere in the project layout see: [target\_sources/qtcreator.png at master · Mizux/target\_sources · GitHub](https://github.com/Mizux/target_sources/blob/master/qtcreator.png))

I guess, `PUBLIC` source is only needed when you need to build several time the same .cc BUT with options/definition provided by the dependent target itself…  
e.g. you have `AppFoo` executable which need `B.cc` compiled with define `FOO`, while you have `AppBar` executable which need `B.cc` to be compiled with define `BAR` in this case you want to inject the `B.cc` source in each executable source list so it will be compiled with the correct define but it’s a very “niche” scenario IMHO…

so it will be better to use:

```cmake
set(MYLIB_HDRS
myfirstpublicheader.h
mysecondpublicheader.h
)

target_sources(mylib PRIVATE
 myprivatesrc.cc
 ${MYLIB_HRS}
)
set_property(TARGET mylib
 PROPERTY PUBLIC_HEADER ${MYLIB_HDRS})

```

note: here we need to use `set_property(TARGET)` since it allow multiple values [_key_, **values…**] (aka list of headers file) while `set_target_property()` while being more “modern” only support pairs [_key_, **value**]  
note: I don’t know/test with `PRIVATE` **and** `BUILD_INTERFACE`/`INSTALL_INTERFACE` and use your `get_target_property()` trick instead.

For digging deeper, I really enjoy you to read the issue #1 open by Sarcasm (github discussion was not a thing at this time) and all the links provided…

---

<div class="post-metadata">

**Author:** ![mherok](https://discourse.cmake.org/user_avatar/discourse.cmake.org/mherok/32/3310_2.png) [@mherok](https://discourse.cmake.org/u/mherok)\
**Post date:** [April 5, 2023, 4:19am UTC](https://discourse.cmake.org/t/installing-headers-the-modern-way-regurgitated-and-revisited/3238/3 "2023-04-05T04:19:57Z")

</div>

From CMake 3.23 version `target_source` contain [FILE\_SET](https://cmake.org/cmake/help/latest/command/target_sources.html#file-sets) option. Now you can define your headers in one place and automatically use them on an install operation. Even `include_directories` it’s not any more necessary. This gives more improvements and can be used for modules export, for details read the documentation, [install](https://cmake.org/cmake/help/latest/command/install.html#targets) section also.

The above example can be written like this:

```auto
target_sources(
  mylib
  PUBLIC
    FILE_SET public-headers
    TYPE HEADERS
    FILES
      myfirstpublicheader.h
      mysecondpublicheader.h
)

install(TARGETS mylib FILE_SET public-headers)

```

---

<div class="post-metadata">

**Author:** ![Arto](https://discourse.cmake.org/user_avatar/discourse.cmake.org/arto/32/3574_2.png) [@Arto](https://discourse.cmake.org/u/Arto)\
**Post date:** [June 29, 2023, 2:06pm UTC](https://discourse.cmake.org/t/installing-headers-the-modern-way-regurgitated-and-revisited/3238/4 "2023-06-29T14:06:00Z")

</div>

Does @mherok’s [reply](https://discourse.cmake.org/t/installing-headers-the-modern-way-regurgitated-and-revisited/3238/3) still remain the best way to go about this?

In any case, I had to update his code a bit with CMake 3.26. Here’s what seems to work well:

```auto
cmake_minimum_required(VERSION 3.23...3.26)

# ...

target_sources(${PROJECT_NAME} PUBLIC
    FILE_SET public_headers
    TYPE HEADERS
    BASE_DIRS include
    FILES include/foobar.h)

include(GNUInstallDirs)
install(TARGETS ${PROJECT_NAME} FILE_SET public_headers)

```

---

<div class="post-metadata">

**Author:** ![Bjorn](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/b/74df32/32.png) [@Bjorn](https://discourse.cmake.org/u/Bjorn)\
**Post date:** [February 13, 2024, 10:43am UTC](https://discourse.cmake.org/t/installing-headers-the-modern-way-regurgitated-and-revisited/3238/5 "2024-02-13T10:43:59Z")

</div>

its getting easier to get somewhat modern cmake ~~, but well it still does bad stuff…~~

installing the headers, yes, ~~but when exporting targets and the cmake packages still hardcodes the path of the headers, so i guess it still needs some generator expressions in there somehow…~~

its a full time job to get the patterns right of somewhat modern cmake, worst part is that cmake still the best solution, but its far from perfect…

~~so far i still use good old fashioned manual install of the headers, so the target files does not hard code paths.~~ now i will try to use FILE\_SET in a real project

one day someone might figure out how all this should work without 45 workarounds.

edit: and of course i succeeded with looking in the wrong file… of course the build tree contains the hardcoded paths, the installed files actually looks correct.

from the looks of it cmake 3.23+ might actually be getting into a good place.
