From 79c30779b16711cef473576d1970778990f8a2bf Mon Sep 17 00:00:00 2001 From: Jonas Rembser Date: Tue, 27 Oct 2026 12:19:47 +0000 Subject: [PATCH] [cmake] Drop CMake's cached DEPFILE list after generating a dictionary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Makefile generators of CMake < 4.0 merge the entries of a custom command's DEPFILE into CMakeFiles/.dir/compiler_depend.internal instead of replacing them (CMake issue 26399 [1], fixed in 4.0 only). Every file rootcling ever recorded therefore stays a prerequisite of the dictionary for the life of the build tree. Once one of them disappears, e.g. because a CI job restores a master build tree and then checks out a PR whose base predates a header or LinkDef that master had added, the stale entry becomes an empty rule for a missing file, which make treats as always updated: the dictionary is regenerated on every build. That is what the "Check for spurious rebuilds" CI step has been reporting for test/Tetris on every Linux job with CMake 3.x since Oct 24: Tetris loads Gui.pcm, Gui had just gained ASImageUtils and ROOTPadPainter in master, and the PR trees did not have those LinkDefs yet. Dictionaries that are OBJECT libraries only escaped because CMake re-ran in the same build, which wipes their cache; without a re-run they regenerate forever as well. Remove the cached list right after rootcling has written the fresh depfile, which is what CMake itself does for library targets whenever it regenerates. The next dependency scan then starts from the depfile alone. Only done for Makefile generators with CMake < 4.0, where the problem exists; Ninja and CMake 4 are unaffected. [1] https://gitlab.kitware.com/cmake/cmake/-/work_items/26399 🤖 Done with the help of AI --- cmake/modules/RootMacros.cmake | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/cmake/modules/RootMacros.cmake b/cmake/modules/RootMacros.cmake index 60a41913e85ed..0cfeaae9b898c 100644 --- a/cmake/modules/RootMacros.cmake +++ b/cmake/modules/RootMacros.cmake @@ -773,6 +773,22 @@ function(ROOT_GENERATE_DICTIONARY dictionary) set(touch_byproducts_command COMMAND ${CMAKE_COMMAND} -E touch_nocreate ${dictionary_byproducts}) endif() + # The Makefile generators of CMake < 4.0 merge the entries of a custom + # command's DEPFILE into CMakeFiles/.dir/compiler_depend.internal + # instead of replacing them (CMake issue 26399). A header, LinkDef or pcm + # that rootcling read once but that has since disappeared, e.g. because the + # build tree is reused across a checkout, then stays recorded as a missing + # prerequisite, and make regenerates the dictionary on every build. Dropping + # the cached list after rootcling has run makes the next dependency scan + # start from the fresh depfile only, as CMake itself does for library + # targets whenever it regenerates. + set(drop_cached_depends_command) + if(CMAKE_GENERATOR MATCHES "Makefiles" AND CMAKE_VERSION VERSION_LESS 4.0) + get_filename_component(dictionary_target ${dictionary} NAME) + set(drop_cached_depends_command COMMAND ${CMAKE_COMMAND} -E rm -f + ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${dictionary_target}.dir/compiler_depend.internal) + endif() + #---call rootcling------------------------------------------ add_custom_command( OUTPUT ${dictionary}.cxx ${pcm_name} ${rootmap_name} ${cpp_module_file} @@ -789,6 +805,7 @@ function(ROOT_GENERATE_DICTIONARY dictionary) -DR__DUMMY_CXX_STANDARD_${CMAKE_CXX_STANDARD} -MF ${depfile_path} ${touch_byproducts_command} + ${drop_cached_depends_command} DEPFILE ${depfile_path} DEPENDS ${_list_of_header_dependencies} ${_linkdef} ${ROOTCLINGDEP} ${pcm_dependencies}