'codegen' target when generating only headers

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 ?

Can you please provide a project which demonstrates the issue?

will do, hopefully tomorrow

CodeGen.tar.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

checking in, did I provide sufficient information to reproduce ?

Update your example with

this 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
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.

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 ?

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 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.

many thanks for the information, much appreciated.

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.

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