{"thread":{"id":"66301","subject":"[PATCH 0/4] make: precompile \"git-compat-util.h\"","startedAt":"2026-09-09T19:50:14Z","lastAt":"2026-10-03T19:00:24Z","messageCount":15,"participants":["SZEDER Gábor","Junio C Hamano","Jeff King"],"isPatch":true,"patchVersion":1,"patchTotal":4},"messages":[{"id":"552365","messageId":"20260909195006.2179119-1-szeder.dev@gmail.com","threadId":"66301","inReplyTo":null,"subject":"[PATCH 0/4] make: precompile \"git-compat-util.h\"","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2026-09-09T19:50:02Z","receivedAt":"2026-09-09T19:50:14Z","isPatch":true,"body":"Update our Makefile to make use of the recently introduced precompiled\nheader to make builds faster (on my setup by about 35%).\n\n\nSZEDER Gábor (4):\n  Makefile: remove XDIFF_OBJS initialization\n  cmake: remove any \"$(*_OBJS)\" variables when parsing Makefile for\n    sources\n  Makefile: reintroduce REFTABLE_OBJS\n  Makefile: precompile \"git-compat-util.h\"\n\n .gitignore                          |  1 +\n Makefile                            | 59 +++++++++++++++++++----------\n contrib/buildsystems/CMakeLists.txt |  6 ++-\n 3 files changed, 45 insertions(+), 21 deletions(-)\n\n-- \n2.55.0.1193.g1b994e35de\n\n"},{"id":"552366","messageId":"20260909195006.2179119-2-szeder.dev@gmail.com","threadId":"66301","inReplyTo":"20260909195006.2179119-1-szeder.dev@gmail.com","subject":"[PATCH 1/4] Makefile: remove XDIFF_OBJS initialization","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2026-09-09T19:50:03Z","receivedAt":"2026-09-09T19:50:19Z","isPatch":true,"body":"Object files under 'xlib/' used to be listed in the XDIFF_OBJS\nMakefile variable so we could build a static library from them.  This\nstatic library was removed in cf680cdb95 (make: delete XDIFF_LIB, add\nxdiff to LIB_OBJS, 2025-10-02), along with filling XDIFF_OBJS with\nobject files.\n\nBut the initial empty initalization of XDIFF_OBJS remained, so remove\nit now.\n\nSigned-off-by: SZEDER Gábor <szeder.dev@gmail.com>\n---\n Makefile | 1 -\n 1 file changed, 1 deletion(-)\n\ndiff --git a/Makefile b/Makefile\nindex d4b775953d..7d9ac15c74 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -695,7 +695,6 @@ BUILTIN_OBJS =\n BUILT_INS =\n COMPAT_CFLAGS =\n COMPAT_OBJS =\n-XDIFF_OBJS =\n GENERATED_H =\n EXTRA_CPPFLAGS =\n FUZZ_OBJS =\n-- \n2.55.0.1193.g1b994e35de\n\n"},{"id":"552367","messageId":"20260909195006.2179119-3-szeder.dev@gmail.com","threadId":"66301","inReplyTo":"20260909195006.2179119-1-szeder.dev@gmail.com","subject":"[PATCH 2/4] cmake: remove any \"$(*_OBJS)\" variables when parsing Makefile for sources","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2026-09-09T19:50:04Z","receivedAt":"2026-09-09T19:50:20Z","isPatch":true,"body":"To get various lists of files, CMake parses our Makefile looking for\nlines matching e.g. \"list_var += ...\".  In case of LIB_OBJS this\npicks up the line \"LIB_OBJS += $(COMPAT_OBJS)\" as well, so the parsing\nmacro has a specific instruction to remove \"$(COMPAT_OBJS)\" from the\nresulting list.\n\nCurrently this is the only such Makefile variable to be removed from\nthe list, but the next patches will (re)introduce more variables\ncontaining lists of object files, so let's generalize that removing\ninstruction to remove any \"$(*_OBJS)\" Makefile variable as well.\n\nNote that we can't make the pattern matching the Makefile variable too\ngeneral, e.g. to match any \"$(VARIABLE)\", because some lines of our\nMakefile do contain variables as directory prefixes, e.g.\n\"UNIT_TEST_OBJS += $(UNIT_TEST_DIR)/test-lib.o\", and we must\ndefinitely keep those.\n\nSigned-off-by: SZEDER Gábor <szeder.dev@gmail.com>\n---\n\nI rarely do CMake, so it's quite possible that this is not the most\nstraightforward or idiomatic approach.\n\n contrib/buildsystems/CMakeLists.txt | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt\nindex 8f56203f34..241da0d43a 100644\n--- a/contrib/buildsystems/CMakeLists.txt\n+++ b/contrib/buildsystems/CMakeLists.txt\n@@ -102,7 +102,7 @@ project(git\n macro(parse_makefile_for_sources list_var makefile regex)\n \tfile(STRINGS ${makefile} ${list_var} REGEX \"^${regex} \\\\+=(.*)\")\n \tstring(REPLACE \"${regex} +=\" \"\" ${list_var} ${${list_var}})\n-\tstring(REPLACE \"$(COMPAT_OBJS)\" \"\" ${list_var} ${${list_var}}) #remove \"$(COMPAT_OBJS)\" This is only for libgit.\n+\tstring(REGEX REPLACE \"\\\\$\\\\([^)]*_OBJS\\\\)\" \"\" ${list_var} ${${list_var}}) # remove any \"$(*_OBJS)\" variables\n \tstring(STRIP ${${list_var}} ${list_var}) #remove trailing/leading whitespaces\n \tstring(REPLACE \".o\" \".c;\" ${list_var} ${${list_var}}) #change .o to .c, ; is for converting the string into a list\n \tlist(TRANSFORM ${list_var} STRIP) #remove trailing/leading whitespaces for each element in list\n-- \n2.55.0.1193.g1b994e35de\n\n"},{"id":"552368","messageId":"20260909195006.2179119-4-szeder.dev@gmail.com","threadId":"66301","inReplyTo":"20260909195006.2179119-1-szeder.dev@gmail.com","subject":"[PATCH 3/4] Makefile: reintroduce REFTABLE_OBJS","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2026-09-09T19:50:05Z","receivedAt":"2026-09-09T19:50:24Z","isPatch":true,"body":"Object files under \"reftable/\" used to be listed in the REFTABLE_OBJS\nMakefile variable so we could build a static library from them.  This\nstatic library was removed in f3b4c89d59 (make: delete REFTABLE_LIB,\nadd reftable to LIB_OBJS, 2025-10-02), along with filling\nREFTALBE_OBJS with object files.\n\nHowever, the reftable source files are kind of special, because the\nreftable implementation is supposed to be easily includable in other\nprojects.  Therefore, the reftable source files don't include\n\"git-compat-util.h\", with the sole exception of the purposefully\nproject-specific \"reftable/system.c\".  Consequently, they shouldn't be\ncompiled with our precompiled header, as it does include\n\"git-compat-util.h\".\n\nResurrect listing object files under \"reftable/\" in REFTABLE_OBJS (but\nnot the static library), so in the next commit we'll be able to easily\nfilter them out and keep building them the old way, without the\nprecompiled header.\n\nSigned-off-by: SZEDER Gábor <szeder.dev@gmail.com>\n---\n Makefile                            | 32 ++++++++++++++++-------------\n contrib/buildsystems/CMakeLists.txt |  4 ++++\n 2 files changed, 22 insertions(+), 14 deletions(-)\n\ndiff --git a/Makefile b/Makefile\nindex 7d9ac15c74..2c4d6f4d4a 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -707,6 +707,7 @@ OBJECTS =\n OTHER_PROGRAMS =\n PROGRAM_OBJS =\n PROGRAMS =\n+REFTABLE_OBJS =\n RUST_SOURCES =\n EXCLUDED_PROGRAMS =\n SCRIPT_PERL =\n@@ -1284,20 +1285,6 @@ LIB_OBJS += refs/iterator.o\n LIB_OBJS += refs/packed-backend.o\n LIB_OBJS += refs/ref-cache.o\n LIB_OBJS += refspec.o\n-LIB_OBJS += reftable/basics.o\n-LIB_OBJS += reftable/block.o\n-LIB_OBJS += reftable/blocksource.o\n-LIB_OBJS += reftable/error.o\n-LIB_OBJS += reftable/fsck.o\n-LIB_OBJS += reftable/iter.o\n-LIB_OBJS += reftable/merged.o\n-LIB_OBJS += reftable/pq.o\n-LIB_OBJS += reftable/record.o\n-LIB_OBJS += reftable/stack.o\n-LIB_OBJS += reftable/system.o\n-LIB_OBJS += reftable/table.o\n-LIB_OBJS += reftable/tree.o\n-LIB_OBJS += reftable/writer.o\n LIB_OBJS += remote.o\n LIB_OBJS += repack.o\n LIB_OBJS += repack-cruft.o\n@@ -1386,6 +1373,23 @@ LIB_OBJS += xdiff/xpatience.o\n LIB_OBJS += xdiff/xprepare.o\n LIB_OBJS += xdiff/xutils.o\n \n+REFTABLE_OBJS += reftable/basics.o\n+REFTABLE_OBJS += reftable/block.o\n+REFTABLE_OBJS += reftable/blocksource.o\n+REFTABLE_OBJS += reftable/error.o\n+REFTABLE_OBJS += reftable/fsck.o\n+REFTABLE_OBJS += reftable/iter.o\n+REFTABLE_OBJS += reftable/merged.o\n+REFTABLE_OBJS += reftable/pq.o\n+REFTABLE_OBJS += reftable/record.o\n+REFTABLE_OBJS += reftable/stack.o\n+REFTABLE_OBJS += reftable/system.o\n+REFTABLE_OBJS += reftable/table.o\n+REFTABLE_OBJS += reftable/tree.o\n+REFTABLE_OBJS += reftable/writer.o\n+\n+LIB_OBJS += $(REFTABLE_OBJS)\n+\n BUILTIN_OBJS += builtin/add.o\n BUILTIN_OBJS += builtin/am.o\n BUILTIN_OBJS += builtin/annotate.o\ndiff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt\nindex 241da0d43a..462c1eb5ec 100644\n--- a/contrib/buildsystems/CMakeLists.txt\n+++ b/contrib/buildsystems/CMakeLists.txt\n@@ -668,6 +668,10 @@ include_directories(${CMAKE_BINARY_DIR})\n #libgit\n parse_makefile_for_sources(libgit_SOURCES ${CMAKE_SOURCE_DIR}/Makefile \"LIB_OBJS\")\n \n+#reftable\n+parse_makefile_for_sources(reftable_SOURCES ${CMAKE_SOURCE_DIR}/Makefile \"REFTABLE_OBJS\")\n+list(APPEND libgit_SOURCES ${reftable_SOURCES})\n+\n list(TRANSFORM libgit_SOURCES PREPEND \"${CMAKE_SOURCE_DIR}/\")\n list(TRANSFORM compat_SOURCES PREPEND \"${CMAKE_SOURCE_DIR}/\")\n \n-- \n2.55.0.1193.g1b994e35de\n\n"},{"id":"552369","messageId":"20260909195006.2179119-5-szeder.dev@gmail.com","threadId":"66301","inReplyTo":"20260909195006.2179119-1-szeder.dev@gmail.com","subject":"[PATCH 4/4] Makefile: precompile \"git-compat-util.h\"","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2026-09-09T19:50:06Z","receivedAt":"2026-09-09T19:50:27Z","isPatch":true,"body":"This patch follows the idea of 671df48df8 (meson: precompile\n\"git-compat-util.h\", 2026-03-19) to make it faster to build Git using\n\"make\".  The notable differences are the boilerplate needed to wire up\nthe precompiled header with \"make\", and the selection of object files\nthat are built using the precompiled header:\n\n  - Add a new rule to precompile \"tools/precompiled.h\" into\n    \"tools/precompiled.h.gch\".\n\n    This rule and its dependencies are basically the same as our\n    existing rule and its dependencies for compiling object files from\n    our source files and, except that in this rule we don't use the\n    EXTRA_CPPFLAGS target-specific variable.  This is partly because\n    any compiler flags in that target-specific variable can't possibly\n    make sense for the project-wide \"git-compat-util.h\", and partly\n    because it could run afoul of \"make\"'s quirks with target-specific\n    variables and GCC's \"-Winvalid-pch\" compiler option [1].\n\n    Our Makefile always writes object files next to the source files\n    they were compiled from.  Since a precompiled header is in many\n    ways similar to an object file, let's follow suit and write it\n    next to the header file it was created from as well.  671df48df8\n    doesn't mention why \"precompiled.h\" was placed in \"tools/\".\n\n    Use the \".gch\" suffix for the precompiled header file, because\n    that's the only suffix GCC looks for when searching for a\n    precompiled header.  Clang apparently looks for both \".pch\" and\n    \".gch\" suffixes.  If we ever encounter a compiler which only\n    supports a different precompiled header suffix, then we might make\n    the suffix configurable via a Makefile knob; but until then it\n    remains hard-coded for the sake of simplicity.\n\n  - Declare the precompiled header's dependencies, so it gets rebuilt\n    when any of our header files it includes is modified.\n\n    When using computed header dependencies, then the compiler can\n    generate the Makefile snippet containing the header files inlcuded\n    by the precompiled header, just like when compiling a C source\n    file into an object file.\n\n    When not using computed header dependencies, then make the\n    precompiled header depend on the same header files that our object\n    files depend on, i.e. those listed in LIB_H and GENERATED_H.  This\n    is not ideal, because the precompiled header will be rebuilt even\n    when a header file it doesn't actually depend on changes; but in\n    that case all object files will be rebuilt as well anyway.\n\n  - List the object files that are built using the precompiled header\n    in the PRECOMPILED_HEADER_USERS variable:\n\n    - The precompiled header should not change what actually gets\n      compiled.  Therefore, use the precompiled header only when\n      compiling source files that start with including\n      \"git-compat-util.h\" (directly or indirectly, e.g. via\n      \"builtin.h\"), or its inclusion is only preceeded by #define\n      directives that don't influence \"git-compat-util.h\" between its\n      include guards [2] (currently DISABLE_SIGN_COMPARE_WARNINGS,\n      USE_THE_REPOSITORY_VARIABLE or GIT_TEST_PROGRESS_ONLY). [3]\n\n      Several (but not all) object or source files listed in the\n      COMPAT_OBJS, REFTABLE_OBJS and THIRD_PARTY_SOURCES variables\n      don't include \"git-compat-util.h\", therefore, for the sake of\n      simplicity, none of the files listed in these variables are\n      built with the precompiled header. [4]\n\n      Since 671df48df8 the Meson build uses the precompiled header\n      even when compiling those reftable source files that don't\n      include \"git-compat-util.h\" at all, although this change is not\n      mentioned in the commit message.\n\n    - But other than that, use the precompiled header when compiling\n      all other source files, including e.g. source files for\n      standalone executables (e.g. \"daemon.c\" for \"git-daemon\"), or\n      those in the directories \"t/helper/\" and \"t/unit-tests/\" as\n      well.\n\n      The Meson build, however, only uses the precompiled header for\n      compiling objects in \"libgit_sources\" and \"builtin_sources\",\n      despite 671df48df8 giving the impression that the precompiled\n      header is included in all compilation units.\n\n    - In short, PRECOMPILED_HEADER_USERS contains all object files\n      listed in OBJECTS, except those that are listed in COMPAT_OBJS,\n      REFTABLE_OBJS or THIRD_PARTY_SOURCES as well.\n\n  - Add a new rule to build object files listed in\n    PRECOMPILED_HEADER_USERS with the precompiled header.\n\n    This rule and its dependencies are basically the same as our\n    existing rule and its dependencies compiling object files from our\n    source files, except:\n\n    - This rule depends on the precompiled header as well, to make\n      sure that it's built before it is used.\n\n    - Use the \"-include tools/precompiled.h\" option to make the\n      precompiled header the first header in the compilation unit,\n      because only then is it used to speed things up.\n\n    - Use the \"-Winvalid-pch\" option to catch any issues when the\n      precompiled header is present but can't be used for whatever\n      reason.  While being unable to use the precompiled header would\n      normally only result in a slower build, it is probably better\n      that developers are made aware that simething is not quite\n      right.  Meson uses this option as well when compiling a source\n      file using the precompiled header.\n\n      Note, that in case of such an issue with \"-Winvalid-pch\" we\n      would get a warning for each object file that is build using the\n      precompiled header, but together with \"-Werror\" (e.g. with\n      DEVELOPER=1) it would fail the build.\n\n    Object files not listed in PRECOMPILED_HEADER_USERS are built with\n    the existing (and unchanged) rule for object files.\n\n  - This way the precompiled header is compiled only once during\n    the whole build process.\n\n    The Meson build, however, currently compiles the precompiled\n    header twice: once for \"libgit_sources\" and once for\n    \"builtin_sources\", despite 671df48df8 giving the impression that\n    it's compiled only once.  And, by the looks of it, it would have\n    to be compiled once more for each new library or executable where\n    we declare the use of the precompiled header.\n\n  - Add the Makefile knob NO_PRECOMPILED_HEADER to make it possible to\n    build without using the precompiled header for testing purposes,\n    or for any compiler that might be out there that doesn't support\n    the \"-Winvalid-pch\" option.\n\n    Note, that when this knob is set then we merely leave\n    PRECOMPILED_HEADER_USERS empty: this way nothing is built with the\n    precompiled header, but \"make clean NO_PRECOMPILED_HEADER=1\" would\n    still remove a leftover precompiled header file.\n\nWith this patch series on top of v2.55.0 I got the following build\ntime improvement:\n\n  Benchmark 1: make -j12 (rev = v2.55.0)\n    Time (mean ± σ):     29.414 s ±  0.031 s    [User: 254.786 s, System: 47.447 s]\n    Range (min … max):   29.370 s … 29.470 s    10 runs\n\n  Benchmark 2: make -j12 (rev = precompile)\n    Time (mean ± σ):     21.725 s ±  0.047 s    [User: 186.047 s, System: 35.109 s]\n    Range (min … max):   21.643 s … 21.768 s    10 runs\n\n  Summary\n    'make -j12 (rev = precompile)' ran\n      1.35 ± 0.00 times faster than 'make -j12 (rev = v2.55.0)'\n\n[1] \"make\" applies target-specific variables to the first target's\n    dependencies as well.  So, if the rule creating the precompiled\n    header included $(EXTRA_CPPFLAGS) as well, then we could get this:\n\n      $ make -s clean\n      $ make V=1 git\n      gcc -o tools/precompiled.h.gch -c -MF [...] -DSHELL_PATH='\"/bin/sh\"' '-DGIT_HTML_PATH=\"share/doc/git-doc\"' '-DGIT_MAN_PATH=\"share/man\"' '-DGIT_INFO_PATH=\"share/info\"' tools/precompiled.h\n      gcc -o git.o -c -include tools/precompiled.h -Winvalid-pch -MF [...] -DSHELL_PATH='\"/bin/sh\"' '-DGIT_HTML_PATH=\"share/doc/git-doc\"' '-DGIT_MAN_PATH=\"share/man\"' '-DGIT_INFO_PATH=\"share/info\"' git.c\n      gcc -o builtin/add.o -c -include tools/precompiled.h -Winvalid-pch -MF [...] -DSHELL_PATH='\"/bin/sh\"'  builtin/add.c\n      cc1: error: ./tools/precompiled.h.gch: not used because `GIT_MAN_PATH' not defined [-Werror=invalid-pch]\n      cc1: all warnings being treated as errors\n      make: *** [Makefile:2940: builtin/add.o] Error 1\n\n    So in this case \"git.o\" is the first object file to be build, and\n    since it has a target-specific EXTRA_CPPFLAGS and since it depends\n    on the precompiled header, the precompiled header is built with\n    the same EXTRA_CPPFLAGS as well.  But then \"builtin/add.o\" is\n    built with empty EXTRA_CPPFLAGS, and GCC's sanity checks for the\n    -Winvalid-pch option complain.  Clang apparently doesn't consider\n    this to be an issue, and builds \"git\" just fine.\n\n    Note, however, that if the first object file to be built is not\n    \"git.o\" (or one of the other object files with target-specific\n    EXTRA_CPPFLAGS), which is usually the case with e.g. \"make all\",\n    then the precompiled header is built with empty EXTRA_CPPFLAGS,\n    and then all is well.  Weird.  We already had issues with \"make\"\n    and target-specific variables in the past, see a673cfede6\n    (Makefile: Fix occasional GIT-CFLAGS breakage, 2010-03-19).\n    The Meson build doesn't use target-specific variables, and the\n    whole project needs to be rebuilt when e.g. \"mandir\" is changed,\n    although this is not mentioned in 904339edbd (Introduce support\n    for the Meson build system, 2024-12-06).\n\n[2] See baa61e46da (git-compat-util.h: move warning infra to prepare\n    for PCHs, 2026-03-19).\n\n[3] This can be verified by simply scanning through the output of:\n\n    {\n            printf '%s\\n' 'print-%:'\n            printf '\\t%s\\n' '@printf \"%s\\n\" $($*)'\n    } >/tmp/printvars.mak\n    for f in $(make -f /tmp/printvars.mak -f Makefile print-PRECOMPILED_HEADER_USERS)\n    do\n            f=\"${f%o}c\"\n            sed -n -E -e \"s%# *(define|include).*%&    $f%p\" -e '/# *include/q' \"$f\"\n    done | sort\n\n[4] \"make\"'s flexibility would allow us to separately list those\n    object files that don't include \"git-compat-util.h\", but then on\n    my (I assume fairly typical) Linux box the number of object files\n    built using the precompiled header increases only by 9 (from 536\n    to 545), which reduces the build time by about 1% (0.2s).\n    Therefore, I don't think it's worth the churn.\n\nSigned-off-by: SZEDER Gábor <szeder.dev@gmail.com>\n---\n .gitignore |  1 +\n Makefile   | 26 +++++++++++++++++++++-----\n 2 files changed, 22 insertions(+), 5 deletions(-)\n\ndiff --git a/.gitignore b/.gitignore\nindex 4da58c6754..0209bd16f2 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -233,6 +233,7 @@\n /.vscode/\n /tags\n /TAGS\n+/tools/precompiled.h.gch\n /cscope*\n /compile_commands.json\n /.cache/\ndiff --git a/Makefile b/Makefile\nindex 2c4d6f4d4a..02162719db 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -321,6 +321,9 @@ include shared.mak\n # dependency rules.  The default is \"auto\", which means to use computed header\n # dependencies if your compiler is detected to support it.\n #\n+# Define NO_PRECOMPILED_HEADER if you want to build Git without precompiling\n+# \"git-compat-util.h\".\n+#\n # Define NATIVE_CRLF if your platform uses CRLF for line endings.\n #\n # Define GIT_USER_AGENT if you want to change how git identifies itself during\n@@ -2905,8 +2908,15 @@ endif\n .PHONY: objects\n objects: $(OBJECTS)\n \n-dep_files := $(foreach f,$(OBJECTS),$(dir $f).depend/$(notdir $f).d)\n-dep_dirs := $(addsuffix .depend,$(sort $(dir $(OBJECTS))))\n+PRECOMPILED_HEADER := tools/precompiled.h\n+PRECOMPILED_HEADER_GCH := $(addsuffix .gch,$(PRECOMPILED_HEADER))\n+\n+ifndef NO_PRECOMPILED_HEADER\n+PRECOMPILED_HEADER_USERS := $(filter-out $(COMPAT_OBJS) $(REFTABLE_OBJS) $(patsubst %.c,%.o,$(THIRD_PARTY_SOURCES)),$(OBJECTS))\n+endif\n+\n+dep_files := $(foreach f,$(OBJECTS) $(PRECOMPILED_HEADER_GCH),$(dir $f).depend/$(notdir $f).d)\n+dep_dirs := $(addsuffix .depend,$(sort $(dir $(OBJECTS) $(PRECOMPILED_HEADER_GCH))))\n \n ifeq ($(uname_S),Darwin)\n \tdep_dirs += $(addsuffix .depend,$(sort $(dir contrib/credential/osxkeychain/git-credential-osxkeychain.o)))\n@@ -2940,7 +2950,13 @@ missing_compdb_dir =\n compdb_args =\n endif\n \n-$(OBJECTS): %.o: %.c GIT-CFLAGS $(missing_dep_dirs) $(missing_compdb_dir)\n+$(PRECOMPILED_HEADER_GCH): %.gch: % GIT-CFLAGS $(missing_dep_dirs) $(missing_compdb_dir)\n+\t$(QUIET_CC)$(CC) -o $@ -c $(dep_args) $(compdb_args) $(ALL_CFLAGS) $<\n+\n+$(PRECOMPILED_HEADER_USERS): %.o: %.c $(PRECOMPILED_HEADER_GCH) GIT-CFLAGS $(missing_dep_dirs) $(missing_compdb_dir)\n+\t$(QUIET_CC)$(CC) -o $*.o -c -include $(PRECOMPILED_HEADER) -Winvalid-pch $(dep_args) $(compdb_args) $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) $<\n+\n+$(filter-out $(PRECOMPILED_HEADER_USERS),$(OBJECTS)): %.o: %.c GIT-CFLAGS $(missing_dep_dirs) $(missing_compdb_dir)\n \t$(QUIET_CC)$(CC) -o $*.o -c $(dep_args) $(compdb_args) $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) $<\n \n %.s: %.c GIT-CFLAGS FORCE\n@@ -2954,7 +2970,7 @@ ifneq ($(dep_files_present),)\n include $(dep_files_present)\n endif\n else\n-$(OBJECTS): $(LIB_H) $(GENERATED_H)\n+$(OBJECTS) $(PRECOMPILED_HEADER_GCH): $(LIB_H) $(GENERATED_H)\n endif\n \n ifeq ($(GENERATE_COMPILATION_DATABASE),yes)\n@@ -3908,7 +3924,7 @@ clean: profile-clean coverage-clean cocciclean\n \t$(RM) GIT-TEST-SUITES\n \t$(RM) po/git.pot po/git-core.pot\n \t$(RM) git.rc git.res\n-\t$(RM) $(OBJECTS)\n+\t$(RM) $(OBJECTS) $(PRECOMPILED_HEADER_GCH)\n \t$(RM) headless-git.o\n \t$(RM) $(LIB_FILE)\n \t$(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) $(OTHER_PROGRAMS)\n-- \n2.55.0.1193.g1b994e35de\n\n"},{"id":"552372","messageId":"aqG6MRWKCcG/ft/s@szeder.dev","threadId":"66301","inReplyTo":"20260909195006.2179119-5-szeder.dev@gmail.com","subject":"Re: [PATCH 4/4] Makefile: precompile \"git-compat-util.h\"","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2026-09-09T19:57:37Z","receivedAt":"2026-09-09T19:57:41Z","isPatch":true,"body":"On Wed, Sep 09, 2026 at 09:50:06PM +0200, SZEDER Gábor wrote:\n>   - List the object files that are built using the precompiled header\n>     in the PRECOMPILED_HEADER_USERS variable:\n> \n>     - The precompiled header should not change what actually gets\n>       compiled.  Therefore, use the precompiled header only when\n>       compiling source files that start with including\n>       \"git-compat-util.h\" (directly or indirectly, e.g. via\n>       \"builtin.h\"), or its inclusion is only preceeded by #define\n>       directives that don't influence \"git-compat-util.h\" between its\n>       include guards [2] (currently DISABLE_SIGN_COMPARE_WARNINGS,\n>       USE_THE_REPOSITORY_VARIABLE or GIT_TEST_PROGRESS_ONLY). [3]\n> \n>       Several (but not all) object or source files listed in the\n>       COMPAT_OBJS, REFTABLE_OBJS and THIRD_PARTY_SOURCES variables\n>       don't include \"git-compat-util.h\", therefore, for the sake of\n>       simplicity, none of the files listed in these variables are\n>       built with the precompiled header. [4]\n\n>     - In short, PRECOMPILED_HEADER_USERS contains all object files\n>       listed in OBJECTS, except those that are listed in COMPAT_OBJS,\n>       REFTABLE_OBJS or THIRD_PARTY_SOURCES as well.\n\n> [4] \"make\"'s flexibility would allow us to separately list those\n>     object files that don't include \"git-compat-util.h\", but then on\n>     my (I assume fairly typical) Linux box the number of object files\n>     built using the precompiled header increases only by 9 (from 536\n>     to 545), which reduces the build time by about 1% (0.2s).\n>     Therefore, I don't think it's worth the churn.\n\nFor reference, a change to do that would look like that patch below,\nbut, as mentioned above, the benefit is rather small:\n\n  Benchmark 1: make -j12 (rev = precompile)\n    Time (mean ± σ):     21.747 s ±  0.048 s    [User: 186.120 s, System: 35.032 s]\n    Range (min … max):   21.650 s … 21.802 s    10 runs\n\n  Benchmark 2: make -j12 (rev = precompile-all)\n    Time (mean ± σ):     21.556 s ±  0.062 s    [User: 184.584 s, System: 35.016 s]\n    Range (min … max):   21.477 s … 21.675 s    10 runs\n\n  Summary\n    'make -j12 (rev = precompile-all)' ran\n      1.01 ± 0.00 times faster than 'make -j12 (rev = precompile)'\n\n  --- >8 ---\n\ndiff --git a/Makefile b/Makefile\nindex 7d9dac83c4..b5d75dc75f 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -698,6 +698,7 @@ BUILTIN_OBJS =\n BUILT_INS =\n COMPAT_CFLAGS =\n COMPAT_OBJS =\n+COMPAT_NOPCH_OBJS =\n GENERATED_H =\n EXTRA_CPPFLAGS =\n FUZZ_OBJS =\n@@ -711,6 +712,7 @@ OTHER_PROGRAMS =\n PROGRAM_OBJS =\n PROGRAMS =\n REFTABLE_OBJS =\n+REFTABLE_NOPCH_OBJS =\n RUST_SOURCES =\n EXCLUDED_PROGRAMS =\n SCRIPT_PERL =\n@@ -721,6 +723,7 @@ TEST_BUILTINS_OBJS =\n TEST_OBJS =\n TEST_PROGRAMS_NEED_X =\n THIRD_PARTY_SOURCES =\n+THIRD_PARTY_NOPCH_SOURCES =\n UNIT_TEST_PROGRAMS =\n UNIT_TEST_DIR = t/unit-tests\n UNIT_TEST_BIN = $(UNIT_TEST_DIR)/bin\n@@ -1376,20 +1379,22 @@ LIB_OBJS += xdiff/xpatience.o\n LIB_OBJS += xdiff/xprepare.o\n LIB_OBJS += xdiff/xutils.o\n \n-REFTABLE_OBJS += reftable/basics.o\n-REFTABLE_OBJS += reftable/block.o\n-REFTABLE_OBJS += reftable/blocksource.o\n-REFTABLE_OBJS += reftable/error.o\n-REFTABLE_OBJS += reftable/fsck.o\n-REFTABLE_OBJS += reftable/iter.o\n-REFTABLE_OBJS += reftable/merged.o\n-REFTABLE_OBJS += reftable/pq.o\n-REFTABLE_OBJS += reftable/record.o\n-REFTABLE_OBJS += reftable/stack.o\n REFTABLE_OBJS += reftable/system.o\n-REFTABLE_OBJS += reftable/table.o\n-REFTABLE_OBJS += reftable/tree.o\n-REFTABLE_OBJS += reftable/writer.o\n+REFTABLE_NOPCH_OBJS += reftable/basics.o\n+REFTABLE_NOPCH_OBJS += reftable/block.o\n+REFTABLE_NOPCH_OBJS += reftable/blocksource.o\n+REFTABLE_NOPCH_OBJS += reftable/error.o\n+REFTABLE_NOPCH_OBJS += reftable/fsck.o\n+REFTABLE_NOPCH_OBJS += reftable/iter.o\n+REFTABLE_NOPCH_OBJS += reftable/merged.o\n+REFTABLE_NOPCH_OBJS += reftable/pq.o\n+REFTABLE_NOPCH_OBJS += reftable/record.o\n+REFTABLE_NOPCH_OBJS += reftable/stack.o\n+REFTABLE_NOPCH_OBJS += reftable/table.o\n+REFTABLE_NOPCH_OBJS += reftable/tree.o\n+REFTABLE_NOPCH_OBJS += reftable/writer.o\n+\n+REFTABLE_OBJS += $(REFTABLE_NOPCH_OBJS)\n \n LIB_OBJS += $(REFTABLE_OBJS)\n \n@@ -1535,11 +1540,13 @@ THIRD_PARTY_SOURCES += compat/inet_ntop.c\n THIRD_PARTY_SOURCES += compat/inet_pton.c\n THIRD_PARTY_SOURCES += compat/obstack.%\n THIRD_PARTY_SOURCES += compat/poll/%\n-THIRD_PARTY_SOURCES += compat/regex/%\n-THIRD_PARTY_SOURCES += sha1collisiondetection/%\n-THIRD_PARTY_SOURCES += sha1dc/%\n-THIRD_PARTY_SOURCES += $(UNIT_TEST_DIR)/clar/%\n-THIRD_PARTY_SOURCES += $(UNIT_TEST_DIR)/clar/clar/%\n+THIRD_PARTY_NOPCH_SOURCES += compat/regex/%\n+THIRD_PARTY_NOPCH_SOURCES += sha1collisiondetection/%\n+THIRD_PARTY_NOPCH_SOURCES += sha1dc/%\n+THIRD_PARTY_NOPCH_SOURCES += $(UNIT_TEST_DIR)/clar/%\n+THIRD_PARTY_NOPCH_SOURCES += $(UNIT_TEST_DIR)/clar/clar/%\n+\n+THIRD_PARTY_SOURCES += $(THIRD_PARTY_NOPCH_SOURCES)\n \n CLAR_TEST_SUITES += u-ctype\n CLAR_TEST_SUITES += u-dir\n@@ -1959,7 +1966,7 @@ ifdef SNPRINTF_RETURNS_BOGUS\n endif\n ifdef FREAD_READS_DIRECTORIES\n \tCOMPAT_CFLAGS += -DFREAD_READS_DIRECTORIES\n-\tCOMPAT_OBJS += compat/fopen.o\n+\tCOMPAT_NOPCH_OBJS += compat/fopen.o\n endif\n ifdef OPEN_RETURNS_EINTR\n \tCOMPAT_CFLAGS += -DOPEN_RETURNS_EINTR\n@@ -2063,7 +2070,7 @@ ifdef NO_TRUSTABLE_FILEMODE\n endif\n ifdef NEEDS_MODE_TRANSLATION\n \tCOMPAT_CFLAGS += -DNEEDS_MODE_TRANSLATION\n-\tCOMPAT_OBJS += compat/stat.o\n+\tCOMPAT_NOPCH_OBJS += compat/stat.o\n endif\n ifdef NO_IPV6\n \tBASIC_CFLAGS += -DNO_IPV6\n@@ -2229,7 +2236,7 @@ ifdef SHA1_MAX_BLOCK_SIZE\n endif\n ifdef NO_HSTRERROR\n \tCOMPAT_CFLAGS += -DNO_HSTRERROR\n-\tCOMPAT_OBJS += compat/hstrerror.o\n+\tCOMPAT_NOPCH_OBJS += compat/hstrerror.o\n endif\n ifdef NO_MEMMEM\n \tCOMPAT_CFLAGS += -DNO_MEMMEM\n@@ -2282,7 +2289,7 @@ ifdef UNRELIABLE_FSTAT\n endif\n ifdef NO_REGEX\n \tCOMPAT_CFLAGS += -Icompat/regex\n-\tCOMPAT_OBJS += compat/regex/regex.o\n+\tCOMPAT_NOPCH_OBJS += compat/regex/regex.o\n else\n ifdef USE_ENHANCED_BASIC_REGULAR_EXPRESSIONS\n \tCOMPAT_CFLAGS += -DUSE_ENHANCED_BASIC_REGULAR_EXPRESSIONS\n@@ -2387,17 +2394,17 @@ endif\n \n ifdef FILENO_IS_A_MACRO\n \tCOMPAT_CFLAGS += -DFILENO_IS_A_MACRO\n-\tCOMPAT_OBJS += compat/fileno.o\n+\tCOMPAT_NOPCH_OBJS += compat/fileno.o\n endif\n \n ifdef NEED_ACCESS_ROOT_HANDLER\n \tCOMPAT_CFLAGS += -DNEED_ACCESS_ROOT_HANDLER\n-\tCOMPAT_OBJS += compat/access.o\n+\tCOMPAT_NOPCH_OBJS += compat/access.o\n endif\n \n ifdef FSMONITOR_DAEMON_BACKEND\n \tCOMPAT_CFLAGS += -DHAVE_FSMONITOR_DAEMON_BACKEND\n-\tCOMPAT_OBJS += compat/fsmonitor/fsm-listen-$(FSMONITOR_DAEMON_BACKEND).o\n+\tCOMPAT_NOPCH_OBJS += compat/fsmonitor/fsm-listen-$(FSMONITOR_DAEMON_BACKEND).o\n \tCOMPAT_OBJS += compat/fsmonitor/fsm-health-$(FSMONITOR_DAEMON_BACKEND).o\n endif\n \n@@ -2531,6 +2538,7 @@ endif\n LIBS = $(filter-out %.o, $(GITLIBS)) $(EXTLIBS)\n \n BASIC_CFLAGS += $(COMPAT_CFLAGS)\n+COMPAT_OBJS += $(COMPAT_NOPCH_OBJS)\n LIB_OBJS += $(COMPAT_OBJS)\n \n # Quote for C\n@@ -2912,7 +2920,7 @@ PRECOMPILED_HEADER := tools/precompiled.h\n PRECOMPILED_HEADER_GCH := $(addsuffix .gch,$(PRECOMPILED_HEADER))\n \n ifndef NO_PRECOMPILED_HEADER\n-PRECOMPILED_HEADER_USERS := $(filter-out $(COMPAT_OBJS) $(REFTABLE_OBJS) $(patsubst %.c,%.o,$(THIRD_PARTY_SOURCES)),$(OBJECTS))\n+PRECOMPILED_HEADER_USERS := $(filter-out $(COMPAT_NOPCH_OBJS) $(REFTABLE_NOPCH_OBJS) $(patsubst %.c,%.o,$(THIRD_PARTY_NOPCH_SOURCES)),$(OBJECTS))\n endif\n \n dep_files := $(foreach f,$(OBJECTS) $(PRECOMPILED_HEADER_GCH),$(dir $f).depend/$(notdir $f).d)\ndiff --git a/config.mak.uname b/config.mak.uname\nindex 95ef6e64dc..c51ebadc40 100644\n--- a/config.mak.uname\n+++ b/config.mak.uname\n@@ -152,7 +152,7 @@ ifeq ($(uname_S),Darwin)\n \tNO_MEMMEM = YesPlease\n \tUSE_ST_TIMESPEC = YesPlease\n \tHAVE_DEV_TTY = YesPlease\n-\tCOMPAT_OBJS += compat/precompose_utf8.o\n+\tCOMPAT_NOPCH_OBJS += compat/precompose_utf8.o\n \tBASIC_CFLAGS += -DPRECOMPOSE_UNICODE\n \tBASIC_CFLAGS += -DPROTECT_HFS_DEFAULT=1\n \tHAVE_BSD_SYSCTL = YesPlease\n@@ -535,7 +535,8 @@ endif\n \tAR = compat/vcbuild/scripts/lib.pl\n \tCFLAGS =\n \tBASIC_CFLAGS = -nologo -I. -Icompat/vcbuild/include -DWIN32 -D_CONSOLE -DHAVE_STRING_H -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE\n-\tCOMPAT_OBJS = compat/msvc.o compat/winansi.o \\\n+\tCOMPAT_NOPCH_OBJS = compat/winansi.o\n+\tCOMPAT_OBJS = compat/msvc.o \\\n \t\tcompat/win32/flush.o \\\n \t\tcompat/win32/path-utils.o \\\n \t\tcompat/win32/pthread.o compat/win32/syslog.o \\\n@@ -735,7 +736,8 @@ ifeq ($(uname_S),MINGW)\n \tBASIC_LDFLAGS += -municode\n \tCOMPAT_CFLAGS += -DNOGDI -Icompat -Icompat/win32\n \tCOMPAT_CFLAGS += -DSTRIP_EXTENSION=\\\".exe\\\"\n-\tCOMPAT_OBJS += compat/mingw.o compat/winansi.o \\\n+\tCOMPAT_NOPCH_OBJS += compat/winansi.o\n+\tCOMPAT_OBJS += compat/mingw.o \\\n \t\tcompat/win32/trace2_win32_process_info.o \\\n \t\tcompat/win32/flush.o \\\n \t\tcompat/win32/path-utils.o \\\ndiff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt\nindex 462c1eb5ec..d96a4ebd84 100644\n--- a/contrib/buildsystems/CMakeLists.txt\n+++ b/contrib/buildsystems/CMakeLists.txt\n@@ -101,7 +101,7 @@ project(git\n #macros for parsing the Makefile for sources and scripts\n macro(parse_makefile_for_sources list_var makefile regex)\n \tfile(STRINGS ${makefile} ${list_var} REGEX \"^${regex} \\\\+=(.*)\")\n-\tstring(REPLACE \"${regex} +=\" \"\" ${list_var} ${${list_var}})\n+\tstring(REGEX REPLACE \"${regex} \\\\+=\" \"\" ${list_var} ${${list_var}})\n \tstring(REGEX REPLACE \"\\\\$\\\\([^)]*_OBJS\\\\)\" \"\" ${list_var} ${${list_var}}) # remove any \"$(*_OBJS)\" variables\n \tstring(STRIP ${${list_var}} ${list_var}) #remove trailing/leading whitespaces\n \tstring(REPLACE \".o\" \".c;\" ${list_var} ${${list_var}}) #change .o to .c, ; is for converting the string into a list\n@@ -669,7 +669,7 @@ include_directories(${CMAKE_BINARY_DIR})\n parse_makefile_for_sources(libgit_SOURCES ${CMAKE_SOURCE_DIR}/Makefile \"LIB_OBJS\")\n \n #reftable\n-parse_makefile_for_sources(reftable_SOURCES ${CMAKE_SOURCE_DIR}/Makefile \"REFTABLE_OBJS\")\n+parse_makefile_for_sources(reftable_SOURCES ${CMAKE_SOURCE_DIR}/Makefile \"REFTABLE_(|NOPCH_)OBJS\")\n list(APPEND libgit_SOURCES ${reftable_SOURCES})\n \n list(TRANSFORM libgit_SOURCES PREPEND \"${CMAKE_SOURCE_DIR}/\")\n-- \n2.55.0.1193.g1b994e35de\n\n\n"},{"id":"552379","messageId":"xmqqmrtqp20n.fsf@gitster.g","threadId":"66301","inReplyTo":"20260909195006.2179119-4-szeder.dev@gmail.com","subject":"Re: [PATCH 3/4] Makefile: reintroduce REFTABLE_OBJS","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-09-09T21:07:20Z","receivedAt":"2026-09-09T21:07:23Z","isPatch":true,"body":"SZEDER Gábor <szeder.dev@gmail.com> writes:\n\n> Object files under \"reftable/\" used to be listed in the REFTABLE_OBJS\n> Makefile variable so we could build a static library from them.  This\n> static library was removed in f3b4c89d59 (make: delete REFTABLE_LIB,\n> add reftable to LIB_OBJS, 2025-10-02), along with filling\n> REFTALBE_OBJS with object files.\n>\n> However, the reftable source files are kind of special, because the\n> reftable implementation is supposed to be easily includable in other\n> projects.  Therefore, the reftable source files don't include\n> \"git-compat-util.h\", with the sole exception of the purposefully\n> project-specific \"reftable/system.c\".  Consequently, they shouldn't be\n> compiled with our precompiled header, as it does include\n> \"git-compat-util.h\".\n\nThis is the first mention of \"our precompiled header\" in this\nseries, and the first hint that \"our precompiled header\" would\ninclude \"git-compat-util.h\".  It may probably give us a better\norganization to state it upfront at the beginning of the proposed\nlog message of this commit.  It is of secondary importance that once\nin the past we used to have REFTABLE_OBJS Makefile variable that\nlisted some files (but for completely different purposes).  How\nabout explaining it along this line...\n\n    The ultimate endgame of this series is to use the precompiled\n    header facility to speed up compilation, and the plan is to have\n    Git specific headers including git-compat-util.h precompiled.\n\n    The reftable sources are largely designed to be independent from\n    the Git source proper, and except for reftable/system.c they do\n    not include <git-compat-util.h>.\n\n    Move the object files in the subsystem from the LIB_OBJS to the\n    REFTABLE_OBJS Makefile variable, so that we can use LIB_OBJS as\n    list of files compiled with precompiled header files and others\n    without\n\n... or something like that, perhaps?\n\n"},{"id":"552738","messageId":"20260915060952.569535-1-szeder.dev@gmail.com","threadId":"66301","inReplyTo":"20260909195006.2179119-1-szeder.dev@gmail.com","subject":"[PATCH v2 0/4] make: precompile \"git-compat-util.h\"","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2026-09-15T06:09:48Z","receivedAt":"2026-09-15T06:10:15Z","isPatch":true,"body":"Update our Makefile to make use of the recently introduced precompiled\nheader to make builds faster (on my setup by about 35%).\n\nChanges since v1:\n  - Update the commit message of patch 3/4 per Junio's suggestion.\n\n\nSZEDER Gábor (4):\n  Makefile: remove XDIFF_OBJS initialization\n  cmake: remove any \"$(*_OBJS)\" variables when parsing Makefile for\n    sources\n  Makefile: reintroduce REFTABLE_OBJS\n  Makefile: precompile \"git-compat-util.h\"\n\n .gitignore                          |  1 +\n Makefile                            | 59 +++++++++++++++++++----------\n contrib/buildsystems/CMakeLists.txt |  6 ++-\n 3 files changed, 45 insertions(+), 21 deletions(-)\n\nRange-diff against v1:\n1:  d52a1476d1 = 1:  d52a1476d1 Makefile: remove XDIFF_OBJS initialization\n2:  5d60181efd = 2:  5d60181efd cmake: remove any \"$(*_OBJS)\" variables when parsing Makefile for sources\n3:  0296c6cffc ! 3:  cfc77c0b2c Makefile: reintroduce REFTABLE_OBJS\n    @@ Metadata\n      ## Commit message ##\n         Makefile: reintroduce REFTABLE_OBJS\n     \n    -    Object files under \"reftable/\" used to be listed in the REFTABLE_OBJS\n    -    Makefile variable so we could build a static library from them.  This\n    -    static library was removed in f3b4c89d59 (make: delete REFTABLE_LIB,\n    -    add reftable to LIB_OBJS, 2025-10-02), along with filling\n    -    REFTALBE_OBJS with object files.\n    +    In the next commit we are about to precompile \"git-compat-util.h\" with\n    +    \"make\" to reduce build times.  But using the precompiled header should\n    +    not change what actually gets compiled, therefore a source file can\n    +    only be compiled using the precompiled header if the first included\n    +    header file is \"git-compat-util.h\".\n     \n    -    However, the reftable source files are kind of special, because the\n    -    reftable implementation is supposed to be easily includable in other\n    -    projects.  Therefore, the reftable source files don't include\n    +    The reftable source files are kind of special, because the reftable\n    +    implementation is supposed to be easily includable in other projects.\n    +    Therefore, the reftable source files don't include\n         \"git-compat-util.h\", with the sole exception of the purposefully\n         project-specific \"reftable/system.c\".  Consequently, they shouldn't be\n    -    compiled with our precompiled header, as it does include\n    -    \"git-compat-util.h\".\n    +    compiled with our precompiled header.\n    +\n    +    List object files under \"reftable\" in the REFTABLE_OBJS Makefile\n    +    variable, so in the next commit we'll be able to easily filter them\n    +    out and keep building them the old way, without the precompiled\n    +    header.\n     \n    -    Resurrect listing object files under \"reftable/\" in REFTABLE_OBJS (but\n    -    not the static library), so in the next commit we'll be able to easily\n    -    filter them out and keep building them the old way, without the\n    -    precompiled header.\n    +    Note that object files under \"reftable/\" used to be listed in\n    +    REFTABLE_OBJS so we could build a static library from them.  This\n    +    static library was removed in f3b4c89d59 (make: delete REFTABLE_LIB,\n    +    add reftable to LIB_OBJS, 2025-10-02), along with filling\n    +    REFTALBE_OBJS with object files.  This change essentially reverts the\n    +    removal of REFTABLE_OBJS, but not the building of that static library.\n     \n         Signed-off-by: SZEDER Gábor <szeder.dev@gmail.com>\n     \n4:  ce9fd91413 = 4:  a933be0664 Makefile: precompile \"git-compat-util.h\"\n-- \n2.56.0.rc0.467.ge054dd0dd6\n\n"},{"id":"552739","messageId":"20260915060952.569535-2-szeder.dev@gmail.com","threadId":"66301","inReplyTo":"20260915060952.569535-1-szeder.dev@gmail.com","subject":"[PATCH v2 1/4] Makefile: remove XDIFF_OBJS initialization","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2026-09-15T06:09:49Z","receivedAt":"2026-09-15T06:10:16Z","isPatch":true,"body":"Object files under 'xlib/' used to be listed in the XDIFF_OBJS\nMakefile variable so we could build a static library from them.  This\nstatic library was removed in cf680cdb95 (make: delete XDIFF_LIB, add\nxdiff to LIB_OBJS, 2025-10-02), along with filling XDIFF_OBJS with\nobject files.\n\nBut the initial empty initalization of XDIFF_OBJS remained, so remove\nit now.\n\nSigned-off-by: SZEDER Gábor <szeder.dev@gmail.com>\n---\n Makefile | 1 -\n 1 file changed, 1 deletion(-)\n\ndiff --git a/Makefile b/Makefile\nindex d4b775953d..7d9ac15c74 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -695,7 +695,6 @@ BUILTIN_OBJS =\n BUILT_INS =\n COMPAT_CFLAGS =\n COMPAT_OBJS =\n-XDIFF_OBJS =\n GENERATED_H =\n EXTRA_CPPFLAGS =\n FUZZ_OBJS =\n-- \n2.56.0.rc0.467.ge054dd0dd6\n\n"},{"id":"552740","messageId":"20260915060952.569535-3-szeder.dev@gmail.com","threadId":"66301","inReplyTo":"20260915060952.569535-1-szeder.dev@gmail.com","subject":"[PATCH v2 2/4] cmake: remove any \"$(*_OBJS)\" variables when parsing Makefile for sources","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2026-09-15T06:09:50Z","receivedAt":"2026-09-15T06:10:18Z","isPatch":true,"body":"To get various lists of files, CMake parses our Makefile looking for\nlines matching e.g. \"list_var += ...\".  In case of LIB_OBJS this\npicks up the line \"LIB_OBJS += $(COMPAT_OBJS)\" as well, so the parsing\nmacro has a specific instruction to remove \"$(COMPAT_OBJS)\" from the\nresulting list.\n\nCurrently this is the only such Makefile variable to be removed from\nthe list, but the next patches will (re)introduce more variables\ncontaining lists of object files, so let's generalize that removing\ninstruction to remove any \"$(*_OBJS)\" Makefile variable as well.\n\nNote that we can't make the pattern matching the Makefile variable too\ngeneral, e.g. to match any \"$(VARIABLE)\", because some lines of our\nMakefile do contain variables as directory prefixes, e.g.\n\"UNIT_TEST_OBJS += $(UNIT_TEST_DIR)/test-lib.o\", and we must\ndefinitely keep those.\n\nSigned-off-by: SZEDER Gábor <szeder.dev@gmail.com>\n---\n contrib/buildsystems/CMakeLists.txt | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt\nindex 8f56203f34..241da0d43a 100644\n--- a/contrib/buildsystems/CMakeLists.txt\n+++ b/contrib/buildsystems/CMakeLists.txt\n@@ -102,7 +102,7 @@ project(git\n macro(parse_makefile_for_sources list_var makefile regex)\n \tfile(STRINGS ${makefile} ${list_var} REGEX \"^${regex} \\\\+=(.*)\")\n \tstring(REPLACE \"${regex} +=\" \"\" ${list_var} ${${list_var}})\n-\tstring(REPLACE \"$(COMPAT_OBJS)\" \"\" ${list_var} ${${list_var}}) #remove \"$(COMPAT_OBJS)\" This is only for libgit.\n+\tstring(REGEX REPLACE \"\\\\$\\\\([^)]*_OBJS\\\\)\" \"\" ${list_var} ${${list_var}}) # remove any \"$(*_OBJS)\" variables\n \tstring(STRIP ${${list_var}} ${list_var}) #remove trailing/leading whitespaces\n \tstring(REPLACE \".o\" \".c;\" ${list_var} ${${list_var}}) #change .o to .c, ; is for converting the string into a list\n \tlist(TRANSFORM ${list_var} STRIP) #remove trailing/leading whitespaces for each element in list\n-- \n2.56.0.rc0.467.ge054dd0dd6\n\n"},{"id":"552741","messageId":"20260915060952.569535-4-szeder.dev@gmail.com","threadId":"66301","inReplyTo":"20260915060952.569535-1-szeder.dev@gmail.com","subject":"[PATCH v2 3/4] Makefile: reintroduce REFTABLE_OBJS","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2026-09-15T06:09:51Z","receivedAt":"2026-09-15T06:10:20Z","isPatch":true,"body":"In the next commit we are about to precompile \"git-compat-util.h\" with\n\"make\" to reduce build times.  But using the precompiled header should\nnot change what actually gets compiled, therefore a source file can\nonly be compiled using the precompiled header if the first included\nheader file is \"git-compat-util.h\".\n\nThe reftable source files are kind of special, because the reftable\nimplementation is supposed to be easily includable in other projects.\nTherefore, the reftable source files don't include\n\"git-compat-util.h\", with the sole exception of the purposefully\nproject-specific \"reftable/system.c\".  Consequently, they shouldn't be\ncompiled with our precompiled header.\n\nList object files under \"reftable\" in the REFTABLE_OBJS Makefile\nvariable, so in the next commit we'll be able to easily filter them\nout and keep building them the old way, without the precompiled\nheader.\n\nNote that object files under \"reftable/\" used to be listed in\nREFTABLE_OBJS so we could build a static library from them.  This\nstatic library was removed in f3b4c89d59 (make: delete REFTABLE_LIB,\nadd reftable to LIB_OBJS, 2025-10-02), along with filling\nREFTALBE_OBJS with object files.  This change essentially reverts the\nremoval of REFTABLE_OBJS, but not the building of that static library.\n\nSigned-off-by: SZEDER Gábor <szeder.dev@gmail.com>\n---\n Makefile                            | 32 ++++++++++++++++-------------\n contrib/buildsystems/CMakeLists.txt |  4 ++++\n 2 files changed, 22 insertions(+), 14 deletions(-)\n\ndiff --git a/Makefile b/Makefile\nindex 7d9ac15c74..2c4d6f4d4a 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -707,6 +707,7 @@ OBJECTS =\n OTHER_PROGRAMS =\n PROGRAM_OBJS =\n PROGRAMS =\n+REFTABLE_OBJS =\n RUST_SOURCES =\n EXCLUDED_PROGRAMS =\n SCRIPT_PERL =\n@@ -1284,20 +1285,6 @@ LIB_OBJS += refs/iterator.o\n LIB_OBJS += refs/packed-backend.o\n LIB_OBJS += refs/ref-cache.o\n LIB_OBJS += refspec.o\n-LIB_OBJS += reftable/basics.o\n-LIB_OBJS += reftable/block.o\n-LIB_OBJS += reftable/blocksource.o\n-LIB_OBJS += reftable/error.o\n-LIB_OBJS += reftable/fsck.o\n-LIB_OBJS += reftable/iter.o\n-LIB_OBJS += reftable/merged.o\n-LIB_OBJS += reftable/pq.o\n-LIB_OBJS += reftable/record.o\n-LIB_OBJS += reftable/stack.o\n-LIB_OBJS += reftable/system.o\n-LIB_OBJS += reftable/table.o\n-LIB_OBJS += reftable/tree.o\n-LIB_OBJS += reftable/writer.o\n LIB_OBJS += remote.o\n LIB_OBJS += repack.o\n LIB_OBJS += repack-cruft.o\n@@ -1386,6 +1373,23 @@ LIB_OBJS += xdiff/xpatience.o\n LIB_OBJS += xdiff/xprepare.o\n LIB_OBJS += xdiff/xutils.o\n \n+REFTABLE_OBJS += reftable/basics.o\n+REFTABLE_OBJS += reftable/block.o\n+REFTABLE_OBJS += reftable/blocksource.o\n+REFTABLE_OBJS += reftable/error.o\n+REFTABLE_OBJS += reftable/fsck.o\n+REFTABLE_OBJS += reftable/iter.o\n+REFTABLE_OBJS += reftable/merged.o\n+REFTABLE_OBJS += reftable/pq.o\n+REFTABLE_OBJS += reftable/record.o\n+REFTABLE_OBJS += reftable/stack.o\n+REFTABLE_OBJS += reftable/system.o\n+REFTABLE_OBJS += reftable/table.o\n+REFTABLE_OBJS += reftable/tree.o\n+REFTABLE_OBJS += reftable/writer.o\n+\n+LIB_OBJS += $(REFTABLE_OBJS)\n+\n BUILTIN_OBJS += builtin/add.o\n BUILTIN_OBJS += builtin/am.o\n BUILTIN_OBJS += builtin/annotate.o\ndiff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt\nindex 241da0d43a..462c1eb5ec 100644\n--- a/contrib/buildsystems/CMakeLists.txt\n+++ b/contrib/buildsystems/CMakeLists.txt\n@@ -668,6 +668,10 @@ include_directories(${CMAKE_BINARY_DIR})\n #libgit\n parse_makefile_for_sources(libgit_SOURCES ${CMAKE_SOURCE_DIR}/Makefile \"LIB_OBJS\")\n \n+#reftable\n+parse_makefile_for_sources(reftable_SOURCES ${CMAKE_SOURCE_DIR}/Makefile \"REFTABLE_OBJS\")\n+list(APPEND libgit_SOURCES ${reftable_SOURCES})\n+\n list(TRANSFORM libgit_SOURCES PREPEND \"${CMAKE_SOURCE_DIR}/\")\n list(TRANSFORM compat_SOURCES PREPEND \"${CMAKE_SOURCE_DIR}/\")\n \n-- \n2.56.0.rc0.467.ge054dd0dd6\n\n"},{"id":"552742","messageId":"20260915060952.569535-5-szeder.dev@gmail.com","threadId":"66301","inReplyTo":"20260915060952.569535-1-szeder.dev@gmail.com","subject":"[PATCH v2 4/4] Makefile: precompile \"git-compat-util.h\"","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2026-09-15T06:09:52Z","receivedAt":"2026-09-15T06:10:25Z","isPatch":true,"body":"This patch follows the idea of 671df48df8 (meson: precompile\n\"git-compat-util.h\", 2026-03-19) to make it faster to build Git using\n\"make\".  The notable differences are the boilerplate needed to wire up\nthe precompiled header with \"make\", and the selection of object files\nthat are built using the precompiled header:\n\n  - Add a new rule to precompile \"tools/precompiled.h\" into\n    \"tools/precompiled.h.gch\".\n\n    This rule and its dependencies are basically the same as our\n    existing rule and its dependencies for compiling object files from\n    our source files and, except that in this rule we don't use the\n    EXTRA_CPPFLAGS target-specific variable.  This is partly because\n    any compiler flags in that target-specific variable can't possibly\n    make sense for the project-wide \"git-compat-util.h\", and partly\n    because it could run afoul of \"make\"'s quirks with target-specific\n    variables and GCC's \"-Winvalid-pch\" compiler option [1].\n\n    Our Makefile always writes object files next to the source files\n    they were compiled from.  Since a precompiled header is in many\n    ways similar to an object file, let's follow suit and write it\n    next to the header file it was created from as well.  671df48df8\n    doesn't mention why \"precompiled.h\" was placed in \"tools/\".\n\n    Use the \".gch\" suffix for the precompiled header file, because\n    that's the only suffix GCC looks for when searching for a\n    precompiled header.  Clang apparently looks for both \".pch\" and\n    \".gch\" suffixes.  If we ever encounter a compiler which only\n    supports a different precompiled header suffix, then we might make\n    the suffix configurable via a Makefile knob; but until then it\n    remains hard-coded for the sake of simplicity.\n\n  - Declare the precompiled header's dependencies, so it gets rebuilt\n    when any of our header files it includes is modified.\n\n    When using computed header dependencies, then the compiler can\n    generate the Makefile snippet containing the header files inlcuded\n    by the precompiled header, just like when compiling a C source\n    file into an object file.\n\n    When not using computed header dependencies, then make the\n    precompiled header depend on the same header files that our object\n    files depend on, i.e. those listed in LIB_H and GENERATED_H.  This\n    is not ideal, because the precompiled header will be rebuilt even\n    when a header file it doesn't actually depend on changes; but in\n    that case all object files will be rebuilt as well anyway.\n\n  - List the object files that are built using the precompiled header\n    in the PRECOMPILED_HEADER_USERS variable:\n\n    - The precompiled header should not change what actually gets\n      compiled.  Therefore, use the precompiled header only when\n      compiling source files that start with including\n      \"git-compat-util.h\" (directly or indirectly, e.g. via\n      \"builtin.h\"), or its inclusion is only preceeded by #define\n      directives that don't influence \"git-compat-util.h\" between its\n      include guards [2] (currently DISABLE_SIGN_COMPARE_WARNINGS,\n      USE_THE_REPOSITORY_VARIABLE or GIT_TEST_PROGRESS_ONLY). [3]\n\n      Several (but not all) object or source files listed in the\n      COMPAT_OBJS, REFTABLE_OBJS and THIRD_PARTY_SOURCES variables\n      don't include \"git-compat-util.h\", therefore, for the sake of\n      simplicity, none of the files listed in these variables are\n      built with the precompiled header. [4]\n\n      Since 671df48df8 the Meson build uses the precompiled header\n      even when compiling those reftable source files that don't\n      include \"git-compat-util.h\" at all, although this change is not\n      mentioned in the commit message.\n\n    - But other than that, use the precompiled header when compiling\n      all other source files, including e.g. source files for\n      standalone executables (e.g. \"daemon.c\" for \"git-daemon\"), or\n      those in the directories \"t/helper/\" and \"t/unit-tests/\" as\n      well.\n\n      The Meson build, however, only uses the precompiled header for\n      compiling objects in \"libgit_sources\" and \"builtin_sources\",\n      despite 671df48df8 giving the impression that the precompiled\n      header is included in all compilation units.\n\n    - In short, PRECOMPILED_HEADER_USERS contains all object files\n      listed in OBJECTS, except those that are listed in COMPAT_OBJS,\n      REFTABLE_OBJS or THIRD_PARTY_SOURCES as well.\n\n  - Add a new rule to build object files listed in\n    PRECOMPILED_HEADER_USERS with the precompiled header.\n\n    This rule and its dependencies are basically the same as our\n    existing rule and its dependencies compiling object files from our\n    source files, except:\n\n    - This rule depends on the precompiled header as well, to make\n      sure that it's built before it is used.\n\n    - Use the \"-include tools/precompiled.h\" option to make the\n      precompiled header the first header in the compilation unit,\n      because only then is it used to speed things up.\n\n    - Use the \"-Winvalid-pch\" option to catch any issues when the\n      precompiled header is present but can't be used for whatever\n      reason.  While being unable to use the precompiled header would\n      normally only result in a slower build, it is probably better\n      that developers are made aware that simething is not quite\n      right.  Meson uses this option as well when compiling a source\n      file using the precompiled header.\n\n      Note, that in case of such an issue with \"-Winvalid-pch\" we\n      would get a warning for each object file that is build using the\n      precompiled header, but together with \"-Werror\" (e.g. with\n      DEVELOPER=1) it would fail the build.\n\n    Object files not listed in PRECOMPILED_HEADER_USERS are built with\n    the existing (and unchanged) rule for object files.\n\n  - This way the precompiled header is compiled only once during\n    the whole build process.\n\n    The Meson build, however, currently compiles the precompiled\n    header twice: once for \"libgit_sources\" and once for\n    \"builtin_sources\", despite 671df48df8 giving the impression that\n    it's compiled only once.  And, by the looks of it, it would have\n    to be compiled once more for each new library or executable where\n    we declare the use of the precompiled header.\n\n  - Add the Makefile knob NO_PRECOMPILED_HEADER to make it possible to\n    build without using the precompiled header for testing purposes,\n    or for any compiler that might be out there that doesn't support\n    the \"-Winvalid-pch\" option.\n\n    Note, that when this knob is set then we merely leave\n    PRECOMPILED_HEADER_USERS empty: this way nothing is built with the\n    precompiled header, but \"make clean NO_PRECOMPILED_HEADER=1\" would\n    still remove a leftover precompiled header file.\n\nWith this patch series on top of v2.55.0 I got the following build\ntime improvement:\n\n  Benchmark 1: make -j12 (rev = v2.55.0)\n    Time (mean ± σ):     29.414 s ±  0.031 s    [User: 254.786 s, System: 47.447 s]\n    Range (min … max):   29.370 s … 29.470 s    10 runs\n\n  Benchmark 2: make -j12 (rev = precompile)\n    Time (mean ± σ):     21.725 s ±  0.047 s    [User: 186.047 s, System: 35.109 s]\n    Range (min … max):   21.643 s … 21.768 s    10 runs\n\n  Summary\n    'make -j12 (rev = precompile)' ran\n      1.35 ± 0.00 times faster than 'make -j12 (rev = v2.55.0)'\n\n[1] \"make\" applies target-specific variables to the first target's\n    dependencies as well.  So, if the rule creating the precompiled\n    header included $(EXTRA_CPPFLAGS) as well, then we could get this:\n\n      $ make -s clean\n      $ make V=1 git\n      gcc -o tools/precompiled.h.gch -c -MF [...] -DSHELL_PATH='\"/bin/sh\"' '-DGIT_HTML_PATH=\"share/doc/git-doc\"' '-DGIT_MAN_PATH=\"share/man\"' '-DGIT_INFO_PATH=\"share/info\"' tools/precompiled.h\n      gcc -o git.o -c -include tools/precompiled.h -Winvalid-pch -MF [...] -DSHELL_PATH='\"/bin/sh\"' '-DGIT_HTML_PATH=\"share/doc/git-doc\"' '-DGIT_MAN_PATH=\"share/man\"' '-DGIT_INFO_PATH=\"share/info\"' git.c\n      gcc -o builtin/add.o -c -include tools/precompiled.h -Winvalid-pch -MF [...] -DSHELL_PATH='\"/bin/sh\"'  builtin/add.c\n      cc1: error: ./tools/precompiled.h.gch: not used because `GIT_MAN_PATH' not defined [-Werror=invalid-pch]\n      cc1: all warnings being treated as errors\n      make: *** [Makefile:2940: builtin/add.o] Error 1\n\n    So in this case \"git.o\" is the first object file to be build, and\n    since it has a target-specific EXTRA_CPPFLAGS and since it depends\n    on the precompiled header, the precompiled header is built with\n    the same EXTRA_CPPFLAGS as well.  But then \"builtin/add.o\" is\n    built with empty EXTRA_CPPFLAGS, and GCC's sanity checks for the\n    -Winvalid-pch option complain.  Clang apparently doesn't consider\n    this to be an issue, and builds \"git\" just fine.\n\n    Note, however, that if the first object file to be built is not\n    \"git.o\" (or one of the other object files with target-specific\n    EXTRA_CPPFLAGS), which is usually the case with e.g. \"make all\",\n    then the precompiled header is built with empty EXTRA_CPPFLAGS,\n    and then all is well.  Weird.  We already had issues with \"make\"\n    and target-specific variables in the past, see a673cfede6\n    (Makefile: Fix occasional GIT-CFLAGS breakage, 2010-03-19).\n    The Meson build doesn't use target-specific variables, and the\n    whole project needs to be rebuilt when e.g. \"mandir\" is changed,\n    although this is not mentioned in 904339edbd (Introduce support\n    for the Meson build system, 2024-12-06).\n\n[2] See baa61e46da (git-compat-util.h: move warning infra to prepare\n    for PCHs, 2026-03-19).\n\n[3] This can be verified by simply scanning through the output of:\n\n    {\n            printf '%s\\n' 'print-%:'\n            printf '\\t%s\\n' '@printf \"%s\\n\" $($*)'\n    } >/tmp/printvars.mak\n    for f in $(make -f /tmp/printvars.mak -f Makefile print-PRECOMPILED_HEADER_USERS)\n    do\n            f=\"${f%o}c\"\n            sed -n -E -e \"s%# *(define|include).*%&    $f%p\" -e '/# *include/q' \"$f\"\n    done | sort\n\n[4] \"make\"'s flexibility would allow us to separately list those\n    object files that don't include \"git-compat-util.h\", but then on\n    my (I assume fairly typical) Linux box the number of object files\n    built using the precompiled header increases only by 9 (from 536\n    to 545), which reduces the build time by about 1% (0.2s).\n    Therefore, I don't think it's worth the churn.\n\nSigned-off-by: SZEDER Gábor <szeder.dev@gmail.com>\n---\n .gitignore |  1 +\n Makefile   | 26 +++++++++++++++++++++-----\n 2 files changed, 22 insertions(+), 5 deletions(-)\n\ndiff --git a/.gitignore b/.gitignore\nindex 4da58c6754..0209bd16f2 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -233,6 +233,7 @@\n /.vscode/\n /tags\n /TAGS\n+/tools/precompiled.h.gch\n /cscope*\n /compile_commands.json\n /.cache/\ndiff --git a/Makefile b/Makefile\nindex 2c4d6f4d4a..02162719db 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -321,6 +321,9 @@ include shared.mak\n # dependency rules.  The default is \"auto\", which means to use computed header\n # dependencies if your compiler is detected to support it.\n #\n+# Define NO_PRECOMPILED_HEADER if you want to build Git without precompiling\n+# \"git-compat-util.h\".\n+#\n # Define NATIVE_CRLF if your platform uses CRLF for line endings.\n #\n # Define GIT_USER_AGENT if you want to change how git identifies itself during\n@@ -2905,8 +2908,15 @@ endif\n .PHONY: objects\n objects: $(OBJECTS)\n \n-dep_files := $(foreach f,$(OBJECTS),$(dir $f).depend/$(notdir $f).d)\n-dep_dirs := $(addsuffix .depend,$(sort $(dir $(OBJECTS))))\n+PRECOMPILED_HEADER := tools/precompiled.h\n+PRECOMPILED_HEADER_GCH := $(addsuffix .gch,$(PRECOMPILED_HEADER))\n+\n+ifndef NO_PRECOMPILED_HEADER\n+PRECOMPILED_HEADER_USERS := $(filter-out $(COMPAT_OBJS) $(REFTABLE_OBJS) $(patsubst %.c,%.o,$(THIRD_PARTY_SOURCES)),$(OBJECTS))\n+endif\n+\n+dep_files := $(foreach f,$(OBJECTS) $(PRECOMPILED_HEADER_GCH),$(dir $f).depend/$(notdir $f).d)\n+dep_dirs := $(addsuffix .depend,$(sort $(dir $(OBJECTS) $(PRECOMPILED_HEADER_GCH))))\n \n ifeq ($(uname_S),Darwin)\n \tdep_dirs += $(addsuffix .depend,$(sort $(dir contrib/credential/osxkeychain/git-credential-osxkeychain.o)))\n@@ -2940,7 +2950,13 @@ missing_compdb_dir =\n compdb_args =\n endif\n \n-$(OBJECTS): %.o: %.c GIT-CFLAGS $(missing_dep_dirs) $(missing_compdb_dir)\n+$(PRECOMPILED_HEADER_GCH): %.gch: % GIT-CFLAGS $(missing_dep_dirs) $(missing_compdb_dir)\n+\t$(QUIET_CC)$(CC) -o $@ -c $(dep_args) $(compdb_args) $(ALL_CFLAGS) $<\n+\n+$(PRECOMPILED_HEADER_USERS): %.o: %.c $(PRECOMPILED_HEADER_GCH) GIT-CFLAGS $(missing_dep_dirs) $(missing_compdb_dir)\n+\t$(QUIET_CC)$(CC) -o $*.o -c -include $(PRECOMPILED_HEADER) -Winvalid-pch $(dep_args) $(compdb_args) $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) $<\n+\n+$(filter-out $(PRECOMPILED_HEADER_USERS),$(OBJECTS)): %.o: %.c GIT-CFLAGS $(missing_dep_dirs) $(missing_compdb_dir)\n \t$(QUIET_CC)$(CC) -o $*.o -c $(dep_args) $(compdb_args) $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) $<\n \n %.s: %.c GIT-CFLAGS FORCE\n@@ -2954,7 +2970,7 @@ ifneq ($(dep_files_present),)\n include $(dep_files_present)\n endif\n else\n-$(OBJECTS): $(LIB_H) $(GENERATED_H)\n+$(OBJECTS) $(PRECOMPILED_HEADER_GCH): $(LIB_H) $(GENERATED_H)\n endif\n \n ifeq ($(GENERATE_COMPILATION_DATABASE),yes)\n@@ -3908,7 +3924,7 @@ clean: profile-clean coverage-clean cocciclean\n \t$(RM) GIT-TEST-SUITES\n \t$(RM) po/git.pot po/git-core.pot\n \t$(RM) git.rc git.res\n-\t$(RM) $(OBJECTS)\n+\t$(RM) $(OBJECTS) $(PRECOMPILED_HEADER_GCH)\n \t$(RM) headless-git.o\n \t$(RM) $(LIB_FILE)\n \t$(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) $(OTHER_PROGRAMS)\n-- \n2.56.0.rc0.467.ge054dd0dd6\n\n"},{"id":"553253","messageId":"20260924235216.GA837070@coredump.intra.peff.net","threadId":"66301","inReplyTo":"20260915060952.569535-5-szeder.dev@gmail.com","subject":"Re: [PATCH v2 4/4] Makefile: precompile \"git-compat-util.h\"","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2026-09-24T23:52:16Z","receivedAt":"2026-09-24T23:52:18Z","isPatch":true,"body":"On Tue, Sep 15, 2026 at 08:09:52AM +0200, SZEDER Gábor wrote:\n\n> This patch follows the idea of 671df48df8 (meson: precompile\n> \"git-compat-util.h\", 2026-03-19) to make it faster to build Git using\n> \"make\".  The notable differences are the boilerplate needed to wire up\n> the precompiled header with \"make\", and the selection of object files\n> that are built using the precompiled header:\n\nI got an interesting error message from this today:\n\n  $ make imap-send.o\n      * new build flags\n      CC tools/precompiled.h.gch\n      CC imap-send.o\n  cc1: warning: ./tools/precompiled.h.gch: not used because ‘NO_OPENSSL’ is defined [-Winvalid-pch]\n\nYou won't see it with:\n\n  make NO_OPENSSL=1 imap-send.o\n\nThe culprit is that I have this in my config.mak:\n\n  imap-send.o: EXTRA_CPPFLAGS += -DNO_OPENSSL\n\nso the build options for the precompiled header and imap-send.c are not\nthe same. So now of course you are asking why I would have such a weird\nline in my config.mak.\n\nThe answer is that I want to disable openssl for old builds, because I\nam often building historical versions which use openssl constructs that\nare deprecated or removed. So naturally you are now asking why it does\nnot just say:\n\n  NO_OPENSSL = BrokenOnOldVersions\n\nor similar. But that breaks _some_ old versions which really do need\nopenssl for various things.\n\nThe good-ish news is that it's mostly cosmetic for me. I also loosen\n-Werror for old builds, for obvious reasons. So it's not breaking any\nbuild.\n\nI don't know if my use case is too crazy to care about, but I thought\nI'd mention it in case there are other less-crazy related cases we might\nrun into.\n\nAnd yes, obviously old versions will not have the precompiled header,\neither, but my logic for \"loosen compilation\" is mostly \"we are not on a\nbranch nor rebasing\", so a sight-seeing trip to \"git checkout\norigin/seen\" puts me in the same mode. And eventually it _will_ be old,\ntoo. ;)\n\n-Peff\n"},{"id":"553269","messageId":"arY+JMPe0KWucyja@szeder.dev","threadId":"66301","inReplyTo":"20260924235216.GA837070@coredump.intra.peff.net","subject":"Re: [PATCH v2 4/4] Makefile: precompile \"git-compat-util.h\"","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2026-09-25T09:25:56Z","receivedAt":"2026-09-25T09:26:01Z","isPatch":true,"body":"On Thu, Sep 24, 2026 at 07:52:16PM -0400, Jeff King wrote:\n> On Tue, Sep 15, 2026 at 08:09:52AM +0200, SZEDER Gábor wrote:\n> \n> > This patch follows the idea of 671df48df8 (meson: precompile\n> > \"git-compat-util.h\", 2026-03-19) to make it faster to build Git using\n> > \"make\".  The notable differences are the boilerplate needed to wire up\n> > the precompiled header with \"make\", and the selection of object files\n> > that are built using the precompiled header:\n> \n> I got an interesting error message from this today:\n> \n>   $ make imap-send.o\n>       * new build flags\n>       CC tools/precompiled.h.gch\n>       CC imap-send.o\n>   cc1: warning: ./tools/precompiled.h.gch: not used because ‘NO_OPENSSL’ is defined [-Winvalid-pch]\n> \n> You won't see it with:\n> \n>   make NO_OPENSSL=1 imap-send.o\n> \n> The culprit is that I have this in my config.mak:\n> \n>   imap-send.o: EXTRA_CPPFLAGS += -DNO_OPENSSL\n> \n> so the build options for the precompiled header and imap-send.c are not\n> the same.\n\nHrm.  I've run into this with 'make git.o' and the other object files\nfor which we set EXTRA_CPPFLAGS in our Makefile, and wrote about it at\nlength in the commit message.  I thought omitting EXTRA_CPPFLAGS from\nthe command building the precompiled header solved this issue, and was\npuzzled at first why your use case still causes problems...  The\nreason for the difference is that none of the EXTRA_CPPFLAGS we set in\nour Makefile affect 'git-compat-util.h', but -DNO_OPENSSL does.\n\nIf you set such a custom EXTRA_CPPFLAGS, then you might as well append\n'-Wno-invalid-pch' to it to silence that warning.  The rule building\nobject files using the precompiled header has '-Winvalid-pch' near the\nbeginning while EXTRA_CPPFLAGS are near the end, so we can override it\nfrom EXTRA_CPPFLAGS.  I didn't find a way to override '-include\nprecompiled.h'.\n\n(Btw, can you do something like this with Meson? :) Without resorting\nto creating yet another static library, of course.)\n\n> So now of course you are asking why I would have such a weird\n> line in my config.mak.\n> \n> The answer is that I want to disable openssl for old builds, because I\n> am often building historical versions which use openssl constructs that\n> are deprecated or removed.\n\nWell, for the same reason I have the following in my config.mak:\n\n  # Build knobs to build older versions:\n  # 1ed2c7b115 (imap-send: use HMAC() function provided by OpenSSL, 2016-04-09)\n  ifeq ($(shell git merge-base --is-ancestor 1ed2c7b11570f5d16bdc70d151fa78c3dccf6d38 HEAD 2>/dev/null; echo $$?),1)\n    $(warning Setting NO_OPENSSL for old revisions)\n    NO_OPENSSL = UnfortunatelyYes\n  endif\n\n> So naturally you are now asking why it does\n> not just say:\n> \n>   NO_OPENSSL = BrokenOnOldVersions\n> \n> or similar. But that breaks _some_ old versions which really do need\n> openssl for various things.\n\nI haven't run into any such breakages with disabling OPENSSL for the\nwhole build... but maybe I just haven't built old enough versions?!\nAnyway, will adapt it to your EXTRA_CPPFLAGS trick, thanks.\n\n> The good-ish news is that it's mostly cosmetic for me. I also loosen\n> -Werror for old builds, for obvious reasons. So it's not breaking any\n> build.\n> \n> I don't know if my use case is too crazy to care about\n\nI would say so, yes ;)\n\n> but I thought\n> I'd mention it in case there are other less-crazy related cases we might\n> run into.\n\nNot sure what those less crazy use cases might be, but I'm inclined to\nsay that \"If you deliberately set a custom EXTRA_CPPFLAGS that affects\n'git-compat-util.h', then you should also add '-Wno-invalid-pch' as\nwell\".\n\n> And yes, obviously old versions will not have the precompiled header,\n> either, but my logic for \"loosen compilation\" is mostly \"we are not on a\n> branch nor rebasing\", so a sight-seeing trip to \"git checkout\n> origin/seen\" puts me in the same mode. And eventually it _will_ be old,\n> too. ;)\n\nI'm not sure about loosening compilation for 'seen', especially when\nit comes to DEVELOPER=1, because it's best to catch any issues with\nDEVELOPER=1 while the commit is still only in 'seen'.  My config.mak\ndoesn't set DEVELOPER=1 when bisecting or when building a revision\nreachable from a tagged release.\n\n\n"},{"id":"554074","messageId":"asFQxeh+IUGrlu6W@szeder.dev","threadId":"66301","inReplyTo":"20260915060952.569535-5-szeder.dev@gmail.com","subject":"Re: [PATCH v2 4/4] Makefile: precompile \"git-compat-util.h\"","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2026-10-03T19:00:21Z","receivedAt":"2026-10-03T19:00:24Z","isPatch":true,"body":"On Tue, Sep 15, 2026 at 08:09:52AM +0200, SZEDER Gábor wrote:\n>     - The precompiled header should not change what actually gets\n>       compiled.  Therefore, use the precompiled header only when\n>       compiling source files that start with including\n>       \"git-compat-util.h\" (directly or indirectly, e.g. via\n>       \"builtin.h\"), or its inclusion is only preceeded by #define\n>       directives that don't influence \"git-compat-util.h\" between its\n>       include guards [2] (currently DISABLE_SIGN_COMPARE_WARNINGS,\n>       USE_THE_REPOSITORY_VARIABLE or GIT_TEST_PROGRESS_ONLY). [3]\n\nWell, it turns out the precompiled header does change what gets\ncompiled, and it causes a visible behavior difference, though I think\nit's really minor.\n\nThe crux of the issue is that without the precompiled header the\ncompiler processes \"git-compat-util.h\", but with it, for some reason,\nit processes \"./git-compat-util.h\".\n\nThis is visible when expanding __FILE__ in \"git-compat-util.h\", e.g.\nin the assert macro in regexec_buf().  When the assertion is\ntriggered, e.g. with this diff:\n\ndiff --git a/common-main.c b/common-main.c\nindex 6b7ab077b0..dd2a90c849 100644\n--- a/common-main.c\n+++ b/common-main.c\n@@ -5,6 +5,9 @@ int main(int argc, const char **argv)\n {\n \tint result;\n \n+\t/* Intentionally bogus regexec_buf() call to trigger its assert() */\n+\tregexec_buf(NULL, NULL, 0, 0, NULL, 0);\n+\n \tinit_git(argv);\n \tresult = cmd_main(argc, argv);\n \nThen without the precompiled header we get:\n\n  $ ./git\n  git: git-compat-util.h:1002: regexec_buf: Assertion `nmatch > 0 && pmatch' failed.\n  Aborted (core dumped)\n\nBut with the precompiled header:\n\n  $ ./git\n  git: ./git-compat-util.h:1002: regexec_buf: Assertion `nmatch > 0 && pmatch' failed.\n  Aborted (core dumped)\n\nSimilar could happen when the ALLOC_GROW_BY() macro is invoked with\nbogus parameters to trigger a BUG().\n\n(Sidenote: While this assert does prevent us from invoking regexec()\nwith nonsense, the source file name and line number in the resulting\nerror message are not as useful as they could be, it would be better\nto show the caller's filename and line number.)\n\nSince in \"git-compat-util.h\" __FILE__ is only expanded in error\nmessages that should basically never happen (BUG() and assert()), I\nthink this is acceptable.\n\n\nBTW, this is also visible in compiler error messages:\n\ndiff --git a/git-compat-util.h b/git-compat-util.h\nindex a0f901ce79..00c1f26911 100644\n--- a/git-compat-util.h\n+++ b/git-compat-util.h\n@@ -1,6 +1,8 @@\n #ifndef GIT_COMPAT_UTIL_H\n #define GIT_COMPAT_UTIL_H\n \n+trigger_compiler_error\n+\n #if __STDC_VERSION__ - 0 < 199901L\n /*\n  * Git is in a testing period for mandatory C99 support in the compiler.  If\n\nWithout precompiled header:\n\n      CC daemon.o\n  In file included from daemon.c:3:\n  git-compat-util.h:4:23: error: expected ‘;’ before ‘typedef’\n      4 | trigger_compiler_error\n        |                       ^\n        |                       ;\n\nWith precompiled header:\n\n      CC tools/precompiled.h.gch\n  In file included from tools/precompiled.h:1:\n  ./git-compat-util.h:4:23: error: expected ‘;’ before ‘typedef’\n      4 | trigger_compiler_error\n        |                       ^\n        |                       ;\n\n"}]}