Search code examples
cmakecmake-custom-commandadd-custom-target

CMake: Regenerate source file if target gets rebuilt


I am trying to embed the build date into a source file, so that every time a specific target gets built, the embedded date is refreshed, without regenerating every time the overall project built.

I.e. I have a header file builddate.h that is generated by a command that has a set of #defines. This header file is then included from other source files.

My first attempt was this:

add_custom_target(builddate COMMAND <command that generates header file>)
add_library(mylibrary ...)
add_dependencies(mylibrary builddate)

This correctly generates the header file, however the header file is generated every time, regardless of whether the mylibrary target needs to be rebuilt.

Trying with a custom command instead, i.e.

add_custom_command(OUTPUT builddate.h COMMAND <command that generates header file>)
add_library(mylibrary ... builddate.h)

correctly generates the header once, but if the mylibrary target is rebuilt, the header is not regenerated as builddate.h is already up to date.

This feels like something that should be reasonably common, but I cannot figure out what incantation of custom commands and targets will give me the desired effect. What I want is to call the command every time the mylibrary target is built, without spurious rebuilds if nothing has changed or if unrelated targets (such as executables using mylibrary) are built.

Using a PRE_BUILD custom command would sound like a good idea, but the docs state that this gets invoked just prior to PRE_LINK commands for generators other than Visual Studio, i.e. after sources are compiled. This seems like it would make this unsuitable for this purpose, as the header is needed while compiling the sources.


Solution

  • Found an old thread at https://cmake.org/pipermail/cmake/2010-October/040247.html suggesting to call CMake's --build for a target as a PRE_LINK command:

    # This is the library that I want to build
    add_library(mylibrary ...)
    
    # Set up a library that contains the code depending on the build date
    # Use an OBJECT library because we don't need it to be a full static lib
    # we just want to build some source that would "normally" have been part of mylibrary
    add_library(builddate OBJECT EXCLUDE_FROM_ALL codethatusesbuilddate.cpp)
    
    # Add a PRE_LINK command for mylibrary so that prior to linking we
    # 1. Generate the builddate.h header
    # 2. Call CMake to build the builddate library we just set up
    add_custom_command(
        TARGET mylibrary PRE_LINK
        COMMAND <command that generates builddate.h>
        COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target builddate
    )
    
    # We also need to link with the library
    # NOTE: uses the generator expression to link with the output files rather than the target
    # to avoid CMake setting up a dependency from builddate to mylibrary which I think
    # would cause builddate to be built prior to building mylibrary, but at that point we
    # haven't generated the header yet. Which we could fix, but then we'd just build it twice
    target_link_libraries(mylibrary PRIVATE $<TARGET_OBJECTS:builddate>)
    
    
    

    This feels a little awkward, but it seems to work.

    Footnote: Generating the header is easily done using CMake, i.e. first configure_file or similar to create a CMake script that does the generation, then invoke ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/generated_cmake_file.cmake as the command to generate the header.