Steve
Steve

Reputation: 73

CMake's add_custom_command doesnt trigger rebuild of library

Iam using CMake together with ninja to build a library. The library is depending on some code which may be generated before-hand by a custom command. The source for this code is within the source-tree and it must stay there, I have no freedom here.

Here's my CMake code:

add_library(some_lib some_source.c)

#some_source.c may be modified by the following custom command
add_custom_command(
    COMMAND codegen.exe -i some_input.xml
    COMMAND ${CMAKE_COMMAND} -E touch "${CMAKE_BINARY_DIR}/generated"
    OUTPUT  ${CMAKE_BINARY_DIR}/generated
    DEPENDS some_input.xml
    COMMENT "Generating code ..."
)
add_custom_target(generate_something DEPENDS ${CMAKE_BINARY_DIR}/generated)
add_dependencies(some_lib generate_something)

Now if some_input.xml is changed I want to also rebuild some_lib. However in practice this code doesnt seem to work, the command is executed but after it is executed some_lib is not beeing rebuild, though the timestamps of the output files (some_source.c) of the custom command are newer than the library.

Can someone give me a hint on what am I doing wrong or how I can achieve this? Or is there a problem with CMake and ninja?

Thanks in advance, if you need more information please let me know.

Steve

[edit:] It seems to be caused by the generator tool, when I use the BYPRODUCTS option of add_custom_command and replace the generator tool invocation by a cmake -E touch command on some_source.c everything works as expected. Can someone give me a hint on how to debug this, why ninja cancels the build after the generator tool has finished?

Thanks, Steve

[edit2] I found the problem, the problem was that the codegen.exe tool was generating not only one source file but several into an output folder. I need to specify all of these potentially modified sources to as "BYPRODUCTS". Than everything works as expected.

Upvotes: 0

Views: 550

Answers (1)

mydisplayname
mydisplayname

Reputation: 356

I dont want CMake to delete the file if a "clean" is executed

BYPRODUCTS will still be cleaned.

What can be done is to utilize a dependency tracking file like you've shown (the "${CMAKE_BINARY_DIR}/generated", I used *.stamp) and then add the dependency tracking file as an OBJECT_DEPENDS to some_source.c

This only works if some_source.c is pre-existing. Initial testing had some_source.c get cleaned if I tried to add the GENERATED property.

cmake_minimum_required(VERSION 3.18)

project(in_source_generated)

add_library(some_lib some_source.c)

#some_source.c may be modified by the following custom command
add_custom_command(
    # This is the command that actually generates the in source code file.
    COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_LIST_DIR}/some_input.c ${CMAKE_CURRENT_LIST_DIR}/some_source.c
    # This command generates a stamp file which is used as the dependency
    # tracking
    COMMAND ${CMAKE_COMMAND} -E touch "${CMAKE_BINARY_DIR}/some_source.stamp"
    OUTPUT  ${CMAKE_BINARY_DIR}/some_source.stamp
    DEPENDS some_input.c
    COMMENT "Generating code ..."
)
add_custom_target(generate_something DEPENDS ${CMAKE_BINARY_DIR}/some_source.stamp)
add_dependencies(some_lib generate_something)

set_source_files_properties(some_source.c PROPERTIES OBJECT_DEPENDS ${CMAKE_BINARY_DIR}/some_source.stamp)

add_executable(main main.c)
target_link_libraries(main PRIVATE some_lib)

A simple main.c

#include <stdio.h>

int get_value();

int main(int argc, char *argv[]) {
    printf("The value is %d.", get_value());
}

some_source.c and some_input.c have the same content. Just toggled the return value in some_input.c to test out.

int get_value(){
    return 4;
}

Upvotes: 1

Related Questions