# 'codegen' target when generating only headers

**URL:** https://discourse.cmake.org/t/codegen-target-when-generating-only-headers/15537
**Category:** Development
**Created:** [March 3, 2026, 3:21pm UTC](https://discourse.cmake.org/t/codegen-target-when-generating-only-headers/15537 "2026-03-03T15:21:29Z")
**Posts on this page:** 11
**Page:** 1

<div class="post-metadata">

### Author: ![Lieven](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/l/c37758/32.png) [@Lieven](https://discourse.cmake.org/u/Lieven)
#### Post date: [March 3, 2026, 3:21pm UTC](https://discourse.cmake.org/t/codegen-target-when-generating-only-headers/15537/1 "2026-03-03T15:21:29Z")

</div>

I wonder if the following behavior is a bug or was intended. When a custom target/command pattern is used to generate code (say some cpp files (with or without some header files)), the new codegen feature works correctly, and is a really wonderful addition to invoke the codegen first just before static checkers can step in.

However, when the custom target/command is generating headers only, which will become part of a header only library (aka INTERFACE) (and explicitly specified via the fileset mechanism), it falls down, and the headers are not generated.  
I hope this is a bug so it can be corrected. Or is there something I should pay attention to, and as such I might be making a mistake ?

Any thoughts on this ?

---

<div class="post-metadata">

### Author: ![ben.boeckel](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/b/ea5d25/32.png) [@ben.boeckel](https://discourse.cmake.org/u/ben.boeckel)
#### Post date: [March 3, 2026, 8:58pm UTC](https://discourse.cmake.org/t/codegen-target-when-generating-only-headers/15537/2 "2026-03-03T20:58:01Z")

</div>

Can you please provide a project which demonstrates the issue?

---

<div class="post-metadata">

### Author: ![Lieven](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/l/c37758/32.png) [@Lieven](https://discourse.cmake.org/u/Lieven)
#### Post date: [March 3, 2026, 10:48pm UTC](https://discourse.cmake.org/t/codegen-target-when-generating-only-headers/15537/3 "2026-03-03T22:48:06Z")

</div>

will do, hopefully tomorrow

---

<div class="post-metadata">

### Author: ![Lieven](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/l/c37758/32.png) [@Lieven](https://discourse.cmake.org/u/Lieven)
#### Post date: [March 4, 2026, 8:47am UTC](https://discourse.cmake.org/t/codegen-target-when-generating-only-headers/15537/4 "2026-03-04T08:47:01Z")

</div>

[CodeGen.tar.gz](https://discourse.cmake.org/uploads/short-url/g22yZj53hRCLW4SoJof7RFyiwzT.gz) (1.7 KB)

Sample project added (CodeGen.tar.gz), little stupid project to reproduce.

2 types of code generation: both start from a file containing a numeric value

- 1 will generate a header only library out of it, having this value as a named (inline) constant (GenHdr → will use the ‘Values’)
- 1 will generate a regular library out of it, header exporting the constant, cpp file having its definition (GenLib → will use ValueLibs)  
GenHdr.sh and Genlib.sh are the ‘generators’  
GenHdr.cmake and GenLib.cmake are cmake eco system (custom command and custom target)  
User is an executable target that uses both

The directories Values and ValueLibs contain the 'source txt files containing the constant numeric values),  
each with their own CMakeLists.txt which invoke the creation of the custom commands/targets

invoke cmake as:  
1)cmake -DCMAKE\_BUILD\_TYPE=Release -B ./build  
2)a)cmake --build ./build

==\> all gets generated and build

if instead of 2) a) we do  
2)b cmake --build ./build --target codegen  
==\> only the full library variants are generated, not the header only ones

---

<div class="post-metadata">

### Author: ![Lieven](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/l/c37758/32.png) [@Lieven](https://discourse.cmake.org/u/Lieven)
#### Post date: [March 9, 2026, 1:14pm UTC](https://discourse.cmake.org/t/codegen-target-when-generating-only-headers/15537/5 "2026-03-09T13:14:30Z")

</div>

checking in, did I provide sufficient information to reproduce ?

---

<div class="post-metadata">

### Author: ![brad.king](https://discourse.cmake.org/user_avatar/discourse.cmake.org/brad.king/32/11_2.png) [@brad.king](https://discourse.cmake.org/u/brad.king)
#### Post date: [March 9, 2026, 2:54pm UTC](https://discourse.cmake.org/t/codegen-target-when-generating-only-headers/15537/6 "2026-03-09T14:54:23Z")

</div>

Update your example with

> **this patch**
>
> ```patch
> diff --git a/cmake/GenHdr.cmake b/cmake/GenHdr.cmake
> index e36af08..729ec50 100644
> --- a/cmake/GenHdr.cmake
> +++ b/cmake/GenHdr.cmake
> @@ -42,7 +42,7 @@ function(AddValueTarget Value LibraryName)
> VERBATIM
> )
> 
> - add_custom_target(${LibraryName}_CT
> + add_custom_target(${LibraryName}_CT ALL
> WORKING_DIRECTORY
> "${CMAKE_CURRENT_BINARY_DIR}"
> DEPENDS
> 
> ```

to make the generated headers part of the `all` target, and they should build as part of `codegen` too.

Alternatively, update your example with

> **this patch**
>
> ```patch
> diff --git a/cmake/GenHdr.cmake b/cmake/GenHdr.cmake
> index e36af08..4e4ac89 100644
> --- a/cmake/GenHdr.cmake
> +++ b/cmake/GenHdr.cmake
> @@ -56,7 +56,7 @@ function(AddValueTarget Value LibraryName)
> 
> target_sources(
> ${LibraryName}
> - INTERFACE
> + PUBLIC
> FILE_SET HEADERS
> BASE_DIRS
> "${OutputDir}/include"
> 
> ```

to make the header-only interface libraries part of the build system, and therefore `all` and `codegen`.

With the latter approach you shouldn’t need the custom targets to generate anything.

---

<div class="post-metadata">

### Author: ![Lieven](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/l/c37758/32.png) [@Lieven](https://discourse.cmake.org/u/Lieven)
#### Post date: [March 9, 2026, 4:31pm UTC](https://discourse.cmake.org/t/codegen-target-when-generating-only-headers/15537/7 "2026-03-09T16:31:58Z")

</div>

Many thanks for this information, I can confirm both suggestions work.

If I understand correctly, for the second suggestion, it means the add\_custom\_target can be removed, and as such the add\_dependencies instruction linking the header library to the custom target.  
I tried this, and this works correctly, and when the input file gets touched, the generation is ran (tested with target codegen), so this solution appeals to me very much.  
The one thing which is strange to me is, that in many other target\_xxx() methods, when applied to an INTERFACE library, one also had to use the keyword INTERFACE, but for this specific target\_sources we should use PUBLIC. Could you explain the rationale please ?

---

<div class="post-metadata">

### Author: ![brad.king](https://discourse.cmake.org/user_avatar/discourse.cmake.org/brad.king/32/11_2.png) [@brad.king](https://discourse.cmake.org/u/brad.king)
#### Post date: [March 9, 2026, 5:40pm UTC](https://discourse.cmake.org/t/codegen-target-when-generating-only-headers/15537/8 "2026-03-09T17:40:23Z")

</div>

> [@Lieven](#):
>
> but for this specific target\_sources we should use PUBLIC. Could you explain the rationale please ?

Using `PUBLIC` instead of `INTERFACE` on that `target_sources` call makes the header set (file set of type `HEADERS`) part of the source files attached directly to the target. See [`add_library` docs](https://cmake.org/cmake/help/v4.3/command/add_library.html#interface-with-sources) on how an `INTERFACE` library with its own `SOURCES` and/or `HEADER_SETS` participates in the build system. This feature was added to represent header-only libraries with generated headers without using a corresponding custom target.

---

<div class="post-metadata">

### Author: ![Lieven](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/l/c37758/32.png) [@Lieven](https://discourse.cmake.org/u/Lieven)
#### Post date: [March 9, 2026, 5:58pm UTC](https://discourse.cmake.org/t/codegen-target-when-generating-only-headers/15537/9 "2026-03-09T17:58:47Z")

</div>

many thanks for the information, much appreciated.

---

<div class="post-metadata">

### Author: ![Lieven](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/l/c37758/32.png) [@Lieven](https://discourse.cmake.org/u/Lieven)
#### Post date: [March 10, 2026, 9:09am UTC](https://discourse.cmake.org/t/codegen-target-when-generating-only-headers/15537/10 "2026-03-10T09:09:41Z")

</div>

to keep the information complete in this discussion, “Professional Cmake" (22nd edition) in chapter 19.2.4 “Interface Libraries” touches this topic too. It first describes the pre cmake 3.19 solution (aka alternative 1 from above –\> add ALL to the custom target), next it describes it the modern way as follows:

- drop the custom target
- enumerate the headers directly during add\_library (INTERFACE keyword only in that way)
  - the example enumerates them as plain list, not a file set

Note: do not be tricked, in this case a target\_include\_directories is still needed.  
I have tested it, and this approach also works fine.

Personally I think it is more consistent, all the INTERFACE keyword (kind of what people expect), but on the other hand FILE\_SETs has other benefits (the modern way for target\_include\_directories) and more re-usable for install instructions, but in this case PUBLIC keyword instead of INTERFACE. My 2cents, would have been nice if it would also work with the INTERFACE keyword.

---

<div class="post-metadata">

### Author: ![craig.scott](https://discourse.cmake.org/user_avatar/discourse.cmake.org/craig.scott/32/20_2.png) [@craig.scott](https://discourse.cmake.org/u/craig.scott)
#### Post date: [March 10, 2026, 8:58pm UTC](https://discourse.cmake.org/t/codegen-target-when-generating-only-headers/15537/11 "2026-03-10T20:58:08Z")

</div>

> [@Lieven](#):
>
> “Professional Cmake" (22nd edition) in chapter 19.2.4 “Interface Libraries” touches this topic too. It first describes the pre cmake 3.19 solution (aka alternative 1 from above –\> add ALL to the custom target), next it describes it the modern way as follows:…

I probably need to update that advice. File sets is generally the way I recommend these days.
