Search code examples
c++cmakemetal

How to configure cmake to recompile a target when a non .cpp source file is modified


If we look at the minimal example below,

cmake_minimum_required(VERSION 3.20)

project(example)

add_executable(${PROJECT_NAME} main.cpp test.txt)

Once the executable target is built, it will only rebuild if main.cpp is modified. If test.txt is modified, it wouldn't rebuild because eventhough test.txt is included as a source for the executable target, it isn't used to compile the executable. Is there any way that we can configure cmake so that when test.txt is modified, it will trigger a rebuild?

The real use case for my application is I have a metal file that is associated with an executable target (e.g. add_executable(${PROJECT_NAME} main.cpp mylib.metal)) and I want to generate mylib.metallib along with the example executable when the target is build. I have something like

add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
                   COMMAND "the command to compile .metal into .metallib")

but this add_custom_command will only be invoked during the first compilation of the executable target, or whenever main.cpp is modified, I want this add_custom_command to be invoked also when mylib.metal is modified. How can this be done?


Solution

  • One way is to create a custom target and add a custom command to it that will generate your mylib.metallib

    cmake_minimum_required(VERSION 3.20)
    project(custom_file_target VERSION 1.0.0)
    
    add_executable(main main.cpp) # main target
    
    add_custom_target(
        custom
        DEPENDS ${CMAKE_BINARY_DIR}/mylib.metallib
    )
    
    add_custom_command(
        OUTPUT ${CMAKE_BINARY_DIR}/mylib.metallib
        COMMAND echo your command that will create mylib.metallib
        DEPENDS ${CMAKE_SOURCE_DIR}/mylib.metal
    )
    
    add_dependencies(main custom)
    

    You can swap main and custom in the last line depending on what dependency ordering you want (main depends on custom or the other way round).

    See also my other answer here: https://stackoverflow.com/a/70626372/8088550

    If mylib.metallib is actually linkable you can also think about creating an imported library that depends on your custom target, i.e.

    cmake_minimum_required(VERSION 3.20)
    project(custom_file_target VERSION 1.0.0)
    
    add_executable(main main.cpp) # main target
    
    add_custom_target(
        custom
        DEPENDS ${CMAKE_BINARY_DIR}/mylib.metallib
    )
    
    add_custom_command(
        OUTPUT ${CMAKE_BINARY_DIR}/mylib.metallib
        COMMAND echo your command that will create mylib.metallib
        DEPENDS ${CMAKE_SOURCE_DIR}/mylib.metal
    )
    
    add_library(mylib STATIC IMPORTED)
    set_property(
        TARGET mylib PROPERTY
        IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/mylib.metallib
    )
    add_dependencies(mylib custom)
    
    target_link_libraries(main PRIVATE mylib)