(Full disclosure; this report was AI generated. I reviewed it for accuracy before submission.)
Summary
src/libgit2/CMakeLists.txt uses FILE(WRITE) to produce ${PROJECT_BINARY_DIR}/include/${LIBGIT2_FILENAME}.h (typically git2.h):
|
FILE(READ "${PROJECT_SOURCE_DIR}/include/git2.h" LIBGIT2_INCLUDE) |
|
STRING(REGEX REPLACE "#include \"git2\/" "#include \"${LIBGIT2_FILENAME}/" LIBGIT2_INCLUDE "${LIBGIT2_INCLUDE}") |
|
FILE(WRITE "${PROJECT_BINARY_DIR}/include/${LIBGIT2_FILENAME}.h" ${LIBGIT2_INCLUDE}) |
FILE(READ "${PROJECT_SOURCE_DIR}/include/git2.h" LIBGIT2_INCLUDE)
STRING(REGEX REPLACE "#include \"git2/" "#include \"${LIBGIT2_FILENAME}/" LIBGIT2_INCLUDE "${LIBGIT2_INCLUDE}")
FILE(WRITE "${PROJECT_BINARY_DIR}/include/${LIBGIT2_FILENAME}.h" ${LIBGIT2_INCLUDE})
Unlike configure_file(), FILE(WRITE) unconditionally updates the output file's timestamp even when the content is unchanged. Since this header is included (directly or transitively) by virtually every libgit2 source file, the timestamp change triggers a full rebuild of the library on every cmake configure.
This is particularly painful when consuming libgit2 via FetchContent / add_subdirectory, where re-running cmake is common during development.
Other generated headers in the project (git2_features.h, experimental.h) already use configure_file() and don't have this problem.
Suggested fix
Replace the FILE(WRITE) with a write-to-temp + configure_file(COPYONLY) pattern, or the equivalent file(COPY_FILE ... ONLY_IF_DIFFERENT):
FILE(READ "${PROJECT_SOURCE_DIR}/include/git2.h" LIBGIT2_INCLUDE)
STRING(REGEX REPLACE "#include \"git2/" "#include \"${LIBGIT2_FILENAME}/" LIBGIT2_INCLUDE "${LIBGIT2_INCLUDE}")
FILE(WRITE "${PROJECT_BINARY_DIR}/include/${LIBGIT2_FILENAME}.h.tmp" ${LIBGIT2_INCLUDE})
configure_file("${PROJECT_BINARY_DIR}/include/${LIBGIT2_FILENAME}.h.tmp"
"${PROJECT_BINARY_DIR}/include/${LIBGIT2_FILENAME}.h"
COPYONLY)
configure_file(COPYONLY) only updates the output when the content differs, preserving the timestamp across re-configures and avoiding unnecessary rebuilds.
Reproduction
- Add libgit2 to a project via
FetchContent or add_subdirectory
- Run
cmake .. followed by cmake --build . (first build — everything compiles)
- Run
cmake .. again (no source changes)
- Run
cmake --build . — all of libgit2 rebuilds
(Full disclosure; this report was AI generated. I reviewed it for accuracy before submission.)
Summary
src/libgit2/CMakeLists.txtusesFILE(WRITE)to produce${PROJECT_BINARY_DIR}/include/${LIBGIT2_FILENAME}.h(typicallygit2.h):libgit2/src/libgit2/CMakeLists.txt
Lines 102 to 104 in ca22574
Unlike
configure_file(),FILE(WRITE)unconditionally updates the output file's timestamp even when the content is unchanged. Since this header is included (directly or transitively) by virtually every libgit2 source file, the timestamp change triggers a full rebuild of the library on every cmake configure.This is particularly painful when consuming libgit2 via
FetchContent/add_subdirectory, where re-running cmake is common during development.Other generated headers in the project (
git2_features.h,experimental.h) already useconfigure_file()and don't have this problem.Suggested fix
Replace the
FILE(WRITE)with a write-to-temp +configure_file(COPYONLY)pattern, or the equivalentfile(COPY_FILE ... ONLY_IF_DIFFERENT):configure_file(COPYONLY)only updates the output when the content differs, preserving the timestamp across re-configures and avoiding unnecessary rebuilds.Reproduction
FetchContentoradd_subdirectorycmake ..followed bycmake --build .(first build — everything compiles)cmake ..again (no source changes)cmake --build .— all of libgit2 rebuilds