Ninja generator not detecting changes to custom command dependencies

Hi, I’m using CMake 3.29.0-rc3 on Windows 11, with the Ninja generator.

I have a project with a custom command which generates a header file called gen.h, depending on an input.txt.

The CMakeLists.txt looks like this:

cmake_minimum_required(VERSION 3.20.0 FATAL_ERROR)

project(cmake_test C)

set(gen_path "${CMAKE_CURRENT_SOURCE_DIR}")
set(gen_h_path "${gen_path}/gen.h")

add_custom_command(OUTPUT "${gen_h_path}"
    COMMAND gen_files.exe
    COMMENT "Generating sources..."
    DEPENDS input.txt
    VERBATIM
)

add_executable(my_exe
    gen.h
    main.c
    src1.c
    src2.c
)

target_include_directories(my_exe PRIVATE "${gen_path}")

All of the .c files do #include "gen.h".

From https://cmake.org/cmake/help/latest/command/add_custom_command.html#examples-generating-files I understand that a custom target isn’t needed in this case, since I only have one target (my_exe) which depends on the files generated by the custom command.

The problem here is that when I change input.txt, I see that gen.h is re-generated, but the sources depending on it aren’t rebuilt:

> ninja -d explain
ninja explain: restat of output C:/Users/martin/test/gen.h older than most recent input C:/Users/martin/test/input.txt (7373880931101038 vs 7373881000530071)
[1/1] Generating sources...

If I re-run ninja at this point, only then I see that it detects the change in gen.h and rebuilds the appropriate files:

> ninja -d explain
ninja explain: output CMakeFiles/my_exe.dir/main.c.obj older than most recent input ../gen.h (7373880931779110 vs 7373881018833957)
ninja explain: CMakeFiles/my_exe.dir/main.c.obj is dirty
ninja explain: output CMakeFiles/my_exe.dir/src1.c.obj older than most recent input ../gen.h (7373880931683828 vs 7373881018833957)
ninja explain: CMakeFiles/my_exe.dir/src1.c.obj is dirty
ninja explain: output CMakeFiles/my_exe.dir/src2.c.obj older than most recent input ../gen.h (7373880931723871 vs 7373881018833957)
ninja explain: CMakeFiles/my_exe.dir/src2.c.obj is dirty
ninja explain: my_exe.exe is dirty
[4/4] Linking C executable my_exe.exe

Am I doing something wrong here? I tried creating a custom target and make my_exe depend on it, but I see the same issue.

I’ve seen https://cmake.org/cmake/help/latest/policy/CMP0154.html which seems to be related, but I’m not using file sets. Even if I set CMP0154 to OLD I see the same issue.

Note that this doesn’t happen with the Visual Studio generator. There, re-generating gen.h correctly triggers re-building of all files depending on it.

Interestingly, if I change gen_path so that the files are generated in ${CMAKE_BINARY_DIR} instead of ${CMAKE_CURRENT_SOURCE_DIR} I see the expected behavior, i.e. changes to input.txt trigger rebuilding of all the files depending on gen.h.

I don’t understand how this works, though. https://cmake.org/cmake/help/latest/command/add_custom_command.html indicates that there may be some guessing around if the OUTPUT file paths are relative, but I understand that ${CMAKE_CURRENT_SOURCE_DIR} is an absolute path.

https://gitlab.kitware.com/cmake/community/-/wikis/FAQ#how-can-i-generate-a-source-file-during-the-build also indicates that the generated files should be placed in the build directory, but doesn’t give a good reason for this either. The problem in this case is that I wish to have the generated header under version control, which would require additional complexity to copy it from the build directory back to the source directory, etc.

(EDIT: Used a more minimalistic example)

Tagging @craig.scott since you seem to be experienced in this area.

This was answered by @brad.king in https://gitlab.kitware.com/cmake/cmake/-/issues/25982#note_1520881