`add_custom_command` and Generated Header Dependencies

Context:

I have been struggling with this for a while, and it was working when I happened to be generating “.cpp” files, but now with c++17 and the ability to move everything generated to static inline, I no longer require generating cpp fragments, only headers.

I have developed a pre-compiler system that parses ANY source/header files, and generates a header file. (It actually generates two, but that’s an implementation detail)

You declare a target, add your SOURCES AND HEADERS as target_sources, and then pass the target to a function.

Sidenote - The precompiler is written itself with llvm’s libtooling, and understands msvc and gcc style arguments. If there is some other way to register a precompiler that I do not know, please inform me.

Finally, after setting up your target, you invoke pre_compile_target(my_target) which goes through all the source files added (headers and sources), and generates a custom command that looks like this:

add_custom_command(
      OUTPUT ${output_refl_in} ${output_rb_refl_in}
      COMMAND
        reflector -i ${source_dir} -o ${output_dir} -f ${relpath} #
        --driver-mode ${refl_driver_mode} #
        ${rt_flag} ${ct_flag} # generation flags
        ${ct_static_init_args} ${rt_static_init_args} #
        "$<$<BOOL:${id}>:-I$<SEMICOLON>$<JOIN:${id},$<SEMICOLON>-I$<SEMICOLON>>>" #
        "$<$<BOOL:${iid}>:-I$<SEMICOLON>$<JOIN:${iid},$<SEMICOLON>-I$<SEMICOLON>>>" #
        "$<$<BOOL:${co}>:-O$<SEMICOLON>\"$<JOIN:${co},\"$<SEMICOLON>-O$<SEMICOLON>\">\">" #
        "$<$<BOOL:${ico}>:-O$<SEMICOLON>\"$<JOIN:${ico},\"$<SEMICOLON>-O$<SEMICOLON>\">\">" #
        "$<$<BOOL:${cd}>:-D$<SEMICOLON>$<JOIN:${cd},$<SEMICOLON>-D$<SEMICOLON>>>" #
        "$<$<BOOL:${icd}>:-D$<SEMICOLON>$<JOIN:${icd},$<SEMICOLON>-D$<SEMICOLON>>>" #
      DEPENDS reflector ${abspath}
      VERBATIM COMMAND_EXPAND_LISTS)

    target_is_interface(is_interface ${target})
    if(is_interface)
      set(link_type INTERFACE)
      set(static_link_type PUBLIC)
    else()
      set(link_type PUBLIC)
      set(static_link_type PRIVATE)
    endif()

    target_sources(${target} ${link_type} ${output_refl_in}
                             ${output_rb_refl_in})

The generated files end in .refl.in, the dependent file is either a c++ source file, or header file.

Note that if a.cpp requires a.h, and a.ha.h.refl.in, that a.h.refl.in must be generated before any source compilation command runs.

Despite adding the a.h.refl.in as a target_source, cmake simply ignores the dependency and never generates the file.

As far as I know cmake uses implicit dependency scanning (assuming it uses ninja in this case) to find out that a.cpp depends on a.h, so why cant I tell cmake that a.h depends on a.h.refl.in which is dependent on a.h itself?

Solution:

Following this post with the new File sets: How to install an interface library with generated source in build folder - #2 by ben.boeckel

I was able to get around this by creating an interface library with a generated name “${target}_gen_lib”, and then put the generated files into the fileset of the interface library.

adding target_link_libraries(${target} ${target}_gen_lib) then allows everything to work, since the gen_lib target must be completed before ${target} is run.